linux內(nèi)核中socket的創(chuàng)建過(guò)程源碼分析(詳細(xì)分析)

1三個(gè)相關(guān)數(shù)據(jù)結(jié)構(gòu).

關(guān)于socket的創(chuàng)建档泽,首先需要分析socket這個(gè)結(jié)構(gòu)體适秩,這是整個(gè)的核心席舍。

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是標(biāo)志位,負(fù)責(zé)一些特殊的設(shè)置荠察,比如SOCK_ASYNC_NOSPACE置蜀,ops則是采用了和超級(jí)塊設(shè)備操作表一樣的邏輯奈搜,專門設(shè)置了一個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)記錄其允許的操作悉盆。Sk是非常重要的,也是非常大的馋吗,負(fù)責(zé)記錄協(xié)議相關(guān)內(nèi)容焕盟。這樣的設(shè)置使得socket具有很好的協(xié)議無(wú)關(guān)性,可以通用。file是與socket相關(guān)的指針列表脚翘,wq是等待隊(duì)列灼卢。

還有兩個(gè)結(jié)構(gòu)體sk_buff和tcp_sock,其中sk_buff與每一個(gè)數(shù)據(jù)包相關(guān)来农,負(fù)責(zé)描述每一個(gè)數(shù)據(jù)包的信息鞋真,而tcp_sock則是tcp相關(guān)的結(jié)構(gòu)體

2.初始化并分配socket結(jié)構(gòu)

Socket()本質(zhì)上是一個(gè)glibc中的函數(shù),執(zhí)行實(shí)際上是是調(diào)用sys_socketcall()系統(tǒng)調(diào)用沃于。sys_socketcall()是幾乎所有socket相關(guān)函數(shù)的入口涩咖,即是說(shuō),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 }

省略號(hào)省略掉的是各種網(wǎng)絡(luò)編程用得到的函數(shù),全部都在這個(gè)系統(tǒng)調(diào)用中咨演,再次證明前文所說(shuō)闸昨,socketcall是系統(tǒng)所有socket相關(guān)函數(shù)打大門。

而對(duì)于創(chuàng)建socket薄风,自然會(huì)在switch中調(diào)用到sys_socket()系統(tǒng)調(diào)用饵较,而這個(gè)函數(shù)僅僅是調(diào)用sock_create()來(lái)創(chuàng)建socket和sock_map_fd()來(lái)與文件系統(tǒng)進(jìn)行關(guān)聯(lián)。其中sock_create()調(diào)用__sock_create().接下來(lái)我盡力地來(lái)分析一下源代碼(直接在代碼中注釋):

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沒(méi)什么區(qū)別,但事實(shí)上二者PF是POSIX標(biāo)準(zhǔn)嵌牺,而AF是BSD標(biāo)準(zhǔn)打洼,按照我的理解,現(xiàn)在大部分使用BSD標(biāo)準(zhǔn)逆粹,因此募疮,如果傳入?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是一個(gè)security_operations的結(jié)構(gòu)體,此處再次使用了linux常用的手段蹋绽,即提供一個(gè)結(jié)構(gòu)體數(shù)據(jù)結(jié)構(gòu)來(lái)描述所有的操作芭毙,該結(jié)構(gòu)體異常復(fù)雜,暫時(shí)還沒(méi)能力理解卸耘,只知道該結(jié)構(gòu)體是對(duì)應(yīng)著linux系統(tǒng)下的LMS安全框架的退敦。這里其實(shí)是個(gè)空函數(shù),實(shí)際上是進(jìn)行一系列的安全檢查蚣抗,然后如果成功則返回0*/

1287 err = security_socket_create(family, type, protocol, kern);

1288 if (err)

1289 return err;

/*此處開(kāi)始需要再分析多個(gè)函數(shù)侈百,不再以注釋的方式進(jìn)行分析,在源代碼的后面一一分析*/

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 }

如注釋中說(shuō)的,接下來(lái)分析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上的注釋我們進(jìn)行分析讽坏。該函數(shù)式分配并初始化一個(gè)socket和一個(gè)inode,二者是捆綁在一起的例证,如果成功則返回socket路呜,如果inode創(chuàng)建出問(wèn)題,則返回NULL织咧。此處有點(diǎn)復(fù)雜拣宰,耦合性有點(diǎn)強(qiáng),慢慢來(lái)分析烦感。首先分析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是指向返回對(duì)象的指針晌该,有點(diǎn)繞,但前文說(shuō)過(guò)绿渣,inode和socket是綁在一起的朝群,此處其實(shí)就是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é)點(diǎn)結(jié)構(gòu)體,無(wú)甚復(fù)雜淀散。宏container_of中還包含了另外一個(gè)宏offsetof:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)右莱。

由此分析container_of的內(nèi)容。首先是offsetof档插,是假設(shè)TYPE(即struct sock_alloc)的地址在0的時(shí)候其中MEMBER的地址慢蜓,即是在計(jì)算MEMBER在結(jié)構(gòu)體中的偏移地址,所以這個(gè)宏取名為offsetof也不為怪了郭膛〕柯眨回過(guò)頭看container_of,這個(gè)宏分兩部分:

第一部分typeof( ((type *)0)->member ) *__mptr = (ptr)则剃,通過(guò)typeof定義一個(gè)struct sock_alloc->vfs_inode的指針__mptr,然后賦值為ptr的值耘柱,即是說(shuō)現(xiàn)在vfs_inode的地址就是ptr(即inode)的地址。

第二部分 (type *)( (char *)__mptr - offsetof(type,member) )棍现,用vfs_inode的地址去減去它在結(jié)構(gòu)體中的偏移地址调煎,自然得到結(jié)構(gòu)體的首地址,又因?yàn)樵摻Y(jié)構(gòu)體的第一個(gè)成員是struct socket socket轴咱,所以自然返回的是socket的地址汛蝙,再通過(guò)(type *)進(jìn)行強(qiáng)制類型轉(zhuǎn)換,所以SOCKET_I就得到了新的socket的指針了朴肺。

這里的兩個(gè)宏有點(diǎn)復(fù)雜窖剑,但實(shí)在是功能強(qiáng)大,值得學(xué)習(xí)戈稿。

這里有一個(gè)值得注意的地方西土,之前分析了這么久的struct sock_alloc,但我們并沒(méi)有對(duì)這個(gè)結(jié)構(gòu)體進(jìn)行初始化鞍盗。所以在new_inode_pseudo(sock_mnt->mnt_sb)中必然對(duì)這個(gè)結(jié)構(gòu)體有所交代需了。該函數(shù)會(huì)調(diào)用alloc_inode(sb)系統(tǒng)調(diào)用,sb是一個(gè)超級(jí)塊結(jié)構(gòu)般甲。

該系統(tǒng)調(diào)用的源代碼顯示如果傳進(jìn)去的超級(jí)快表操作是alloc_inode的話肋乍,則執(zhí)行執(zhí)行之》蟠妫可以看到墓造,傳進(jìn)去的參數(shù)是sock_mnt->mnt_sb,再查找該參數(shù)發(fā)現(xiàn)這個(gè)參數(shù)是在初始化sock_init中進(jìn)行kern_mount時(shí)賦值的锚烦,恰好使得傳進(jìn)去的參數(shù)是alloc_inode觅闽,所以,接下來(lái)執(zhí)行超級(jí)快操作表中的alloc_inode涮俄,然后調(diào)用sock_alloc_inode系統(tǒng)調(diào)用蛉拙。

該系統(tǒng)調(diào)用先是ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);進(jìn)行slab分配,爾后的代碼也是進(jìn)行inode的操作和slab的分配彻亲,到此為止孕锄,暫時(shí)不再繼續(xù)深入,只是注意到經(jīng)過(guò)這一系列地分配之后苞尝,socket處于未連接狀態(tài)硫惕,即socket.state = SS_UNCONNECTED。

至此回到__sock_create()繼續(xù)分析野来。接下來(lái)是一次條件編譯恼除。#ifdef CONFIG_MODULES,這個(gè)選項(xiàng)是用于linux的模塊編寫(xiě)的曼氛,如果在模塊編寫(xiě)時(shí)用上了CONFIG_MODULES豁辉,是會(huì)在makemenuconfig中出現(xiàn)該模塊選項(xiàng)的。該處如果有CONFIG_MODULES但是卻沒(méi)有找到對(duì)應(yīng)的下屬選項(xiàng)則會(huì)裝載一個(gè)default的模塊舀患。

接下去是:

1312 if (rcu_access_pointer(net_families[family]) == NULL)

1313 request_module("net-pf-%d", family);

這兩句檢查對(duì)應(yīng)協(xié)議族的操作表是否已經(jīng)安裝徽级,如果沒(méi)有安裝則使用request_module進(jìn)行安裝。現(xiàn)在都是在TCP/IP協(xié)議下進(jìn)行分析聊浅,所以family是AF_INET餐抢,也就是2现使,所以實(shí)際檢查的是全局變量net_families[2]。這個(gè)全局變量是在系統(tǒng)初始化時(shí)由net/ipv4/af_inet.c文件進(jìn)行安裝旷痕,具體代碼是:fs_initcall(inet_init);而fs_initcall是個(gè)宏碳锈,具體實(shí)現(xiàn)是:

204 #define fs_initcall(fn) __define_initcall(fn, 5)

即是把inet_init裝載在init_call中,所以在系統(tǒng)啟動(dòng)時(shí)自然會(huì)初始化欺抗。

1三個(gè)相關(guān)數(shù)據(jù)結(jié)構(gòu).

關(guān)于socket的創(chuàng)建售碳,首先需要分析socket這個(gè)結(jié)構(gòu)體,這是整個(gè)的核心绞呈。

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是標(biāo)志位,負(fù)責(zé)一些特殊的設(shè)置圾亏,比如SOCK_ASYNC_NOSPACE力惯,ops則是采用了和超級(jí)塊設(shè)備操作表一樣的邏輯,專門設(shè)置了一個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)記錄其允許的操作召嘶。Sk是非常重要的父晶,也是非常大的,負(fù)責(zé)記錄協(xié)議相關(guān)內(nèi)容弄跌。這樣的設(shè)置使得socket具有很好的協(xié)議無(wú)關(guān)性甲喝,可以通用。file是與socket相關(guān)的指針列表铛只,wq是等待隊(duì)列埠胖。

還有兩個(gè)結(jié)構(gòu)體sk_buff和tcp_sock,其中sk_buff與每一個(gè)數(shù)據(jù)包相關(guān)淳玩,負(fù)責(zé)描述每一個(gè)數(shù)據(jù)包的信息直撤,而tcp_sock則是tcp相關(guān)的結(jié)構(gòu)體

3.初始化并分配socket結(jié)構(gòu)

Socket()本質(zhì)上是一個(gè)glibc中的函數(shù),執(zhí)行實(shí)際上是是調(diào)用sys_socketcall()系統(tǒng)調(diào)用蜕着。sys_socketcall()是幾乎所有socket相關(guān)函數(shù)的入口谋竖,即是說(shuō),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 }

省略號(hào)省略掉的是各種網(wǎng)絡(luò)編程用得到的函數(shù),全部都在這個(gè)系統(tǒng)調(diào)用中韧骗,再次證明前文所說(shuō)嘉抒,socketcall是系統(tǒng)所有socket相關(guān)函數(shù)打大門。

而對(duì)于創(chuàng)建socket袍暴,自然會(huì)在switch中調(diào)用到sys_socket()系統(tǒng)調(diào)用些侍,而這個(gè)函數(shù)僅僅是調(diào)用sock_create()來(lái)創(chuàng)建socket和sock_map_fd()來(lái)與文件系統(tǒng)進(jìn)行關(guān)聯(lián)隶症。其中sock_create()調(diào)用__sock_create().接下來(lái)我盡力地來(lái)分析一下源代碼(直接在代碼中注釋):

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沒(méi)什么區(qū)別蚂会,但事實(shí)上二者PF是POSIX標(biāo)準(zhǔn),而AF是BSD標(biāo)準(zhǔn)狈定,按照我的理解颂龙,現(xiàn)在大部分使用BSD標(biāo)準(zhǔn)习蓬,因此纽什,如果傳入?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是一個(gè)security_operations的結(jié)構(gòu)體,此處再次使用了linux常用的手段枫慷,即提供一個(gè)結(jié)構(gòu)體數(shù)據(jù)結(jié)構(gòu)來(lái)描述所有的操作让蕾,該結(jié)構(gòu)體異常復(fù)雜,暫時(shí)還沒(méi)能力理解或听,只知道該結(jié)構(gòu)體是對(duì)應(yīng)著linux系統(tǒng)下的LMS安全框架的探孝。這里其實(shí)是個(gè)空函數(shù),實(shí)際上是進(jìn)行一系列的安全檢查誉裆,然后如果成功則返回0*/

1287 err = security_socket_create(family, type, protocol, kern);

1288 if (err)

1289 return err;

/*此處開(kāi)始需要再分析多個(gè)函數(shù)顿颅,不再以注釋的方式進(jìn)行分析,在源代碼的后面一一分析*/

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 }

如注釋中說(shuō)的足丢,接下來(lái)分析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上的注釋我們進(jìn)行分析。該函數(shù)式分配并初始化一個(gè)socket和一個(gè)inode斩跌,二者是捆綁在一起的绍些,如果成功則返回socket,如果inode創(chuàng)建出問(wèn)題耀鸦,則返回NULL柬批。此處有點(diǎn)復(fù)雜,耦合性有點(diǎn)強(qiáng)袖订,慢慢來(lái)分析萝快。首先分析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是指向返回對(duì)象的指針,有點(diǎn)繞吏口,但前文說(shuō)過(guò)奄容,inode和socket是綁在一起的冰更,此處其實(shí)就是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é)點(diǎn)結(jié)構(gòu)體,無(wú)甚復(fù)雜戈盈。宏container_of中還包含了另外一個(gè)宏offsetof:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)奠衔。

由此分析container_of的內(nèi)容。首先是offsetof塘娶,是假設(shè)TYPE(即struct sock_alloc)的地址在0的時(shí)候其中MEMBER的地址归斤,即是在計(jì)算MEMBER在結(jié)構(gòu)體中的偏移地址,所以這個(gè)宏取名為offsetof也不為怪了刁岸≡嗬铮回過(guò)頭看container_of,這個(gè)宏分兩部分:

第一部分typeof( ((type *)0)->member ) *__mptr = (ptr)虹曙,通過(guò)typeof定義一個(gè)struct sock_alloc->vfs_inode的指針__mptr,然后賦值為ptr的值迫横,即是說(shuō)現(xiàn)在vfs_inode的地址就是ptr(即inode)的地址。

第二部分 (type *)( (char *)__mptr - offsetof(type,member) )酝碳,用vfs_inode的地址去減去它在結(jié)構(gòu)體中的偏移地址矾踱,自然得到結(jié)構(gòu)體的首地址,又因?yàn)樵摻Y(jié)構(gòu)體的第一個(gè)成員是struct socket socket疏哗,所以自然返回的是socket的地址呛讲,再通過(guò)(type *)進(jìn)行強(qiáng)制類型轉(zhuǎn)換,所以SOCKET_I就得到了新的socket的指針了沃斤。

這里的兩個(gè)宏有點(diǎn)復(fù)雜圣蝎,但實(shí)在是功能強(qiáng)大,值得學(xué)習(xí)衡瓶。

這里有一個(gè)值得注意的地方徘公,之前分析了這么久的struct sock_alloc,但我們并沒(méi)有對(duì)這個(gè)結(jié)構(gòu)體進(jìn)行初始化哮针。所以在new_inode_pseudo(sock_mnt->mnt_sb)中必然對(duì)這個(gè)結(jié)構(gòu)體有所交代关面。該函數(shù)會(huì)調(diào)用alloc_inode(sb)系統(tǒng)調(diào)用,sb是一個(gè)超級(jí)塊結(jié)構(gòu)十厢。

該系統(tǒng)調(diào)用的源代碼顯示如果傳進(jìn)去的超級(jí)快表操作是alloc_inode的話等太,則執(zhí)行執(zhí)行之÷牛可以看到缩抡,傳進(jìn)去的參數(shù)是sock_mnt->mnt_sb,再查找該參數(shù)發(fā)現(xiàn)這個(gè)參數(shù)是在初始化sock_init中進(jìn)行kern_mount時(shí)賦值的包颁,恰好使得傳進(jìn)去的參數(shù)是alloc_inode瞻想,所以压真,接下來(lái)執(zhí)行超級(jí)快操作表中的alloc_inode,然后調(diào)用sock_alloc_inode系統(tǒng)調(diào)用蘑险。

該系統(tǒng)調(diào)用先是ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);進(jìn)行slab分配滴肿,爾后的代碼也是進(jìn)行inode的操作和slab的分配,到此為止佃迄,暫時(shí)不再繼續(xù)深入泼差,只是注意到經(jīng)過(guò)這一系列地分配之后,socket處于未連接狀態(tài)呵俏,即socket.state = SS_UNCONNECTED堆缘。

至此回到__sock_create()繼續(xù)分析。接下來(lái)是一次條件編譯柴信。#ifdef CONFIG_MODULES套啤,這個(gè)選項(xiàng)是用于linux的模塊編寫(xiě)的宽气,如果在模塊編寫(xiě)時(shí)用上了CONFIG_MODULES随常,是會(huì)在makemenuconfig中出現(xiàn)該模塊選項(xiàng)的。該處如果有CONFIG_MODULES但是卻沒(méi)有找到對(duì)應(yīng)的下屬選項(xiàng)則會(huì)裝載一個(gè)default的模塊萄涯。

接下去是:

1312 if (rcu_access_pointer(net_families[family]) == NULL)

1313 request_module("net-pf-%d", family);

這兩句檢查對(duì)應(yīng)協(xié)議族的操作表是否已經(jīng)安裝绪氛,如果沒(méi)有安裝則使用request_module進(jìn)行安裝。現(xiàn)在都是在TCP/IP協(xié)議下進(jìn)行分析涝影,所以family是AF_INET枣察,也就是2,所以實(shí)際檢查的是全局變量net_families[2]燃逻。這個(gè)全局變量是在系統(tǒng)初始化時(shí)由net/ipv4/af_inet.c文件進(jìn)行安裝序目,具體代碼是:fs_initcall(inet_init);而fs_initcall是個(gè)宏,具體實(shí)現(xiàn)是:

204 #define fs_initcall(fn) __define_initcall(fn, 5)

即是把inet_init裝載在init_call中伯襟,所以在系統(tǒng)啟動(dòng)時(shí)自然會(huì)初始化猿涨。下面不計(jì)細(xì)節(jié)地分析inet_init:

1678 static int __init inet_init(void)

1679 {

...............

/*把各種proto注冊(cè)到全局鏈表中去*/

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;

.......................

/*注冊(cè)協(xié)議族操作表*/

1710 (void)sock_register(&inet_family_ops);

.....................

/*把各個(gè)協(xié)議對(duì)應(yīng)的基礎(chǔ)原型(base protocol)加到對(duì)應(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[]注冊(cè)進(jìn)基礎(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鎖的保護(hù)下取得指定處的內(nèi)容姆怪。if (!try_module_get(pf->owner))是模塊檢查叛赚。因?yàn)橹笠{(diào)用->create,這個(gè)恰好有可能存在于某個(gè)可裝載的模塊中稽揭,所以先檢查是否在模塊中俺附,不在的話繼續(xù)執(zhí)行下去。

然后是err = pf->create(net, sock, protocol, kern);這里調(diào)用pf中的create成員溪掀,由前面的inet_init()中的sock_register可知事镣,把inet_family_ops裝載進(jìn)去,而這張表的create成員(也可以叫行為)掛入的是inet_create()揪胃,下面分析inet_create()璃哟。這個(gè)函數(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的值看是否有加密字符串,若沒(méi)有則檢查協(xié)議類型(只有TCP肯能有加密字符串)沮稚,如果socket類型不是原始套接字或者數(shù)據(jù)報(bào)型的話艺沼,則創(chuàng)建一個(gè)加密字符串*/

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;

接下來(lái)涉及到inet_protosw結(jié)構(gòu)體,該結(jié)構(gòu)體是用于IP協(xié)議對(duì)應(yīng)socket接口蕴掏,分析如下:

struct inet_protosw {

struct list_head list;

unsigned short type; /* 對(duì)應(yīng)socket的類型)*/. unsigned short protocol; /* IP協(xié)議編碼 */

struct proto *prot; /*對(duì)應(yīng)的協(xié)議結(jié)構(gòu)體的指針*/

const struct proto_ops *ops; /*對(duì)應(yīng)協(xié)議的操作表*/

char no_check; /* 是否計(jì)算校驗(yàn)和 */

unsigned char flags; /* 標(biāo)志位 */

};

接下來(lái)繼續(xù)分析inet_create():

/* 遍歷尋找請(qǐng)求的協(xié)議類型 */

294 lookup_protocol:

295 err = -ESOCKTNOSUPPORT;

296 rcu_read_lock();

/* 遍歷inetsw[]數(shù)組對(duì)應(yīng)請(qǐng)求類型的鏈表元素障般,此處需要看一下inetsw[]的定義。這個(gè)數(shù)組由三個(gè)結(jié)構(gòu)變量組成盛杰。第一種用于TCP數(shù)據(jù)流協(xié)議挽荡,標(biāo)識(shí)碼為IPPROTO_TCP,第二種用于UDP數(shù)據(jù)包協(xié)議即供,協(xié)議標(biāo)識(shí)碼為IPPROTO_UDP定拟,第三種為原始套接字使用,可以是用戶自己的協(xié)議逗嫡,標(biāo)識(shí)碼IPPROTO_IP是虛擬IP協(xié)議類型青自。這個(gè)數(shù)組是在inet_init()中由inet_register_protosw來(lái)注冊(cè)的,該函數(shù)的主要步驟為1.檢查參數(shù)結(jié)構(gòu)體inet_protosw是否越界2.通過(guò)type查找匹配的隊(duì)列3.插入到隊(duì)列中驱证。

然后看list_for_each_rcu延窜,這個(gè)函數(shù)在數(shù)組inetsw[]中根據(jù)sock->type(一般程序指定為SOCK_STREAM即TCP協(xié)議類型)找到協(xié)議類型所在隊(duì)列并使得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的代碼負(fù)責(zé)選擇出合適的協(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ù)量動(dòng)態(tài)的安裝本次請(qǐng)求需要的協(xié)議類型的結(jié)構(gòu)吻育,但看到了unlikely念秧,再分析前面的代碼發(fā)現(xiàn)err很大可能(最起碼在我們一般性的TCP/IP編程中)此時(shí)還是等于0的,即是說(shuō)扫沼,由于TCP協(xié)議的結(jié)構(gòu)已經(jīng)安裝出爹,不必再安裝,直接進(jìn)入下一步*/

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;

/*對(duì)socket的操作集合進(jìn)行了掛鉤,指向了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)式一個(gè)struct proto結(jié)構(gòu),負(fù)責(zé)傳輸層使用铸董。*/

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分配一個(gè)struct sock祟印,該結(jié)構(gòu)體龐大,其作用是網(wǎng)絡(luò)層對(duì)socket的表示粟害,意思就是IP協(xié)議下有很多東西比如IP地址蕴忆,網(wǎng)卡接口,端口等等信息需要再socket層中有所體現(xiàn)從而使編程者方便使用悲幅,然后就利用指針等形式把內(nèi)容進(jìn)行一定程度上的映射套鹅。sk_alloc首先對(duì)sock->proto和sock_creator進(jìn)行設(shè)置,設(shè)置成當(dāng)前協(xié)議對(duì)應(yīng)的proto調(diào)用sk_prot_alloc()根據(jù)是否提供了slab緩存而判斷是使用slab緩存還是通用緩存汰具。只要分配成功卓鹿,則調(diào)用sock_lock_init()對(duì)緩存進(jìn)行初始化,主要是對(duì)sock鎖留荔、等待隊(duì)列以及進(jìn)程數(shù)據(jù)結(jié)構(gòu)中的網(wǎng)絡(luò)空間結(jié)構(gòu)進(jìn)行分配吟孙。初始化完了后調(diào)用sock_net_set()函數(shù)對(duì)網(wǎng)絡(luò)空間結(jié)構(gòu)進(jìn)行記錄,然后最后增加一個(gè)net計(jì)數(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;

/*可復(fù)用性檢查,不懂*/

357 sk->sk_no_check = answer_no_check;

358 if (INET_PROTOSW_REUSE & answer_flags)

359 sk->sk_reuse = SK_CAN_REUSE;

/*返回一個(gè)struct inet_sock的指針給inet*/

361 inet = inet_sk(sk);

/*判斷是不是面向連通(目前只有SOCK_STREAM是面向連通的既荚,不可以理解成TCP稚失,因?yàn)镮CMP也是面向連通的)*/

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;

/*進(jìn)一步初始化sk結(jié)構(gòu)(struct sock),此處分析一下sock_init_data:初始化接收(讀)隊(duì)列,初始化寫(xiě)隊(duì)列吸占,初始化錯(cuò)誤信息隊(duì)列晴叨,注意這三個(gè)隊(duì)列都是雙向鏈表,屬于sk_buff_head結(jié)構(gòu)體矾屯,其中會(huì)把sk_buff結(jié)構(gòu)體串聯(lián)起來(lái)兼蕊。初始化數(shù)據(jù)包發(fā)送定時(shí)器,變量(主要是函數(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會(huì)發(fā)現(xiàn)件蚕,這里的sk_prot被設(shè)置為answer_prot(即answer->prot)孙技,而answer_prot又被設(shè)置成了tcp_prot,所以此處調(diào)用tcp_prot中的init排作,查閱tcp_prot即可得到init這個(gè)函數(shù)指針指向的是tcp_v4_init_sock()牵啦,接下來(lái)分析tcp_v4_init_sock(見(jiàn)源代碼后面)*/

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,這個(gè)函數(shù)負(fù)責(zé)初始化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 }

這里涉及兩個(gè)新的數(shù)據(jù)結(jié)構(gòu)tcp_sock與inet_connection_sock,其實(shí)還有一個(gè)數(shù)據(jù)結(jié)構(gòu)inet_sock妄痪。之前提過(guò)struct socket是面向用戶態(tài)的哈雏,而struct sock是面向內(nèi)核驅(qū)動(dòng)的,但socket與內(nèi)核協(xié)議棧的交互實(shí)際上是通過(guò)xxx_sock這個(gè)數(shù)據(jù)機(jī)構(gòu),此處是TCP協(xié)議裳瘪,自然是tcp_sock 數(shù)據(jù)結(jié)構(gòu)土浸,保存所有tcp相關(guān)信息睛琳。而inet_connection_sock實(shí)際上是inet_sock的擴(kuò)展组民,之前說(shuō)過(guò)inet_sock是INET域在socket的表現(xiàn),而inet_connection_sock就是在inet_sock的基礎(chǔ)上保存連接信息钓觉。

tcp_v4_init_sock中的tcp_init_sock(sk)是具體的初始化舉措派殷,尤其重要的是對(duì)SIN和AFK這兩個(gè)SYN幀的初始化毅舆。

至此inet_create函數(shù)分析完畢,同時(shí)sock_create函數(shù)也分析完畢愈腾,接下來(lái)分析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是完全當(dāng)成文件在操作憋活,但之前只分配了相應(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會(huì)調(diào)用__alloc_fd()橱乱,該函數(shù)負(fù)責(zé)分配一個(gè)文件描述符并設(shè)置成busy狀態(tài)辜梳。然后執(zhí)行sock_alloc_file,該函數(shù)負(fù)責(zé)得到第一個(gè)沒(méi)被使用的文件泳叠,并對(duì)其path作瞄,狀態(tài)等等進(jìn)行設(shè)置,然后再把文件操作表socket_file_ops裝載好危纫。然后執(zhí)行fd_install(fd, newfile)宗挥,把文件描述符和文件進(jìn)行關(guān)聯(lián)。最后執(zhí)行put_unused_fd种蝶。

至此整個(gè)socket的創(chuàng)建完成契耿。

總結(jié)一下這次分析的過(guò)程。對(duì)分析源碼的步驟有了新的想法螃征,首先應(yīng)該想到要完成這一步從理論上需要完成哪些東西搪桂,然后再遞歸地去分析這些東西又需要完成那些步驟。然后根據(jù)每個(gè)步驟去看由哪些代碼執(zhí)行盯滚,比如在socket的創(chuàng)建中踢械,我就應(yīng)該從文件系統(tǒng)初始化,通用socket初始化魄藕,TCP初始化内列,INET初始化,sock初始化泼疑,協(xié)議對(duì)應(yīng)等等部分來(lái)分析德绿,而不是盲目地一步一步地去看荷荤。

其次,不能糾纏于細(xì)節(jié)移稳,個(gè)別有重大意義的細(xì)節(jié)可以仔細(xì)分析蕴纳,但更多的時(shí)候應(yīng)該一塊一塊地去看代碼看這些代碼完成了什么功能,而不是糾結(jié)于每一句想完成什么个粱。

最后也是最重要的古毛,在分析源碼之前需要先分析整個(gè)過(guò)程涉及的數(shù)據(jù)結(jié)構(gòu)以及他們存在的目的,他們之間的關(guān)系都许,只有在數(shù)據(jù)結(jié)構(gòu)清晰明了了之后才可能對(duì)過(guò)程有較為清晰容易地理解稻薇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市胶征,隨后出現(xiàn)的幾起案子塞椎,更是在濱河造成了極大的恐慌,老刑警劉巖睛低,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件案狠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡钱雷,警方通過(guò)查閱死者的電腦和手機(jī)骂铁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)罩抗,“玉大人拉庵,你說(shuō)我怎么就攤上這事√椎伲” “怎么了钞支?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)泣懊。 經(jīng)常有香客問(wèn)我伸辟,道長(zhǎng),這世上最難降的妖魔是什么馍刮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮窃蹋,結(jié)果婚禮上卡啰,老公的妹妹穿的比我還像新娘。我一直安慰自己警没,他們只是感情好匈辱,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著杀迹,像睡著了一般亡脸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天浅碾,我揣著相機(jī)與錄音大州,去河邊找鬼。 笑死垂谢,一個(gè)胖子當(dāng)著我的面吹牛厦画,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播滥朱,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼根暑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了徙邻?” 一聲冷哼從身側(cè)響起排嫌,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缰犁,沒(méi)想到半個(gè)月后躏率,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡民鼓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年薇芝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丰嘉。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡夯到,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饮亏,到底是詐尸還是另有隱情耍贾,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布路幸,位于F島的核電站荐开,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏简肴。R本人自食惡果不足惜晃听,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望砰识。 院中可真熱鬧能扒,春花似錦、人聲如沸辫狼。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)膨处。三九已至见秤,卻和暖如春砂竖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鹃答。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工乎澄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挣跋。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓三圆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親避咆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舟肉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容