亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1529 | 回復(fù): 0
打印 上一主題 下一主題

內(nèi)核中的TCP的追蹤分析-1-追蹤TCP(IPV4)的socket的初始化 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2008-12-05 11:00 |只看該作者 |倒序?yàn)g覽

我是無名小卒來自山東泰山,轉(zhuǎn)載的朋友請注明出處,無名小卒的博客
http://qinjiana0786.cublog.cn
我們首先來解釋一下什么是socket,很多初次朋友可能對此概念還不是很清楚,socket這個單詞英文是插座的意思,但是在計(jì)算機(jī)領(lǐng)域里有時稱為插口,也有時稱 為套接字,無論什么叫法它的作用都不會變的,都是為應(yīng)用程序提供了今天網(wǎng)絡(luò)通訊的橋梁架構(gòu),我們舉一個電話通訊的例子來看。準(zhǔn)備通話的二部電話相當(dāng)于準(zhǔn)備好了2個進(jìn)程,電話區(qū)號是它的網(wǎng)絡(luò)地址;本地電話區(qū)域的交換機(jī)相當(dāng)于一臺主機(jī),這臺主機(jī)分配給每個電話一個號碼相當(dāng)于socket號。當(dāng)我們在通話之前,首先要找到帶號碼的電話機(jī),相當(dāng)于申請了一個socket;同時還要知道對方的號碼,相當(dāng)于對方有一個固定的socket。然后向?qū)Ψ綋芴柡艚,相?dāng)于發(fā)出連接請求(假如對方不在同一地區(qū)內(nèi),還要加拔對方區(qū)號,相當(dāng)于給出網(wǎng)絡(luò)地址)。對方如果此時空閑或者可以響鈴的話(相當(dāng)于通信區(qū)域的另一主機(jī)開機(jī)且可以接受連接請求),拿起電話話筒,雙方就可以正式通話,相當(dāng)于連接成功。我們在通話的過程,是一方向電話機(jī)發(fā)出信號而對方從電話機(jī)接收信號的過程,相當(dāng)于向socket發(fā)送數(shù)據(jù)和從socket接收數(shù)據(jù)。通話結(jié)束后,一方掛起電話機(jī)相當(dāng)于關(guān)閉socket(撤消連接)。
   在這個電話例子中,我們只能感受到電話機(jī)和電話號碼的存在,建立通話的過程,話音傳輸?shù)倪^程等整個系統(tǒng)的技術(shù)細(xì)節(jié)都是透明的,這與 我們要講的socket機(jī)制非常相似。socket可以利用網(wǎng)絡(luò)間的通信設(shè)施實(shí)現(xiàn)二臺計(jì)算機(jī)之間進(jìn)程的通信,但它對通信設(shè)施的細(xì)節(jié)毫不關(guān)心,只要網(wǎng)絡(luò)通信的條件能夠滿足就可以實(shí)現(xiàn)客戶端與服務(wù)器端的計(jì)算機(jī)之間的通訊。 為了形象的說明socket的工作過程,我們先把我們要進(jìn)行的分析過程中關(guān)于服務(wù)器端和客戶端的執(zhí)行路線以圖示的方法

上圖中左邊是服務(wù)器要執(zhí)行的過程,右邊為客戶端的執(zhí)行過程,很顯然分析的方法最好要分別圍繞服務(wù)器端和客戶端展開進(jìn)行,這樣即能夠使我們更加清楚linux內(nèi)核網(wǎng)絡(luò)的工作路線,有這樣一條主線引導(dǎo)著我們?nèi)ヂ糜,何樂而不為呢?我們會?jīng);氐竭@個圖中來對比、來參照我們的導(dǎo)游地圖產(chǎn)。好了,地圖上指示我們應(yīng)該先從socket的創(chuàng)建開始,那么回憶一下我們的應(yīng)用經(jīng)常要用的編程經(jīng)過,我們以簡寫的方式列在下面,注意服務(wù)器端的工作過程與客戶端的過程不同所以也會此將分別編寫二個socket程序,我們先看服務(wù)器端的練習(xí)程序簡代碼過程
int main()/* test for network,wumingxiaozu */
{
    int server_fd, client_fd;
    int server_len, client_len;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;
下面創(chuàng)建一個網(wǎng)絡(luò)使用的socket
/* wumingxiaozu */
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
為這個創(chuàng)建的socket指定IP地址和端口,ip地址是192.168.1.1而端口則為9266
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = inet_addr("192.168.1.1");
    server_address.sin_port = 9266;
    server_len = sizeof(server_address);
將“電話號碼”賦值給“電話”,即將設(shè)定的地址結(jié)構(gòu)與socket掛起鉤來,朋友們以后理解指針賦值可以理解為鉤子,就象網(wǎng)上有種叫法“鉤子函數(shù)”實(shí)際就是指針函數(shù)
    bind(server_fd, (struct sockaddr *)&server_address, server_len);
創(chuàng)建一個socket的連接隊(duì)列監(jiān)聽只允許接收10個連接,等待客戶端處的socket來連接
    listen(server_fd, 10);
    while(1) {/* wumingxiaozu */
        char temp;
        printf("server waiting\n");
到達(dá)此處時,已經(jīng)說明客戶端的連接請求來到了,下面是接受它的連接請求并將客戶端的“電話號碼”記錄在client_address中。并且通過accept克隆了一個二者保持通訊的socket,為什么要克隆,我們追蹤到內(nèi)核再說,這里函數(shù)返回了服務(wù)器端與客戶端建立連接的socket的ID號
        client_len = sizeof(client_address);
        client_fd = accept(server_fd,
            (struct sockaddr *)&client_address, &client_len);
使用read和write函數(shù)向已經(jīng)建立的socket接收客戶端的一個字符然后再發(fā)送回去
        read(client_fd, &temp, 1);
        temp++;
        write(client_fd, &temp, 1);
        close(client_fd);
    }
}
同樣在我們下面要分析的過程中要反復(fù)回到這個練習(xí)中參考工作的過程,對照上面的練習(xí)程序(以后我們也可能在文章中以應(yīng)用程序、用戶程序等名詞的形式出現(xiàn),所以讀者朋友請注意都是指這里的練習(xí)程序),我在代碼中加入了我們的注釋,所以讓大家閱讀的很容易理解,結(jié)合代碼部分上面的服務(wù)器過程也就是我們的導(dǎo)游地圖左邊所展示的那樣,只不過在練習(xí)程序中我們看到發(fā)送和接收數(shù)據(jù)是通過read和write的C庫函數(shù)來完成的,這其實(shí)也會最終調(diào)用recv和send來實(shí)現(xiàn)socket的數(shù)據(jù)發(fā)送,只是為了方便我們的旅行,所以沒有將C庫的調(diào)用過程寫出來,因?yàn)槲覀兊哪康氖欠治鰏ocket的tcp/ip過程,所以關(guān)于如何從C庫執(zhí)行到系統(tǒng)調(diào)用又好何到達(dá)我們下面的socket系統(tǒng)調(diào)用的總?cè)肟诤瘮?shù)sys_socketcall(),我們都不會再詳細(xì)的列出了,很多書籍和資料講的都非常的清晰,那不是我們本文的重點(diǎn),在本文中我們假設(shè)朋友們已經(jīng)具備了那些知識,如果你沒有了解過這些過程請手邊放一本linux內(nèi)核方面的基礎(chǔ)書籍,可以參閱了解系統(tǒng)調(diào)用的實(shí)現(xiàn)原理后,再接下來看我們下面的分析過程,好了,這節(jié)開始我們就進(jìn)入探討IPV4的TCP的socket的創(chuàng)建,我們直接從socket的系統(tǒng)調(diào)用的總?cè)肟诤瘮?shù)sys_socketcall()開始看重點(diǎn)與我們本節(jié)內(nèi)容相關(guān)的部分,以下從sys_socketcall()函數(shù)中節(jié)摘了與創(chuàng)建相關(guān)的代碼部分
asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
。。。。。。
   if (copy_from_user(a, args, nargs[call]))
        return -EFAULT;
。。。。。。
case SYS_SOCKET:
        err = sys_socket(a0, a1, a[2]);/* wumingxiaozu */
。。。。。。
}
我們知道sys_socketcall(),這個函數(shù)是內(nèi)核socket的總系統(tǒng)調(diào)用入口,參數(shù)call是具體的操作碼,參數(shù)args是一個數(shù)組指針。另外我們需要明確從用戶空間復(fù)制的參數(shù)數(shù)量,這是根據(jù)nargs[]來決定的,以call為下標(biāo)將會從該數(shù)組中找到參數(shù)的個數(shù),依據(jù)個數(shù)來把a(bǔ)rgs處的參數(shù)從用戶空間即我們的應(yīng)用程序復(fù)制過來
/* Argument list sizes for sys_socketcall */
#define AL(x) ((x) * sizeof(unsigned long))
static const unsigned char nargs[18]={
    AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
    AL(3),AL(3),AL(4),AL(4),AL(4),AL(6),
    AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)
};/* wumingxiaozu */
#undef AL
可以看到上面的nargs數(shù)組中可以看出是規(guī)定了參數(shù)的個數(shù),根據(jù)AL的宏解釋,我們還可以看到在include/linux/net.h中規(guī)定了call的詳細(xì)數(shù)字
#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_CONNECT 3 /* sys_connect(2) */
#define SYS_LISTEN 4 /* sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept(2) */
#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
#define SYS_SEND 9 /* sys_send(2) */
#define SYS_RECV 10 /* sys_recv(2) */
#define SYS_SENDTO 11 /* sys_sendto(2) */
#define SYS_RECVFROM 12 /* sys_recvfrom(2) */
#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
#define SYS_SENDMSG 16 /* sys_sendmsg(2) */
#define SYS_RECVMSG 17 /* sys_recvmsg(2) */
這里就可以確定上面數(shù)組中call具體數(shù)字了。有可能朋友們會對注釋中的(2)有疑惑,其的含義喻為這里是系統(tǒng)調(diào)用號。好了我們進(jìn)入sys_socket()函數(shù) ,同樣我們只列出重要的部分,注意我們以后在函數(shù)前面用“-à”符號來表明調(diào)用的路徑過程,來方便大家了解了掌握函數(shù)的執(zhí)行路徑,當(dāng)然我們也會在文章中插入圖片來方便大家學(xué)習(xí)與閱讀
sys_socketcall()-->sys_socket()

asmlinkage long sys_socket(int family, int type, int protocol)
{
。。。。。。
    retval = sock_create(family, type, protocol, &sock);/* wumingxiaozu */
。。。。。。

}
這個函數(shù)從名稱可以看出是為了創(chuàng)建一個插口(有的資料上也稱為套接字,注意下邊我們有時稱為插口有時可能稱為套接字),對照應(yīng)用程序也就是我們最前的練習(xí)程序中的界面(我們稱界面的意思就是練習(xí)程序中的代碼語句) server_sockfd = socket(AF_INET, SOCK_STREAM, 0);傳遞過來的三個參數(shù),第一個參數(shù)family應(yīng)用程序傳遞過來的值為AF_INET,第二個參數(shù)是SOCK_STREAM,第三個參數(shù)是0。我們從上面的函數(shù)中可以看到是進(jìn)入了sock_create()函數(shù)去創(chuàng)建socket。另外在2.6.26內(nèi)核的net/socket.c處的300行我們可以看到
static struct vfsmount *sock_mnt __read_mostly;
/* wumingxiaozu */
static struct file_system_type sock_fs_type = {
    .name = "sockfs",
    .get_sb = sockfs_get_sb,
    .kill_sb = kill_anon_super,
};
這里如果看過、了解文件系統(tǒng)的朋友,會知道file_system_type的作用是代表我們的網(wǎng)絡(luò)文件系統(tǒng),上面代碼就標(biāo)識著我們聲明了一個名為sockfs的網(wǎng)絡(luò)文件系統(tǒng),但是這里安裝過程與某些文件系統(tǒng)不同,實(shí)在不了解的朋友可以翻閱一下操作系統(tǒng)理論方面的書,在此類書中講述的數(shù)據(jù)結(jié)構(gòu)file_system_type非常清楚,關(guān)于文件系統(tǒng)雖然不是本文的重點(diǎn)但是我們還是需要了解網(wǎng)絡(luò)文件系統(tǒng)的初始化經(jīng)過。
我是無名小卒,本文是原創(chuàng)如果轉(zhuǎn)載請注明出處,我們在以前沒提到過這里簡要介紹一下,首先是內(nèi)核在初始時會執(zhí)行到init/main.c,而執(zhí)行到內(nèi)核的初始化函數(shù)kernel_init()在其內(nèi)部調(diào)用了do_basic_setup()函數(shù),再調(diào)用do_initcalls()函數(shù),這里會看到有一個
Main()-->kernel_init()-->do_basic_setup()-->do_initcalls()
/* wumingxiaozu */
static void __init do_initcalls(void)
{
。。。。。。
for (call = __initcall_start; call  __initcall_end; call++)
        do_one_initcall(*call);
。。。。。。
}
這就是稱作為initcall機(jī)制,我們查看socket.c中,可以看到下面語句
core_initcall(sock_init);    /* early initcall */
#define core_initcall(fn)        __define_initcall("1",fn,1)
在2.6內(nèi)核中已經(jīng)把很多初始化函數(shù)都放在initcall中實(shí)現(xiàn)了。所以初始化中會執(zhí)行socket.c中的sock_init()函數(shù)
static int __init sock_init(void)
{
。。。。。。
    register_filesystem(&sock_fs_type);/* wumingxiaozu */
    sock_mnt = kern_mount(&sock_fs_type);
。。。。。。
}
就是在sock_init()函數(shù)中成功的將我們的網(wǎng)絡(luò)文件系統(tǒng)登記、安裝到了linux的內(nèi)核中,register_filesystem函數(shù)將我們的sockfs套接字文件系統(tǒng)注冊到linux內(nèi)核中去,kern_mount函數(shù)完成了在linux內(nèi)核中的套接字文件系統(tǒng)的安裝,在內(nèi)核中建立了網(wǎng)絡(luò)文件系統(tǒng)安裝點(diǎn)。這二個函數(shù)均與具體的使用的文件系統(tǒng)相關(guān),所以我們不再跟進(jìn)分析了,朋友們參閱操作系統(tǒng)理論的文件系統(tǒng)內(nèi)容可以了解一下。在安裝的過程中,我們的套接字文件系統(tǒng)會調(diào)用sock_fs_type數(shù)據(jù)結(jié)構(gòu)中的get_sb鉤子函數(shù),以后我們稱這類初始化的結(jié)構(gòu)變量為鉤子結(jié)構(gòu),因?yàn)樗峁┝宋覀兒瘮?shù)的入口,就象鉤子一樣,將函數(shù)掛入到結(jié)構(gòu)體中,所以這樣的結(jié)構(gòu)體我們稱之為鉤子結(jié)構(gòu)體,而掛入的函數(shù)稱之為鉤子函數(shù)。我們在上面的代碼中已經(jīng)看到了sock_fs_type的設(shè)置了get_sb的鉤子函數(shù)為sockfs_get_sb(),所以理所當(dāng)然進(jìn)入函數(shù)中去完成文件系統(tǒng)的安裝過程
sock_init()-->kern_mount()-->。。。。。。--> sockfs_get_sb()
/* wumingxiaozu */
static int sockfs_get_sb(struct file_system_type *fs_type,
             int flags, const char *dev_name, void *data,
             struct vfsmount *mnt)
{
    return get_sb_pseudo(fs_type, "socket:", &sockfs_ops, SOCKFS_MAGIC,mnt);/* wumingxiaozu */
}
這個函數(shù)一轉(zhuǎn)手交給了get_sb_pseudo()去執(zhí)行,注意傳遞的參數(shù)一個是我們的sock_fs_type,第二個參數(shù)要求創(chuàng)建socket名稱的根目錄項(xiàng),第三個參數(shù)非常重要是關(guān)于我們套接字文件系統(tǒng)的操作函數(shù)表,而最后一個為安裝點(diǎn)。我們看一下重點(diǎn)的文件系統(tǒng)的操作函數(shù)表
static struct super_operations sockfs_ops = {
    .alloc_inode =    sock_alloc_inode,
    .destroy_inode =sock_destroy_inode,
    .statfs =    simple_statfs,
};/* wumingxiaozu */
這個函數(shù)表是對于套接字文件系統(tǒng)的節(jié)點(diǎn)和目錄提供了具體的鉤子函數(shù),以后在涉及到文件系統(tǒng)的操作內(nèi)容時,linux內(nèi)核都會層層跳轉(zhuǎn)到這里的sockfs_ops鉤子結(jié)構(gòu)中再進(jìn)入具體的鉤子函數(shù)。那不是我們重要的分析過程了,為了不至于跑的太遠(yuǎn),讓大家在旅行途中過于勞累,我們將嚴(yán)格根據(jù)導(dǎo)游地圖去前行,所以中途中的“風(fēng)景”我們也是駐足欣賞不會踏入太深,一來是時間有限,加之那樣將會使我們的篇幅巨增,對于朋友們的學(xué)習(xí)旅途將是無益的,所以還是前邊我們提到的,如果想涉足其中請參考相關(guān)的文件系統(tǒng)書籍,get_sb_pseudo()與文件系統(tǒng)密切相關(guān),其代碼就是完成我們套接字sockfs的安裝過程。我們回到sys_socket()函數(shù)繼續(xù)往下看
sys_socketcall()-->sys_socket()-->sock_create()
/* wumingxiaozu */
int sock_create(int family, int type, int protocol, struct socket **res)
{
    return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);/* wumingxiaozu */
}
進(jìn)入__sock_create(),然后在那里執(zhí)行
sys_socketcall()-->sys_socket()-->sock_create()-->__sock_create()
/* wumingxiaozu */
static int __sock_create(struct net *net, int family, int type, int protocol,
            struct socket **res, int kern)
{
。。。。。。
sock = sock_alloc();
。。。。。。
err = pf->create(net, sock, protocol);/* wumingxiaozu */
。。。。。。
}

函數(shù)的前邊不是很重要所以我們刪減了一些內(nèi)容,只留下最重要的,也是我們要分析的重點(diǎn),我們首先是進(jìn)入了函數(shù)sock_alloc()
sys_socketcall()-->sys_socket()-->sock_create()-->__sock_create()-->sock_alloc()
/* wumingxiaozu */
static struct socket *sock_alloc(void)
{
    struct inode *inode;
    struct socket *sock;
    inode = new_inode(sock_mnt->mnt_sb);
    if (!inode)
        return NULL;
    sock = SOCKET_I(inode);
    /* wumingxiaozu */
    inode->i_mode = S_IFSOCK | S_IRWXUGO;
    inode->i_uid = current->fsuid;
    inode->i_gid = current->fsgid;
    get_cpu_var(sockets_in_use)++;
    put_cpu_var(sockets_in_use);
    return sock;
}
這里我們看到socket數(shù)據(jù)結(jié)構(gòu),這個定義位于include/linux/net.h的117行
struct socket {
    socket_state state;
    unsigned long flags;
    const struct proto_ops *ops;
    struct fasync_struct *fasync_list;
    struct file *file;
    struct sock *sk;
    wait_queue_head_t wait;
    short type;
};/* wumingxiaozu */
我們暫不對其內(nèi)容加以具體的解析,以后朋友們會經(jīng)?吹劫N出了數(shù)據(jù)結(jié)構(gòu)的定義而沒有做進(jìn)步的分析,其原因是我認(rèn)為“用中學(xué)習(xí)”的方法是最有效的,經(jīng)常看到有的網(wǎng)站上把結(jié)構(gòu)體的解晰做的很全而細(xì),但是對于朋友學(xué)習(xí)掌握,或者在將來的再學(xué)習(xí)強(qiáng)化記憶過程中的作用是渺小的,就象記英語單詞,如果大家天天對著英中注釋來記憶,而不是先看詞猜想其含義,最后再看中文詞義的話,可想而知記憶效果的差距有多大,正是今天很多學(xué)習(xí)方法的改進(jìn)才使大家的學(xué)習(xí)效果收到了事半功倍的效果。對于上面的結(jié)構(gòu)體我們特別應(yīng)該注意的是sock這個結(jié)構(gòu)變量,其定義非常大,我們也不想占用太多的篇幅來列了,這個結(jié)構(gòu)體是因?yàn)槲覀兪褂貌煌膮f(xié)議而掛入不同的鉤子結(jié)構(gòu)變量,之所以從socket中分離出這么一個重要的結(jié)構(gòu)是因?yàn)閟ocket是通用的套接字結(jié)構(gòu)體,而sock而與具體使用的協(xié)議相密切,所以公共的通用部分放在socket結(jié)構(gòu)中,而私有的專用的放在sock結(jié)構(gòu)體,這就是為什么socket內(nèi)容相對少而sock結(jié)構(gòu)體龐大的原因。結(jié)構(gòu)內(nèi)容我們也暫且放一放,我們用時再說,有朋友說ULK也就是深入理解內(nèi)核書不錯,我個人感覺他里面有大量的文字來描述結(jié)構(gòu)體和函數(shù)作用甚至詳細(xì)的參數(shù)作用,大量的篇幅都是用文字來描述,如果是英文版的看起更加吃力,我就是讀了第三版的英文版,感覺不如讀代碼舒服來的痛快,與其說那本書是學(xué)習(xí)資料不如說更加象一本工具字典,現(xiàn)在我們分析一下sock_allock()這個函數(shù),首先這里涉及到文件系統(tǒng)的inode,我們看到他調(diào)用new_inode(sock_mnt->mnt_sb);可以看到sock_mnt是上一節(jié)我們看到的socket文件系統(tǒng)的根節(jié)點(diǎn),這里是在socket文件系統(tǒng)中分配一個節(jié)點(diǎn),關(guān)于文件系統(tǒng)的內(nèi)容將在以后的專門來分析,這里我們只需要知道他為我們在內(nèi)存中分配了一個socket的inode節(jié)點(diǎn)即可,然后函數(shù)中接著是SOCKET_I(),這是一個內(nèi)聯(lián)函數(shù),我們看一下他的內(nèi)容
sys_socketcall()-->sys_socket()-->sock_create()-->__sock_create()-->sock_alloc()-->SOCKET_I()
/* wumingxiaozu */
static inline struct socket *SOCKET_I(struct inode *inode)
{
    return &container_of(inode, struct socket_alloc, vfs_inode)->socket;/* wumingxiaozu */
}
#define container_of(ptr, type, member) ({ \
    const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    (type *)( (char *)__mptr - offsetof(type,member) );})
這里我們看到他使用了一個宏container_of,這個宏我們需要分析一下,首先我們把他轉(zhuǎn)換一下成下面這樣
#define container_of(inode, struct socket_alloc, vfs_inode) ({ \
    const typeof( ((struct socket_alloc *)0)->vfs_inode ) *__mptr = (inode); \/* wumingxiaozu */
    (struct socket_alloc *)( (char *)__mptr - offsetof(struct socket_alloc,vfs_inode) );})
上面有一個數(shù)據(jù)結(jié)構(gòu)我貼在下面
struct socket_alloc {
    struct socket socket;
    struct inode vfs_inode;
};/* wumingxiaozu */
上面還引用了另一個宏,也貼在下面并代入翻譯
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)翻譯成offsetof(struct socket_alloc,vfs_inode) ((size_t)&((struct socket_alloc)0)->vfs_inode)
上面的整個宏我們分析一下,首先是(struct socket_alloc)0)->vfs_inode,這句有很多朋友不很理解,它就是假設(shè)數(shù)據(jù)結(jié)構(gòu)socket_alloc在0地址處時取得vfs_inode的地址也實(shí)際上就是vfs_inode在數(shù)據(jù)結(jié)構(gòu)的偏離,所以宏名稱使用了offsetof(),這樣我們就理解了offsetof()宏是取得vfs_inode在struct socket_alloc的相對偏離位置,即地址差,然后我們看container_of宏,這個宏首先是假設(shè)0地址處的socket_alloc結(jié)構(gòu)中的vfs_inode指向我們創(chuàng)建的inode,然后用這個inode的地址減掉vfs_inode的偏離差,就得到了數(shù)據(jù)結(jié)構(gòu)socket_alloc的起始地址,而起始地址我們看到在socket_alloc頭部是socket的數(shù)據(jù)結(jié)構(gòu),因此我們也就得到了socket的地址,即指針。這個宏是非常關(guān)鍵的一個宏,肯定今后要經(jīng)常在內(nèi)核中出現(xiàn),希望朋友們認(rèn)真把我的描述研究一遍,做到真正的理解,如果還是不得其解那就請記住他的作用就是找到數(shù)據(jù)結(jié)構(gòu)中的指定數(shù)據(jù)結(jié)構(gòu)的頭地址。sock_alloc()剩下的代碼部分是為inode節(jié)點(diǎn)設(shè)置他的相關(guān)指示標(biāo)記,使他在內(nèi)存中標(biāo)記為socket,以其創(chuàng)建進(jìn)程的信息,還有增加內(nèi)存中socket的使用記數(shù)。然后我們回到__sock_create()函數(shù)中繼續(xù)往下分析,創(chuàng)建了一個socket以后,我們看到
    if (net_families[family] == NULL)
        request_module("net-pf-%d", family);
也就是檢查相應(yīng)的協(xié)議有沒有安裝,我們在實(shí)踐練習(xí)中曾經(jīng)在創(chuàng)建socket用過這句
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
由些傳遞下來的family參數(shù)則是AF_INET,這個值是2,也就是說我們要在net_families[2]處安裝這里的tcp協(xié)議,那么在我們這里要談到的網(wǎng)絡(luò)協(xié)議是何時安裝到數(shù)組中的呢?同樣我們在net/ipv4/af_inet.c中看到
fs_initcall(inet_init);

#define fs_initcall(fn)            __define_initcall("5",fn,5)
所以初始化中會執(zhí)行inet_init()函數(shù),這是我們要說的登記的重點(diǎn),我們在這個inet_init()函數(shù)中看到有一句關(guān)鍵的
static int __init inet_init(void)
{
。。。。。。
(void)sock_register(&inet_family_ops);/* wumingxiaozu */
。。。。。。
}
會在sock_register中完成注冊
inet_init()-->sock_register()
/* wumingxiaozu */
int sock_register(const struct net_proto_family *ops)
{
。。。。。。
    if (net_families[ops->family])
        err = -EEXIST;
    else {
        net_families[ops->family] = ops;
        err = 0;
    }
。。。。。。
}
這里會把
static struct net_proto_family inet_family_ops = {
    .family = PF_INET,
    .create = inet_create,
    .owner    = THIS_MODULE,
};/* wumingxiaozu */
注冊到net_families數(shù)組中,而PF_INET就是上面我們說的AF_INET
#define PF_INET        AF_INET
所以這里我們的練習(xí)中會進(jìn)經(jīng)過pf->create(net, sock, protocol)到inet_create()中,下一篇待續(xù)


本文來自ChinaUnix博客,如果查看原文請點(diǎn):http://blog.chinaunix.net/u2/64681/showart_1680618.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP