1三個相關(guān)數(shù)據(jù)結(jié)構(gòu).
關(guān)于socket的創(chuàng)建,首先需要分析socket這個結(jié)構(gòu)體,這是整個的核心。
104 struct socket {
105 socket_state state;
106
107 kmemcheck_bitfield_begin(type);
108 short type;
109 kmemcheck_bitfield_end(type);
110
111 unsigned long flags;
112
113 struct socket_wq __rcu *wq;
114
115 struct file *file;
116 struct sock *sk;
117 const struct proto_ops *ops;
118 }
其中,state是socket的狀態(tài),比如CONNECTED,type是類型,比如TCP下使用的流式套接字SOCK_STREAM,flags是標志位,負責一些特殊的設(shè)置,比如SOCK_ASYNC_NOSPACE,ops則是采用了和超級塊設(shè)備操作表一樣的邏輯,專門設(shè)置了一個數(shù)據(jù)結(jié)構(gòu)來記錄其允許的操作。Sk是非常重要的,也是非常大的,負責記錄協(xié)議相關(guān)內(nèi)容。這樣的設(shè)置使得socket具有很好的協(xié)議無關(guān)性,可以通用。file是與socket相關(guān)的指針列表,wq是等待隊列。
還有兩個結(jié)構(gòu)體sk_buff和tcp_sock,其中sk_buff與每一個數(shù)據(jù)包相關(guān),負責描述每一個數(shù)據(jù)包的信息,而tcp_sock則是tcp相關(guān)的結(jié)構(gòu)體
2.初始化并分配socket結(jié)構(gòu)
Socket()本質(zhì)上是一個glibc中的函數(shù),執(zhí)行實際上是是調(diào)用sys_socketcall()系統(tǒng)調(diào)用。sys_socketcall()是幾乎所有socket相關(guān)函數(shù)的入口,即是說,bind,connect等等函數(shù)都需要sys_socketcall()作為入口。該系統(tǒng)調(diào)用代碼如下:
2435 SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
2436 {
2437 unsigned long a[6];
2438 unsigned long a0, a1;
2439 int err;
2440 unsigned int len;
2441
2442 if (call < 1 || call > SYS_SENDMMSG)
2443 return -EINVAL;
2444
2445 len = nargs[call];
2446 if (len > sizeof(a))
2447 return -EINVAL;
2448
2449 /* copy_from_user should be SMP safe. */
2450 if (copy_from_user(a, args, len))
2451 return -EFAULT;
2452
2453 audit_socketcall(nargs[call] / sizeof(unsigned long), a);
2454
2455 a0 = a[0];
2456 a1 = a[1];
2457
2458 switch (call) {
2459 case SYS_SOCKET:
2460 err = sys_socket(a0, a1, a[2]);
2461 break;
2462 case SYS_BIND:
2463 err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
2464 break;
.......
2531 default:
2532 err = -EINVAL;
2533 break;
2534 }
2535 return err;
2536 }
省略號省略掉的是各種網(wǎng)絡(luò)編程用得到的函數(shù),全部都在這個系統(tǒng)調(diào)用中,再次證明前文所說,socketcall是系統(tǒng)所有socket相關(guān)函數(shù)打大門。
而對于創(chuàng)建socket,自然會在switch中調(diào)用到sys_socket()系統(tǒng)調(diào)用,而這個函數(shù)僅僅是調(diào)用sock_create()來創(chuàng)建socket和sock_map_fd()來與文件系統(tǒng)進行關(guān)聯(lián)。其中sock_create()調(diào)用__sock_create().接下來我盡力地來分析一下源代碼(直接在代碼中注釋):
1257 int __sock_create(struct net *net, int family, int type, int protocol,
1258 struct socket **res, int kern)
1259 {
1260 int err;
1261 struct socket *sock;
1262 const struct net_proto_family *pf;
/*檢查傳入的協(xié)議族參數(shù)是否正確*/
1267 if (family < 0 || family >= NPROTO)
1268 return -EAFNOSUPPORT;
/*檢查傳入的類型參數(shù)是否正確*/
1269 if (type < 0 || type >= SOCK_MAX)
1270 return -EINVAL;
/*檢查兼容性。在使用中,family是PF還是AF沒什么區(qū)別,但事實上二者PF是POSIX標準,而AF是BSD標準,按照我的理解,現(xiàn)在大部分使用BSD標準,因此,如果傳入?yún)?shù)是使用的PF,那么在內(nèi)核打印消息,并把family設(shè)置成PF*/
1277 if (family == PF_INET && type == SOCK_PACKET) {
1278 static int warned;
1279 if (!warned) {
1280 warned = 1;
1281 printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n",
1282 current->comm);
1283 }
1284 family = PF_PACKET;
1285 }
1286 /*這里調(diào)用security_socket_create函數(shù),該函數(shù)是調(diào)用security_ops->socket_create(family, type, protocol, kern),而security_ops是一個security_operations的結(jié)構(gòu)體,此處再次使用了linux常用的手段,即提供一個結(jié)構(gòu)體數(shù)據(jù)結(jié)構(gòu)來描述所有的操作,該結(jié)構(gòu)體異常復雜,暫時還沒能力理解,只知道該結(jié)構(gòu)體是對應(yīng)著linux系統(tǒng)下的LMS安全框架的。這里其實是個空函數(shù),實際上是進行一系列的安全檢查,然后如果成功則返回0*/
1287 err = security_socket_create(family, type, protocol, kern);
1288 if (err)
1289 return err;
/*此處開始需要再分析多個函數(shù),不再以注釋的方式進行分析,在源代碼的后面一一分析*/
1296 sock = sock_alloc();
1297 if (!sock) {
1298 net_warn_ratelimited("socket: no more sockets\n");
1299 return -ENFILE; /* Not exactly a match, but its the
1300 closest posix thing */
1301 }
1302
1303 sock->type = type;
1304
1305 #ifdef CONFIG_MODULES
1312 if (rcu_access_pointer(net_families[family]) == NULL)
1313 request_module("net-pf-%d", family);
1314 #endif
1315
1316 rcu_read_lock();
1317 pf = rcu_dereference(net_families[family]);
1318 err = -EAFNOSUPPORT;
1319 if (!pf)
1320 goto out_release;
1321
1326 if (!try_module_get(pf->owner))
1327 goto out_release;
1328
1330 rcu_read_unlock();
1331
1332 err = pf->create(net, sock, protocol, kern);
1333 if (err < 0)
1334 goto out_module_put;
1335
1340 if (!try_module_get(sock->ops->owner))
1341 goto out_module_busy;
1342
1347 module_put(pf->owner);
1348 err = security_socket_post_create(sock, family, type, protocol, kern);
1349 if (err)
1350 goto out_sock_release;
1351 *res = sock;
1352
1353 return 0;
1354
1355 out_module_busy:
1356 err = -EAFNOSUPPORT;
1357 out_module_put:
1358 sock->ops = NULL;
1359 module_put(pf->owner);
1360 out_sock_release:
1361 sock_release(sock);
1362 return err;
1363
1364 out_release:
1365 rcu_read_unlock();
1366 goto out_sock_release;
1367 }
如注釋中說的,接下來分析sock = sock_alloc(),首先看sock_alloc()的源代碼:
530 static struct socket *sock_alloc(void)
531 {
532 struct inode *inode;
533 struct socket *sock;
534
535 inode = new_inode_pseudo(sock_mnt->mnt_sb);
536 if (!inode)
537 return NULL;
538
539 sock = SOCKET_I(inode);
540
541 kmemcheck_annotate_bitfield(sock, type);
542 inode->i_ino = get_next_ino();
543 inode->i_mode = S_IFSOCK | S_IRWXUGO;
544 inode->i_uid = current_fsuid();
545 inode->i_gid = current_fsgid();
546 inode->i_op = &sockfs_inode_ops;
547
548 this_cpu_add(sockets_in_use, 1);
549 return sock;
550 }
借助LXR上的注釋我們進行分析。該函數(shù)式分配并初始化一個socket和一個inode,二者是捆綁在一起的,如果成功則返回socket,如果inode創(chuàng)建出問題,則返回NULL。此處有點復雜,耦合性有點強,慢慢來分析。首先分析SOCKET_I(),該函數(shù)的目的是取得socket的結(jié)構(gòu)體指針,具體分析如下:
1322 static inline struct socket *SOCKET_I(struct inode *inode)
1323 {
1324 return &container_of(inode, struct socket_alloc, vfs_inode)->socket;
1325 }
這里使用了宏container_of,我們繼續(xù)分析container_of宏:
718 #define container_of(ptr, type, member) ({ \
719 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
720 (type *)( (char *)__mptr - offsetof(type,member) );})
首先分析傳入?yún)?shù),ptr是指向返回對象的指針,有點繞,但前文說過,inode和socket是綁在一起的,此處其實就是inode指向socket的意思。然后是type,在SOCKET_I中傳入的事socket_alloc結(jié)構(gòu)體,該結(jié)構(gòu)體如下:
1317 struct socket_alloc {
1318 struct socket socket;
1319 struct inode vfs_inode;
1320 };只包含了socket結(jié)構(gòu)體和在虛擬文件系統(tǒng)中的節(jié)點結(jié)構(gòu)體,無甚復雜。宏container_of中還包含了另外一個宏offsetof:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)。
由此分析container_of的內(nèi)容。首先是offsetof,是假設(shè)TYPE(即struct sock_alloc)的地址在0的時候其中MEMBER的地址,即是在計算MEMBER在結(jié)構(gòu)體中的偏移地址,所以這個宏取名為offsetof也不為怪了。回過頭看container_of,這個宏分兩部分:
第一部分typeof( ((type *)0)->member ) *__mptr = (ptr),通過typeof定義一個struct sock_alloc->vfs_inode的指針__mptr,然后賦值為ptr的值,即是說現(xiàn)在vfs_inode的地址就是ptr(即inode)的地址。
第二部分 (type *)( (char *)__mptr - offsetof(type,member) ),用vfs_inode的地址去減去它在結(jié)構(gòu)體中的偏移地址,自然得到結(jié)構(gòu)體的首地址,又因為該結(jié)構(gòu)體的第一個成員是struct socket socket,所以自然返回的是socket的地址,再通過(type *)進行強制類型轉(zhuǎn)換,所以SOCKET_I就得到了新的socket的指針了。
這里的兩個宏有點復雜,但實在是功能強大,值得學習。
這里有一個值得注意的地方,之前分析了這么久的struct sock_alloc,但我們并沒有對這個結(jié)構(gòu)體進行初始化。所以在new_inode_pseudo(sock_mnt->mnt_sb)中必然對這個結(jié)構(gòu)體有所交代。該函數(shù)會調(diào)用alloc_inode(sb)系統(tǒng)調(diào)用,sb是一個超級塊結(jié)構(gòu)。
該系統(tǒng)調(diào)用的源代碼顯示如果傳進去的超級快表操作是alloc_inode的話,則執(zhí)行執(zhí)行之。可以看到,傳進去的參數(shù)是sock_mnt->mnt_sb,再查找該參數(shù)發(fā)現(xiàn)這個參數(shù)是在初始化sock_init中進行kern_mount時賦值的,恰好使得傳進去的參數(shù)是alloc_inode,所以,接下來執(zhí)行超級快操作表中的alloc_inode,然后調(diào)用sock_alloc_inode系統(tǒng)調(diào)用。
該系統(tǒng)調(diào)用先是ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);進行slab分配,爾后的代碼也是進行inode的操作和slab的分配,到此為止,暫時不再繼續(xù)深入,只是注意到經(jīng)過這一系列地分配之后,socket處于未連接狀態(tài),即socket.state = SS_UNCONNECTED。
至此回到__sock_create()繼續(xù)分析。接下來是一次條件編譯。#ifdef CONFIG_MODULES,這個選項是用于linux的模塊編寫的,如果在模塊編寫時用上了CONFIG_MODULES,是會在makemenuconfig中出現(xiàn)該模塊選項的。該處如果有CONFIG_MODULES但是卻沒有找到對應(yīng)的下屬選項則會裝載一個default的模塊。
接下去是:
1312 if (rcu_access_pointer(net_families[family]) == NULL)
1313 request_module("net-pf-%d", family);
這兩句檢查對應(yīng)協(xié)議族的操作表是否已經(jīng)安裝,如果沒有安裝則使用request_module進行安裝。現(xiàn)在都是在TCP/IP協(xié)議下進行分析,所以family是AF_INET,也就是2,所以實際檢查的是全局變量net_families[2]。這個全局變量是在系統(tǒng)初始化時由net/ipv4/af_inet.c文件進行安裝,具體代碼是:fs_initcall(inet_init);而fs_initcall是個宏,具體實現(xiàn)是:
204 #define fs_initcall(fn) __define_initcall(fn, 5)
即是把inet_init裝載在init_call中,所以在系統(tǒng)啟動時自然會初始化。
1三個相關(guān)數(shù)據(jù)結(jié)構(gòu).
關(guān)于socket的創(chuàng)建,首先需要分析socket這個結(jié)構(gòu)體,這是整個的核心。
104 struct socket {
105 socket_state state;
106
107 kmemcheck_bitfield_begin(type);
108 short type;
109 kmemcheck_bitfield_end(type);
110
111 unsigned long flags;
112
113 struct socket_wq __rcu *wq;
114
115 struct file *file;
116 struct sock *sk;
117 const struct proto_ops *ops;
118 }
其中,state是socket的狀態(tài),比如CONNECTED,type是類型,比如TCP下使用的流式套接字SOCK_STREAM,flags是標志位,負責一些特殊的設(shè)置,比如SOCK_ASYNC_NOSPACE,ops則是采用了和超級塊設(shè)備操作表一樣的邏輯,專門設(shè)置了一個數(shù)據(jù)結(jié)構(gòu)來記錄其允許的操作。Sk是非常重要的,也是非常大的,負責記錄協(xié)議相關(guān)內(nèi)容。這樣的設(shè)置使得socket具有很好的協(xié)議無關(guān)性,可以通用。file是與socket相關(guān)的指針列表,wq是等待隊列。
還有兩個結(jié)構(gòu)體sk_buff和tcp_sock,其中sk_buff與每一個數(shù)據(jù)包相關(guān),負責描述每一個數(shù)據(jù)包的信息,而tcp_sock則是tcp相關(guān)的結(jié)構(gòu)體
3.初始化并分配socket結(jié)構(gòu)
Socket()本質(zhì)上是一個glibc中的函數(shù),執(zhí)行實際上是是調(diào)用sys_socketcall()系統(tǒng)調(diào)用。sys_socketcall()是幾乎所有socket相關(guān)函數(shù)的入口,即是說,bind,connect等等函數(shù)都需要sys_socketcall()作為入口。該系統(tǒng)調(diào)用代碼如下:
2435 SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
2436 {
2437 unsigned long a[6];
2438 unsigned long a0, a1;
2439 int err;
2440 unsigned int len;
2441
2442 if (call < 1 || call > SYS_SENDMMSG)
2443 return -EINVAL;
2444
2445 len = nargs[call];
2446 if (len > sizeof(a))
2447 return -EINVAL;
2448
2449 /* copy_from_user should be SMP safe. */
2450 if (copy_from_user(a, args, len))
2451 return -EFAULT;
2452
2453 audit_socketcall(nargs[call] / sizeof(unsigned long), a);
2454
2455 a0 = a[0];
2456 a1 = a[1];
2457
2458 switch (call) {
2459 case SYS_SOCKET:
2460 err = sys_socket(a0, a1, a[2]);
2461 break;
2462 case SYS_BIND:
2463 err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
2464 break;
.......
2531 default:
2532 err = -EINVAL;
2533 break;
2534 }
2535 return err;
2536 }
省略號省略掉的是各種網(wǎng)絡(luò)編程用得到的函數(shù),全部都在這個系統(tǒng)調(diào)用中,再次證明前文所說,socketcall是系統(tǒng)所有socket相關(guān)函數(shù)打大門。
而對于創(chuàng)建socket,自然會在switch中調(diào)用到sys_socket()系統(tǒng)調(diào)用,而這個函數(shù)僅僅是調(diào)用sock_create()來創(chuàng)建socket和sock_map_fd()來與文件系統(tǒng)進行關(guān)聯(lián)。其中sock_create()調(diào)用__sock_create().接下來我盡力地來分析一下源代碼(直接在代碼中注釋):
1257 int __sock_create(struct net *net, int family, int type, int protocol,
1258 struct socket **res, int kern)
1259 {
1260 int err;
1261 struct socket *sock;
1262 const struct net_proto_family *pf;
/*檢查傳入的協(xié)議族參數(shù)是否正確*/
1267 if (family < 0 || family >= NPROTO)
1268 return -EAFNOSUPPORT;
/*檢查傳入的類型參數(shù)是否正確*/
1269 if (type < 0 || type >= SOCK_MAX)
1270 return -EINVAL;
/*檢查兼容性。在使用中,family是PF還是AF沒什么區(qū)別,但事實上二者PF是POSIX標準,而AF是BSD標準,按照我的理解,現(xiàn)在大部分使用BSD標準,因此,如果傳入?yún)?shù)是使用的PF,那么在內(nèi)核打印消息,并把family設(shè)置成PF*/
1277 if (family == PF_INET && type == SOCK_PACKET) {
1278 static int warned;
1279 if (!warned) {
1280 warned = 1;
1281 printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n",
1282 current->comm);
1283 }
1284 family = PF_PACKET;
1285 }
1286 /*這里調(diào)用security_socket_create函數(shù),該函數(shù)是調(diào)用security_ops->socket_create(family, type, protocol, kern),而security_ops是一個security_operations的結(jié)構(gòu)體,此處再次使用了linux常用的手段,即提供一個結(jié)構(gòu)體數(shù)據(jù)結(jié)構(gòu)來描述所有的操作,該結(jié)構(gòu)體異常復雜,暫時還沒能力理解,只知道該結(jié)構(gòu)體是對應(yīng)著linux系統(tǒng)下的LMS安全框架的。這里其實是個空函數(shù),實際上是進行一系列的安全檢查,然后如果成功則返回0*/
1287 err = security_socket_create(family, type, protocol, kern);
1288 if (err)
1289 return err;
/*此處開始需要再分析多個函數(shù),不再以注釋的方式進行分析,在源代碼的后面一一分析*/
1296 sock = sock_alloc();
1297 if (!sock) {
1298 net_warn_ratelimited("socket: no more sockets\n");
1299 return -ENFILE; /* Not exactly a match, but its the
1300 closest posix thing */
1301 }
1302
1303 sock->type = type;
1304
1305 #ifdef CONFIG_MODULES
1312 if (rcu_access_pointer(net_families[family]) == NULL)
1313 request_module("net-pf-%d", family);
1314 #endif
1315
1316 rcu_read_lock();
1317 pf = rcu_dereference(net_families[family]);
1318 err = -EAFNOSUPPORT;
1319 if (!pf)
1320 goto out_release;
1321
1326 if (!try_module_get(pf->owner))
1327 goto out_release;
1328
1330 rcu_read_unlock();
1331
1332 err = pf->create(net, sock, protocol, kern);
1333 if (err < 0)
1334 goto out_module_put;
1335
1340 if (!try_module_get(sock->ops->owner))
1341 goto out_module_busy;
1342
1347 module_put(pf->owner);
1348 err = security_socket_post_create(sock, family, type, protocol, kern);
1349 if (err)
1350 goto out_sock_release;
1351 *res = sock;
1352
1353 return 0;
1354
1355 out_module_busy:
1356 err = -EAFNOSUPPORT;
1357 out_module_put:
1358 sock->ops = NULL;
1359 module_put(pf->owner);
1360 out_sock_release:
1361 sock_release(sock);
1362 return err;
1363
1364 out_release:
1365 rcu_read_unlock();
1366 goto out_sock_release;
1367 }
如注釋中說的,接下來分析sock = sock_alloc(),首先看sock_alloc()的源代碼:
530 static struct socket *sock_alloc(void)
531 {
532 struct inode *inode;
533 struct socket *sock;
534
535 inode = new_inode_pseudo(sock_mnt->mnt_sb);
536 if (!inode)
537 return NULL;
538
539 sock = SOCKET_I(inode);
540
541 kmemcheck_annotate_bitfield(sock, type);
542 inode->i_ino = get_next_ino();
543 inode->i_mode = S_IFSOCK | S_IRWXUGO;
544 inode->i_uid = current_fsuid();
545 inode->i_gid = current_fsgid();
546 inode->i_op = &sockfs_inode_ops;
547
548 this_cpu_add(sockets_in_use, 1);
549 return sock;
550 }
借助LXR上的注釋我們進行分析。該函數(shù)式分配并初始化一個socket和一個inode,二者是捆綁在一起的,如果成功則返回socket,如果inode創(chuàng)建出問題,則返回NULL。此處有點復雜,耦合性有點強,慢慢來分析。首先分析SOCKET_I(),該函數(shù)的目的是取得socket的結(jié)構(gòu)體指針,具體分析如下:
1322 static inline struct socket *SOCKET_I(struct inode *inode)
1323 {
1324 return &container_of(inode, struct socket_alloc, vfs_inode)->socket;
1325 }
這里使用了宏container_of,我們繼續(xù)分析container_of宏:
718 #define container_of(ptr, type, member) ({ \
719 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
720 (type *)( (char *)__mptr - offsetof(type,member) );})
首先分析傳入?yún)?shù),ptr是指向返回對象的指針,有點繞,但前文說過,inode和socket是綁在一起的,此處其實就是inode指向socket的意思。然后是type,在SOCKET_I中傳入的事socket_alloc結(jié)構(gòu)體,該結(jié)構(gòu)體如下:
1317 struct socket_alloc {
1318 struct socket socket;
1319 struct inode vfs_inode;
1320 };只包含了socket結(jié)構(gòu)體和在虛擬文件系統(tǒng)中的節(jié)點結(jié)構(gòu)體,無甚復雜。宏container_of中還包含了另外一個宏offsetof:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)。
由此分析container_of的內(nèi)容。首先是offsetof,是假設(shè)TYPE(即struct sock_alloc)的地址在0的時候其中MEMBER的地址,即是在計算MEMBER在結(jié)構(gòu)體中的偏移地址,所以這個宏取名為offsetof也不為怪了。回過頭看container_of,這個宏分兩部分:
第一部分typeof( ((type *)0)->member ) *__mptr = (ptr),通過typeof定義一個struct sock_alloc->vfs_inode的指針__mptr,然后賦值為ptr的值,即是說現(xiàn)在vfs_inode的地址就是ptr(即inode)的地址。
第二部分 (type *)( (char *)__mptr - offsetof(type,member) ),用vfs_inode的地址去減去它在結(jié)構(gòu)體中的偏移地址,自然得到結(jié)構(gòu)體的首地址,又因為該結(jié)構(gòu)體的第一個成員是struct socket socket,所以自然返回的是socket的地址,再通過(type *)進行強制類型轉(zhuǎn)換,所以SOCKET_I就得到了新的socket的指針了。
這里的兩個宏有點復雜,但實在是功能強大,值得學習。
這里有一個值得注意的地方,之前分析了這么久的struct sock_alloc,但我們并沒有對這個結(jié)構(gòu)體進行初始化。所以在new_inode_pseudo(sock_mnt->mnt_sb)中必然對這個結(jié)構(gòu)體有所交代。該函數(shù)會調(diào)用alloc_inode(sb)系統(tǒng)調(diào)用,sb是一個超級塊結(jié)構(gòu)。
該系統(tǒng)調(diào)用的源代碼顯示如果傳進去的超級快表操作是alloc_inode的話,則執(zhí)行執(zhí)行之。可以看到,傳進去的參數(shù)是sock_mnt->mnt_sb,再查找該參數(shù)發(fā)現(xiàn)這個參數(shù)是在初始化sock_init中進行kern_mount時賦值的,恰好使得傳進去的參數(shù)是alloc_inode,所以,接下來執(zhí)行超級快操作表中的alloc_inode,然后調(diào)用sock_alloc_inode系統(tǒng)調(diào)用。
該系統(tǒng)調(diào)用先是ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);進行slab分配,爾后的代碼也是進行inode的操作和slab的分配,到此為止,暫時不再繼續(xù)深入,只是注意到經(jīng)過這一系列地分配之后,socket處于未連接狀態(tài),即socket.state = SS_UNCONNECTED。
至此回到__sock_create()繼續(xù)分析。接下來是一次條件編譯。#ifdef CONFIG_MODULES,這個選項是用于linux的模塊編寫的,如果在模塊編寫時用上了CONFIG_MODULES,是會在makemenuconfig中出現(xiàn)該模塊選項的。該處如果有CONFIG_MODULES但是卻沒有找到對應(yīng)的下屬選項則會裝載一個default的模塊。
接下去是:
1312 if (rcu_access_pointer(net_families[family]) == NULL)
1313 request_module("net-pf-%d", family);
這兩句檢查對應(yīng)協(xié)議族的操作表是否已經(jīng)安裝,如果沒有安裝則使用request_module進行安裝。現(xiàn)在都是在TCP/IP協(xié)議下進行分析,所以family是AF_INET,也就是2,所以實際檢查的是全局變量net_families[2]。這個全局變量是在系統(tǒng)初始化時由net/ipv4/af_inet.c文件進行安裝,具體代碼是:fs_initcall(inet_init);而fs_initcall是個宏,具體實現(xiàn)是:
204 #define fs_initcall(fn) __define_initcall(fn, 5)
即是把inet_init裝載在init_call中,所以在系統(tǒng)啟動時自然會初始化。下面不計細節(jié)地分析inet_init:
1678 static int __init inet_init(void)
1679 {
...............
/*把各種proto注冊到全局鏈表中去*/
1690 rc = proto_register(&tcp_prot, 1);
1691 if (rc)
1692 goto out_free_reserved_ports;
1693
1694 rc = proto_register(&udp_prot, 1);
1695 if (rc)
1696 goto out_unregister_tcp_proto;
1697
1698 rc = proto_register(&raw_prot, 1);
1699 if (rc)
1700 goto out_unregister_udp_proto;
.......................
/*注冊協(xié)議族操作表*/
1710 (void)sock_register(&inet_family_ops);
.....................
/*把各個協(xié)議對應(yīng)的基礎(chǔ)原型(base protocol)加到對應(yīng)的數(shù)據(jù)結(jié)構(gòu)中*/
1720 if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
1721 pr_crit("%s: Cannot add ICMP protocol\n", __func__);
1722 if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
1723 pr_crit("%s: Cannot add UDP protocol\n", __func__);
1724 if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
1725 pr_crit("%s: Cannot add TCP protocol\n", __func__);
..................
1731 /* Register the socket-side information for inet_create. */
1732 for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
1733 INIT_LIST_HEAD(r);
/*把inetsw_array[]注冊進基礎(chǔ)原型(base protocol)的數(shù)組鏈表中*/
1735 for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
1736 inet_register_protosw(q);
..............
1802 }
至此回到__socket_create()的分析。pf = rcu_dereference(net_families[family]);是在RCU鎖的保護下取得指定處的內(nèi)容。if (!try_module_get(pf->owner))是模塊檢查。因為之后要調(diào)用->create,這個恰好有可能存在于某個可裝載的模塊中,所以先檢查是否在模塊中,不在的話繼續(xù)執(zhí)行下去。
然后是err = pf->create(net, sock, protocol, kern);這里調(diào)用pf中的create成員,由前面的inet_init()中的sock_register可知,把inet_family_ops裝載進去,而這張表的create成員(也可以叫行為)掛入的是inet_create(),下面分析inet_create()。這個函數(shù)有些龐大,一部分一部分地看:
275 static int inet_create(struct net *net, struct socket *sock, int protocol,
276 int kern)
277 {
278 struct sock *sk;
279 struct inet_protosw *answer;
280 struct inet_sock *inet;
281 struct proto *answer_prot;
282 unsigned char answer_flags;
283 char answer_no_check;
284 int try_loading_module = 0;
285 int err;
/*由inet_ehash_secret的值看是否有加密字符串,若沒有則檢查協(xié)議類型(只有TCP肯能有加密字符串),如果socket類型不是原始套接字或者數(shù)據(jù)報型的話,則創(chuàng)建一個加密字符串*/
287 if (unlikely(!inet_ehash_secret))
288 if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)
289 build_ehash_secret();
/*把socket的狀態(tài)設(shè)置成未聯(lián)通*/
291 sock->state = SS_UNCONNECTED;
接下來涉及到inet_protosw結(jié)構(gòu)體,該結(jié)構(gòu)體是用于IP協(xié)議對應(yīng)socket接口,分析如下:
struct inet_protosw {
struct list_head list;
unsigned short type; /* 對應(yīng)socket的類型)*/. unsigned short protocol; /* IP協(xié)議編碼 */
struct proto *prot; /*對應(yīng)的協(xié)議結(jié)構(gòu)體的指針*/
const struct proto_ops *ops; /*對應(yīng)協(xié)議的操作表*/
char no_check; /* 是否計算校驗和 */
unsigned char flags; /* 標志位 */
};
接下來繼續(xù)分析inet_create():
/* 遍歷尋找請求的協(xié)議類型 */
294 lookup_protocol:
295 err = -ESOCKTNOSUPPORT;
296 rcu_read_lock();
/* 遍歷inetsw[]數(shù)組對應(yīng)請求類型的鏈表元素,此處需要看一下inetsw[]的定義。這個數(shù)組由三個結(jié)構(gòu)變量組成。第一種用于TCP數(shù)據(jù)流協(xié)議,標識碼為IPPROTO_TCP,第二種用于UDP數(shù)據(jù)包協(xié)議,協(xié)議標識碼為IPPROTO_UDP,第三種為原始套接字使用,可以是用戶自己的協(xié)議,標識碼IPPROTO_IP是虛擬IP協(xié)議類型。這個數(shù)組是在inet_init()中由inet_register_protosw來注冊的,該函數(shù)的主要步驟為1.檢查參數(shù)結(jié)構(gòu)體inet_protosw是否越界2.通過type查找匹配的隊列3.插入到隊列中。
然后看list_for_each_rcu,這個函數(shù)在數(shù)組inetsw[]中根據(jù)sock->type(一般程序指定為SOCK_STREAM即TCP協(xié)議類型)找到協(xié)議類型所在隊列并使得answer指向了TCP協(xié)議的inet_protosw結(jié)構(gòu)。為后面的判斷做好鋪墊。
*/
297 list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {
298
299 err = 0;
/*由于在服務(wù)器程序中一般引用listenfd = socket(AF_INET,SOCK_STREAM,0)的代碼,所以此處的protocol=0,而answer->protocol由之前的list_for_each_rcu可知是TCP的,而IPPROTO_IP是屬于虛擬IP類型,與原始套接字相關(guān)。所以此處301-313的代碼負責選擇出合適的協(xié)議*/
301 if (protocol == answer->protocol) {
302 if (protocol != IPPROTO_IP)
303 break;
304 } else {
306 if (IPPROTO_IP == protocol) {
307 protocol = answer->protocol;
308 break;
309 }
310 if (IPPROTO_IP == answer->protocol)
311 break;
312 }
313 err = -EPROTONOSUPPORT;
314 }
/*此處根據(jù)err的值和已經(jīng)加載了的模塊數(shù)量動態(tài)的安裝本次請求需要的協(xié)議類型的結(jié)構(gòu),但看到了unlikely,再分析前面的代碼發(fā)現(xiàn)err很大可能(最起碼在我們一般性的TCP/IP編程中)此時還是等于0的,即是說,由于TCP協(xié)議的結(jié)構(gòu)已經(jīng)安裝,不必再安裝,直接進入下一步*/
316 if (unlikely(err)) {
317 if (try_loading_module < 2) {
318 rcu_read_unlock();
321 *(net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM)
323 if (++try_loading_module == 1)
324 request_module("net-pf-%d-proto-%d-type-%d",
325 PF_INET, protocol, sock->type);
/*否則就使用通用名稱*/
330 else
331 request_module("net-pf-%d-proto-%d",
332 PF_INET, protocol);
333 goto lookup_protocol;
334 } else
335 goto out_rcu_unlock;
336 }
337
338 err = -EPERM;
/*檢查通用性。只有root才有權(quán)限使用原始套接字。*/
339 if (sock->type == SOCK_RAW && !kern &&
340 !ns_capable(net->user_ns, CAP_NET_RAW))
341 goto out_rcu_unlock;
/*對socket的操作集合進行了掛鉤,指向了answer->ops,由于在inet_protosw的設(shè)置中已經(jīng)設(shè)置成了TCP協(xié)議相關(guān),所以此處sock->ops設(shè)置為inet_stream_ops,而answer_prot設(shè)置為tcp_prot結(jié)構(gòu)。該結(jié)構(gòu)式一個struct proto結(jié)構(gòu),負責傳輸層使用。*/
343 sock->ops = answer->ops;
344 answer_prot = answer->prot;
345 answer_no_check = answer->no_check;
346 answer_flags = answer->flags;
347 rcu_read_unlock();
348
349 WARN_ON(answer_prot->slab == NULL);
350
351 err = -ENOBUFS;
/*此處調(diào)用sk_alloc分配一個struct sock,該結(jié)構(gòu)體龐大,其作用是網(wǎng)絡(luò)層對socket的表示,意思就是IP協(xié)議下有很多東西比如IP地址,網(wǎng)卡接口,端口等等信息需要再socket層中有所體現(xiàn)從而使編程者方便使用,然后就利用指針等形式把內(nèi)容進行一定程度上的映射。sk_alloc首先對sock->proto和sock_creator進行設(shè)置,設(shè)置成當前協(xié)議對應(yīng)的proto調(diào)用sk_prot_alloc()根據(jù)是否提供了slab緩存而判斷是使用slab緩存還是通用緩存。只要分配成功,則調(diào)用sock_lock_init()對緩存進行初始化,主要是對sock鎖、等待隊列以及進程數(shù)據(jù)結(jié)構(gòu)中的網(wǎng)絡(luò)空間結(jié)構(gòu)進行分配。初始化完了后調(diào)用sock_net_set()函數(shù)對網(wǎng)絡(luò)空間結(jié)構(gòu)進行記錄,然后最后增加一個net計數(shù)器。至此回到inet_create,判斷是否成功分配*/
352 sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
353 if (sk == NULL)
354 goto out;
355
356 err = 0;
/*可復用性檢查,不懂*/
357 sk->sk_no_check = answer_no_check;
358 if (INET_PROTOSW_REUSE & answer_flags)
359 sk->sk_reuse = SK_CAN_REUSE;
/*返回一個struct inet_sock的指針給inet*/
361 inet = inet_sk(sk);
/*判斷是不是面向連通(目前只有SOCK_STREAM是面向連通的,不可以理解成TCP,因為ICMP也是面向連通的)*/
362 inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;
363
364 inet->nodefrag = 0;
/*判斷是否是原始套接字,如果是,新建IP頭部*/
366 if (SOCK_RAW == sock->type) {
367 inet->inet_num = protocol;
368 if (IPPROTO_RAW == protocol)
369 inet->hdrincl = 1;
370 }
/*判斷是否采用路徑MTU發(fā)現(xiàn)算法*/
372 if (ipv4_config.no_pmtu_disc)
373 inet->pmtudisc = IP_PMTUDISC_DONT;
374 else
375 inet->pmtudisc = IP_PMTUDISC_WANT;
376
377 inet->inet_id = 0;
/*進一步初始化sk結(jié)構(gòu)(struct sock),此處分析一下sock_init_data:初始化接收(讀)隊列,初始化寫隊列,初始化錯誤信息隊列,注意這三個隊列都是雙向鏈表,屬于sk_buff_head結(jié)構(gòu)體,其中會把sk_buff結(jié)構(gòu)體串聯(lián)起來。初始化數(shù)據(jù)包發(fā)送定時器,變量(主要是函數(shù)指針即鉤子函數(shù))*/
379 sock_init_data(sock, sk);
380
381 sk->sk_destruct = inet_sock_destruct;
382 sk->sk_protocol = protocol;
383 sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
384
385 inet->uc_ttl = -1;
386 inet->mc_loop = 1;
387 inet->mc_ttl = 1;
388 inet->mc_all = 1;
389 inet->mc_index = 0;
390 inet->mc_list = NULL;
391 inet->rcv_tos = 0;
392
393 sk_refcnt_debug_inc(sk);
394
395 if (inet->inet_num) {
396 /* It assumes that any protocol which allows
397 * the user to assign a number at socket
398 * creation time automatically
399 * shares.
400 */
401 inet->inet_sport = htons(inet->inet_num);
402 /* Add to protocol hash chains. */
403 sk->sk_prot->hash(sk);
404 }
/*查閱前面的sock_alloc會發(fā)現(xiàn),這里的sk_prot被設(shè)置為answer_prot(即answer->prot),而answer_prot又被設(shè)置成了tcp_prot,所以此處調(diào)用tcp_prot中的init,查閱tcp_prot即可得到init這個函數(shù)指針指向的是tcp_v4_init_sock(),接下來分析tcp_v4_init_sock(見源代碼后面)*/
406 if (sk->sk_prot->init) {
407 err = sk->sk_prot->init(sk);
408 if (err)
409 sk_common_release(sk);
410 }
411 out:
412 return err;
413 out_rcu_unlock:
414 rcu_read_unlock();
415 goto out;
416 }
這里分析tcp_v4_init_sock,這個函數(shù)負責初始化TCP的IPV4的部分:
2150 static int tcp_v4_init_sock(struct sock *sk)
2151 {
2152 struct inet_connection_sock *icsk = inet_csk(sk);
2153
2154 tcp_init_sock(sk);
2155
2156 icsk->icsk_af_ops = &ipv4_specific;
2157
2158 #ifdef CONFIG_TCP_MD5SIG
2159 tcp_sk(sk)->af_specific = &tcp_sock_ipv4_specific;
2160 #endif
2161
2162 return 0;
2163 }
這里涉及兩個新的數(shù)據(jù)結(jié)構(gòu)tcp_sock與inet_connection_sock,其實還有一個數(shù)據(jù)結(jié)構(gòu)inet_sock。之前提過struct socket是面向用戶態(tài)的,而struct sock是面向內(nèi)核驅(qū)動的,但socket與內(nèi)核協(xié)議棧的交互實際上是通過xxx_sock這個數(shù)據(jù)機構(gòu),此處是TCP協(xié)議,自然是tcp_sock 數(shù)據(jù)結(jié)構(gòu),保存所有tcp相關(guān)信息。而inet_connection_sock實際上是inet_sock的擴展,之前說過inet_sock是INET域在socket的表現(xiàn),而inet_connection_sock就是在inet_sock的基礎(chǔ)上保存連接信息。
tcp_v4_init_sock中的tcp_init_sock(sk)是具體的初始化舉措,尤其重要的是對SIN和AFK這兩個SYN幀的初始化。
至此inet_create函數(shù)分析完畢,同時sock_create函數(shù)也分析完畢,接下來分析sock_map_fd():
386 static int sock_map_fd(struct socket *sock, int flags)
387 {
388 struct file *newfile;
389 int fd = get_unused_fd_flags(flags);
390 if (unlikely(fd < 0))
391 return fd;
392
393 newfile = sock_alloc_file(sock, flags, NULL);
394 if (likely(!IS_ERR(newfile))) {
395 fd_install(fd, newfile);
396 return fd;
397 }
398
399 put_unused_fd(fd);
400 return PTR_ERR(newfile);
401 }
首先明白在用戶空間中操作socket是完全當成文件在操作,但之前只分配了相應(yīng)的inode和網(wǎng)絡(luò)空間結(jié)構(gòu),所以本函數(shù)的目的是與文件系統(tǒng)形成關(guān)聯(lián)。
本函數(shù)在3.9中與2.6內(nèi)核區(qū)別較大,一句一句地分析。get_unused_fd_flags會調(diào)用__alloc_fd(),該函數(shù)負責分配一個文件描述符并設(shè)置成busy狀態(tài)。然后執(zhí)行sock_alloc_file,該函數(shù)負責得到第一個沒被使用的文件,并對其path,狀態(tài)等等進行設(shè)置,然后再把文件操作表socket_file_ops裝載好。然后執(zhí)行fd_install(fd, newfile),把文件描述符和文件進行關(guān)聯(lián)。最后執(zhí)行put_unused_fd。
至此整個socket的創(chuàng)建完成。
總結(jié)一下這次分析的過程。對分析源碼的步驟有了新的想法,首先應(yīng)該想到要完成這一步從理論上需要完成哪些東西,然后再遞歸地去分析這些東西又需要完成那些步驟。然后根據(jù)每個步驟去看由哪些代碼執(zhí)行,比如在socket的創(chuàng)建中,我就應(yīng)該從文件系統(tǒng)初始化,通用socket初始化,TCP初始化,INET初始化,sock初始化,協(xié)議對應(yīng)等等部分來分析,而不是盲目地一步一步地去看。
其次,不能糾纏于細節(jié),個別有重大意義的細節(jié)可以仔細分析,但更多的時候應(yīng)該一塊一塊地去看代碼看這些代碼完成了什么功能,而不是糾結(jié)于每一句想完成什么。
最后也是最重要的,在分析源碼之前需要先分析整個過程涉及的數(shù)據(jù)結(jié)構(gòu)以及他們存在的目的,他們之間的關(guān)系,只有在數(shù)據(jù)結(jié)構(gòu)清晰明了了之后才可能對過程有較為清晰容易地理解。