📄 linux-tcp ip源码阅读笔记.htm
字号:
<DIV>/*yfhuang ipsec*/<BR>#ifdef
CONFIG_IPSEC <BR> pfkey_init();<BR>#endif<BR>/*yfhuang
ipsec*/<BR>}</DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV><STRONG> 2.2
do_initcalls()</STRONG> 中做了其它的初始化,其中包括</DIV>
<DIV> </DIV>
<DIV> 协议初始化,路由初始化,网络接口设备初始化</DIV>
<DIV> </DIV>
<DIV><STRONG>(例如inet_init函数以_init开头表示是系统初始化时做,函数结束后跟module_init(inet_init),这是一个宏,在include/linux/init.c中定义,展开为_initcall(inet_init),表示这个函数在do_initcalls被调用了)</STRONG></DIV>
<DIV> </DIV>
<DIV><STRONG> 2.3 协议初始化</STRONG></DIV>
<DIV><STRONG>此处主要列举inet协议的初始化过程。</STRONG></DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV>static int __init inet_init(void)<BR>{<BR> struct sk_buff
*dummy_skb;<BR> struct inet_protocol *p;<BR> struct inet_protosw
*q;<BR> struct list_head *r;</DIV>
<DIV> </DIV>
<DIV> printk(KERN_INFO "NET4: Linux TCP/IP 1.0 for NET4.0\n");</DIV>
<DIV> </DIV>
<DIV> if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb))
{<BR> printk(KERN_CRIT "inet_proto_init:
panic\n");<BR> return -EINVAL;<BR> }</DIV>
<DIV> </DIV>
<DIV> /*<BR> * Tell SOCKET that we are
alive... 注册socket,告诉socket inet类型的地址族已经准备好了<BR> */<BR>
<BR> (void) sock_register(&inet_family_ops);</DIV>
<DIV> </DIV>
<DIV> /*<BR> * Add all the protocols.
包括arp,ip、ICMP、UPD、tcp_v4、tcp、igmp的初始化,主要初始化各种协议对应的inode和socket变量。</DIV>
<DIV> </DIV>
<DIV>其中arp_init完成系统中路由部分neighbour表的初始化</DIV>
<DIV> </DIV>
<DIV>ip_init完成ip协议的初始化。在这两个函数中,都通过定义一个packet_type结构的变量将这种数据包对应的协议发送数据、允许发送设备都做初始化。</DIV>
<DIV><BR> */</DIV>
<DIV> </DIV>
<DIV> printk(KERN_INFO "IP Protocols: ");<BR> for (p =
inet_protocol_base; p != NULL;) {<BR> struct inet_protocol *tmp =
(struct inet_protocol *)
p->next;<BR> inet_add_protocol(p);<BR> printk("%s%s",p->name,tmp?",
":"\n");<BR> p = tmp;<BR> }</DIV>
<DIV> </DIV>
<DIV> /* Register the socket-side information for inet_create.
*/<BR> for(r = &inetsw[0]; r < &inetsw[SOCK_MAX];
++r)<BR> INIT_LIST_HEAD(r);</DIV>
<DIV> </DIV>
<DIV> for(q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN];
++q)<BR> inet_register_protosw(q);</DIV>
<DIV> </DIV>
<DIV> /*<BR> * Set the ARP module up <BR> */</DIV>
<DIV> </DIV>
<DIV> arp_init();</DIV>
<DIV> </DIV>
<DIV> /*<BR> * Set the IP module up<BR>
*/</DIV>
<DIV> </DIV>
<DIV> ip_init();</DIV>
<DIV> </DIV>
<DIV> tcp_v4_init(&inet_family_ops);</DIV>
<DIV> </DIV>
<DIV> /* Setup TCP slab cache for open requests.
*/<BR> tcp_init();</DIV>
<DIV><BR> /*<BR> * Set the ICMP layer up<BR> */</DIV>
<DIV> </DIV>
<DIV> icmp_init(&inet_family_ops);</DIV>
<DIV> </DIV>
<DIV> /* I wish inet_add_protocol had no constructor
hook...<BR> I had to move IPIP from net/ipv4/protocol.c :-(
--ANK<BR> */<BR>#ifdef
CONFIG_NET_IPIP<BR> ipip_init();<BR>#endif<BR>#ifdef
CONFIG_NET_IPGRE<BR> ipgre_init();<BR>#endif</DIV>
<DIV> </DIV>
<DIV> /*<BR> * Initialise the multicast router<BR>
*/<BR>#if defined(CONFIG_IP_MROUTE)<BR> ip_mr_init();<BR>#endif</DIV>
<DIV> </DIV>
<DIV> /*<BR> * Create all the /proc entries.<BR>
*/<BR>#ifdef CONFIG_PROC_FS<BR> proc_net_create ("raw", 0,
raw_get_info);<BR> proc_net_create ("netstat", 0,
netstat_get_info);<BR> proc_net_create ("snmp", 0,
snmp_get_info);<BR> proc_net_create ("sockstat", 0,
afinet_get_info);<BR> proc_net_create ("tcp", 0,
tcp_get_info);<BR> proc_net_create ("udp", 0,
udp_get_info);<BR>#endif /* CONFIG_PROC_FS */</DIV>
<DIV> </DIV>
<DIV> ipfrag_init();</DIV>
<DIV> </DIV>
<DIV> return 0;<BR>} </DIV>
<DIV>module_init(inet_init);<BR></DIV>
<DIV> </DIV>
<DIV> </DIV>
<DIV> <STRONG> 2.4
路由初始化(包括neighbour表、FIB表、和路由缓存表的初始化工作)</STRONG></DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV> <STRONG>2.4.1
rtcache表 ip_rt_init()函数
在net/ipv4/ip_output中调用,net/ipv4/route.c中定义</STRONG></DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV> <STRONG>2.4.2
FIB初始化 在ip_rt_init()中调用 在net/ipv4/fib_front.c中定义</STRONG></DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV><STRONG> 2.4.3
neigbour表初始化 arp_init()函数中定义 </STRONG></DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV><STRONG> 2.5 网络接口设备初始化</STRONG></DIV>
<DIV> </DIV>
<DIV> 在系统中网络接口都是由一个dev_base链表进行管理的。通过内核的启动方式也是通过这个链表进行操作的。在系统启动之初,将所有内核能够支持的网络接口都初始化成这个链表中的一个节点,并且每个节点都需要初始化出init函数指针,用来检测网络接口设备。然后,系统遍历整个dev_base链表,对每个节点分别调用init函数指针,如果成功,证明网络接口设备可用,那么这个节点就可以进一步初始化,如果返回失败,那么证明该网络设备不存在或是不可用,只能将该节点删除。启动结束之后,在dev_base中剩下的都是可以用的网络接口设备。</DIV>
<DIV> </DIV>
<DIV> 2.5.1
do_initcalls---->net_dev_init()(net/core/dev.c)------>ethif_probe()(drivers/net/Space.c,在netdevice{}结构的init中调用,这边ethif_probe是以太网卡针对的调用)</DIV>
<DIV> </DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV><STRONG>3.网络设备驱动程序(略)</STRONG></DIV>
<DIV><STRONG> </STRONG></DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV><STRONG>4.网络连接</STRONG></DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV><STRONG> 4.1 连接的建立和关闭</STRONG></DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV> tcp连接建立的代码如下:</DIV>
<DIV> server=gethostbyname(SERVER_NAME);</DIV>
<DIV> sockfd=socket(AF_INET,SOCK_STREAM,0);</DIV>
<DIV> address.sin_family=AF_INET;</DIV>
<DIV> address.sin_port=htons(PORT_NUM);</DIV>
<DIV> memcpy(&address.sin_addr,server->h_addr,server->h_length);</DIV>
<DIV> connect(sockfd,&address,sizeof(address));</DIV>
<DIV> </DIV>
<DIV> 连接的初始化与建立期间主要发生的事情如下:</DIV>
<DIV> </DIV>
<DIV> 1)sys_socket调用:调用socket_creat(),创建出一个满足传入参数family、type、和protocol的socket,调用sock_map_fd()获取一个未被使用的文件描述符,并且申请并初始化对应的file{}结构。</DIV>
<DIV> </DIV>
<DIV> 2)sock_creat():创建socket结构,针对每种不同的family的socket结构的初始化,就需要调用不同的create函数来完成。对应于inet类型的地址来说,在网络协议初始化时调用sock_register()函数中完成注册的定义如下:</DIV>
<DIV> struct net_proto_family
inet_family_ops={</DIV>
<DIV> PF_INET;</DIV>
<DIV> inet_create</DIV>
<DIV> };所以inet协议最后会调用inet_create函数。</DIV>
<DIV> </DIV>
<DIV> 3)inet_create:
初始化sock的状态设置为SS_UNCONNECTED,申请一个新的sock结构,并且初始化socket的成员ops初始化为inet_stream_ops,而sock的成员prot初始化为tcp_prot。然后调用sock_init_data,将该socket结构的变量sock和sock类型的变量关联起来。</DIV>
<DIV> </DIV>
<DIV> 4)在系统初始化完毕后便是进行connect的工作,系统调用connect将一个和socket结构关联的文件描述符和一个sockaddr{}结构的地址对应的远程机器相关联,并且调用各个协议自己对应的connect连接函数。对应于tcp类型,则sock->ops->connect便为inet_stream_connect。</DIV>
<DIV> </DIV>
<DIV> </DIV>
<DIV> 5)inet_stream_connect:
得到sk,sk=sock->sk,锁定sk,对自动获取sk的端口号存放在sk->num中,并且用htons()函数转换存放在sk->sport中。然后调用sk->prot->connect()函数指针,对tcp协议来说就是tcp_v4_connect()函数。然后将sock->state状态字设置为SS_CONNECTING,等待后面一系列的处理完成之后,就将状态改成SS_CONNECTTED。</DIV>
<DIV> </DIV>
<DIV> 6)
tcp_v4_connect():调用函数ip_route_connect(),寻找合适的路由存放在rt中。ip_route_connect找两次,第一次找到下一跳的ip地址,在路由缓存或fib中找到,然后第二次找到下一跳的具体邻居,到neigh_table中找到。然后申请出tcp头的空间存放在buff中。将sk中相关地址数据做一些针对路由的变动,并且初始化一个tcp连接的序列号,调用函数tcp_connect(),初始化tcp头,并设置tcp处理需要的定时器。一次connect()建立的过程就结束了。</DIV>
<DIV> </DIV>
<DIV> 连接的关闭主要如下:</DIV>
<DIV> </DIV>
<DIV> 1)close:
一个socket文件描述符对应的file{}结构中,有一个file_operations{}结构的成员f_ops,它的初始化关闭函数为sock_close函数。</DIV>
<DIV> </DIV>
<DIV> 2)sock_close:调用函数sock_release(),参数为一个socket{}结构的指针。</DIV>
<DIV> </DIV>
<DIV> 3)sock_release:调用inet_release,并释放socket的指针和文件空间</DIV>
<DIV> </DIV>
<DIV> 4)inet_release:
调用和该socket对应协议的关闭函数inet_release,如果是tcp协议,那么调用的是tcp_close;最后释放sk。</DIV>
<DIV> </DIV>
<DIV> 4.2
<STRONG>数据发送流程图</STRONG></DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV> <IMG alt="" src="linux-Tcp IP源码阅读笔记.files/send.gif" useMap=#mymap
border=0></DIV>
<DIV><STRONG></STRONG> </DIV>
<DIV>各层主要函数以及位置功能说明:</DIV>
<DIV> 1)sock_write:初始化msghdr{}结构
net/socket.c</DIV>
<DIV> 2)sock_sendmsg:net/socket.c</DIV>
<DIV> 3)inet_sendmsg:net/ipv4/af_net.c</DIV>
<DIV> 4)tcp_sendmsg:申请sk_buff{}结构的空间,把msghdr{}结构中的数据填入sk_buff空间。net/ipv4/tcp.c</DIV>
<DIV> 5)tcp_send_skb:net/ipv4/tcp_output.c</DIV>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -