Socket创建
内核参照2.6.34 (部分2.6.21)
函数调用
sys_socketall() => sys_socket() =>sock_create() => __sock_create()
概要流程
1. 分配socket结构空间
2. 记录socket类型
3. 检查、取得协议族操作表
注:
Kernel 通过initcall将inet_family注册到net_families,如下:
fs_initcall(inet_init); => (void)sock_register(&inet_family_ops);
在inet中,会在inet_init()注册不同的sockettype: SOCK_STREAM, SOCK_DGRAM, SOCK_RAW. (这里注册的是不同类型的socket对应的操作所需要的处理函数.也就是各中socket的bind(), listen(), accept()等操作的处理函数.)
4. 执行协议族的创建函数
5. 利用协议族的函数表初始化socket
监听连接listen
调用协议族相应的处理函数,inet则调用inet_listen();
接收连接accept
调用协议族相应的处理函数,inet则调用inet_accept();
准备连接请求connect
1. 内核的连接
sys_socketcall() => sys_connect() =>inet_stream_connect() => tcp_v4_connect()
1) 端口检查与复用
2) MTU,MSS的设置
3) 滑动窗口的初始化
4) …
Tcp_connect() 发送第一次握手的SYN数据包
2. 分配数据包结构和数据空间
tcp_v4_connect() => tcp_connect() => alloc_skb_fclone() =>__alloc_skb()
3. 构建、发送TCP数据包
Tcp_connect() => tcp_transmit_skb()
4. 进化成IP数据包
Tcp_connect() => tcp_transmit_skb() => ip_queue_xmit()
Tcp_connect() => tcp_transmit_skb() => ip_queue_xmit() =>ip_local_out => dst_output() => ip_output() => ip_finish_output()=> ip_finish_output2() => neigh_resolve_output()
5. 进化成以太网数据包
neigh_resolve_output() => neigh_hh_init()
6. 发送以太网数据包
neith->ops->queue_xmit();
arp_hh_ops中queue_xmit() 对应dev_queue_xmit();
neigh_resolve_output() => dev_queue_xmit();
建立连接的过程
接收数据包
注册网卡设备驱动函数
以cs89x0为例, 网卡设备驱动函数调用如下:
init_module() => cs89x0_probe1() => dev->netdev_ops= &net_ops; => register_netdev(dev);
1. 当使用ifconfig eth0 up启动网卡时会执行系统调用sys_ioctl(), 最终内核会执行dev_open();
dev_open() =>__dev_open();
如下,__dev_open()中最终会调用net_ops中的net_open();
2. 在net_open()中会注册中断函数net_ineterrupt(), 如下:
3. 中断到来时会调用net_interrupt(), 如果是由于接收数据包产生的中断, 则调用net_rx();
4. 在net_rx()中
a) 分配数据包结构和缓冲块
b) 将数据读入到数据包的数据块中,并调用skb_put调整数据块
c) 记录协议
d) 调用netif_rx()通知内核接收数据包
5. netif_rx()处理数据包, 使用NAPI,采用轮询的方式获取数据包,改善网络处理的性能.
这里的backlog在net_dev_init()中初始化, 如下:
l 初始化接收数据包队列和NAPI队列
l 设置数据包处理函数process_backlog();
l 注册发送数据包的软中断函数net_tx_action();
l 注册接收数据包的软中断函数net_rx_action();
6. 在net_rx_action()中, 如下, poll()会调用上面注册的函数process_backlog()处理数据包.
do_softirq() => net_rx_action() => process_backlog()
7. 在process_backlog()中调用netif_receive_skb()向上层传递数据包.
8. 在netif_receive_skb()中查看是否是bridge, vlan, 是则进入相应的处理, 不是则根据数据包的协议类型, 调用deliver_skb()传递数据包.
此处的pt_prev->func() 由如下的实现在inet_init()中注册.
对于ip 数据包, packet_type在inet_init()函数中注册, 如下:
IP处理数据包
1. 调用ip_rcv(), 进入IP数据包的处理流程, 同时进入netfilter系统.
a) Ip_rcv()首先解析IP数据包,如包头, checksum等;
b) 调用NF_HOOK, 进入NF_INET_PRE_ROUTING处理, 处理结束后调用ip_rcv_finish();
c) ip_rcv_finish()主要处理两件事情:
i. 明确数据包是本地接收还是转发, 如果是转发就需要进一步明确转发的网络设备和跳转出口.
ii. 分析和处理关于IP选项的内容
d) 接下来通过dst_input()调用路由表的输入处理函数:
i. 如果是转发的数据包, 转发路由表指定的输入处理函数为ip_forward(), 这个函数最终调用dst_output函数转发数据包.
Ip_rcv_finish()=> ip_route_input() => ip_route_input_slow() => ip_mkroute_input()=> __mkroute_input()
i. Ip_forward() 进入netfilter的后续处理
ip_finish_output()=> ip_finish_output2() => net_ratelimit();
ii. 如果是本地的数据包, 本地路由表指定的输入处理函数为ip_local_deliver()这个函数继续处理数据包.
Ip_rcv_finish() => ip_route_input() => ip_route_input_slow()
i. 对数据包IP头部进行检查, 查看是否是分段数据包, 如果是则调用ip_defrage()将分段数据包插入分段队列; 如果是接收的最后一个分段数据包, 则重组数据包.
ii. 无论是重组后的数据包还是直接接收的数据包都要进入ip_local_deliver_finish()函数交给传输层处理.
TCP数据包处理及三次握手
1. 根据IP头部获取当前数据包的协议类型;
2. 传递给raw socket处理
3. 获取传输层函数表的数据结构, 调用传输层函数表的处理函数
a) 传输层函数表是由inet_init()注册. (此处内核注册了对不同协议类型的数据包的处理)
b) 对于tcp, 即调用tcp_v4_rcv();
4. Tcp_v4_rcv()及三次握手
发送与接收数据包
发送和接收数据包的函数有: 文件系统APIs: read(), write(), SOCKET APIs: send(), recv(), sendto(),recvfrom(), sendmsg(), recvmsg(). 经分析, 无论是文件系统APIs, 还是socket APIs最终都会调用sock_sendmsg()/__sock_sendmsg(),sock_recvmsg()/__sock_recvmsg().
针对不同类型的数据类型的socket, 内核注册了不同的针对socket操作的处理函数, 见概要流程 #3.
发送数据包
接收数据包
![](https://i-blog.csdnimg.cn/blog_migrate/a6b205c9221b1d196e4c3ba686df70b0.png)