共计 13186 个字符,预计需要花费 33 分钟才能阅读完成。
一.linux 内核网络栈代码的筹备常识
1. linux 内核 ipv4 网络局部分层构造:
BSD socket 层: 这一部分解决 BSD socket 相干操作,每个 socket 在内核中以 struct socket 构造体现。这一部分的文件
次要有:/net/socket.c /net/protocols.c etc INET socket 层:BSD socket 是个能够用于各种网络协议的接口,而当用于 tcp/ip,即建设了 AF_INET 模式的 socket 时,
还须要保留些额定的参数,于是就有了 struct sock 构造。文件次要
有:/net/ipv4/protocol.c /net/ipv4/af_inet.c /net/core/sock.c etc TCP/UDP 层:解决传输层的操作,传输层用 struct inet_protocol 和 struct proto 两个构造示意。文件次要
有:/net/ipv4/udp.c /net/ipv4/datagram.c /net/ipv4/tcp.c /net/ipv4/tcp_input.c /net/ipv4//tcp_output.c /net/ipv4/tcp_minisocks.c /net/ipv4/tcp_output.c /net/ipv4/tcp_timer.c
etc IP 层:解决网络层的操作,网络层用 struct packet_type 构造示意。文件次要有:/net/ipv4/ip_forward.c
ip_fragment.c ip_input.c ip_output.c etc. 数据链路层和驱动程序:每个网络设备以 struct net_device 示意,通用的解决在 dev.c 中,驱动程序都在 /driver/net 目
录下。
2. 两台主机建设 udp 通信所走过的函数列表
^
| sys_read fs/read_write.c
| sock_read net/socket.c
| sock_recvmsg net/socket.c
| inet_recvmsg net/ipv4/af_inet.c
| udp_recvmsg net/ipv4/udp.c
skb_recv_datagram net/core/datagram.c |
---|
sock_queue_rcv_skb include/net/sock.h |
udp_queue_rcv_skb net/ipv4/udp.c |
udp_rcv net/ipv4/udp.c |
ip_local_deliver_finish net/ipv4/ip_input.c |
ip_local_deliver net/ipv4/ip_input.c |
ip_recv net/ipv4/ip_input.c |
net_rx_action net/dev.c |
netif_rx net/dev.c |
el3_rx driver/net/3c309.c |
el3_interrupt driver/net/3c309.c |
==========================
| sys_write fs/read_write.c
| sock_writev net/socket.c
| sock_sendmsg net/socket.c
| inet_sendmsg net/ipv4/af_inet.c
| udp_sendmsg net/ipv4/udp.c
| ip_build_xmit net/ipv4/ip_output.c
| output_maybe_reroute net/ipv4/ip_output.c
| ip_output net/ipv4/ip_output.c
| ip_finish_output net/ipv4/ip_output.c
dev_queue_xmit net/dev.c |
---|
el3_start_xmit driver/net/3c309.c |
V
须要 C /C++ Linux 服务器架构师学习材料加群 563998835(材料包含 C /C++,Linux,golang 技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg 等),收费分享
二.linux 的 tcp-ip 栈代码的详细分析
1. 数据结构(msghdr,sk_buff,socket,sock,proto_ops,proto)
bsd 套接字层, 操作的对象是 socket, 数据寄存在 msghdr 这样的数据结构:
创立 socket 须要传递 family,type,protocol 三个参数,创立 socket 其实就是创立一个 socket 实例,而后创立一个文件描述符构造,并且相互建设一些关联,即建设相互连贯的指针,并且初始化这些对文件的写读操作映射到 socket 的 read,write 函数上来。
同时初始化 socket 的操作函数(proto_ops 构造), 如果传入的 type 参数是 STREAM 类型,那么就初始化为 SOCKET->ops 为 inet_stream_ops,如果是 DGRAM 类型,则 SOCKET-ops 为 inet_dgram_ops。对于 inet_stream_ops 其实是一个构造体,蕴含了 stream 类型的 socket 操作的一些入口函数,在这些函数里次要做的是对 socket 进行相干的操作,同时通过调用上面提到的 sock 中的相干操作实现 socket 到 sock 层的传递。比方在 inet_stream_ops 里有个 inet_release 的操作,这个操作除了开释 socket 的类型空间操作外,还通过调用 socket 连贯的 sock 的 close 操作,对于 stream 类型来说,即 tcp_close 来敞开 sock
开释 sock。
创立 socket 同时还创立 sock 数据空间,初始化 sock, 初始化过程次要做的事件是初始化三个队列,receive_queue(接管到的数据包 sk_buff 链表队列),send_queue(须要发送数据包的 sk_buff 链表队列),backlog_queue(次要用于 tcp 中三次握手胜利的那些数据包, 本人猜的), 依据 family、type 参数,初始化 sock 的操作,比方对于 family 为 inet 类型的,type 为 stream 类型的,sock->proto 初始化为 tcp_prot. 其中包含 stream 类型的协定 sock 操作对应的入口函数。
在一端对 socket 进行 write 的过程中,首先会把要 write 的字符串缓冲区整顿成 msghdr 的数据结构模式(参见 linux 内核 2.4 版源代码剖析大全), 而后调用 sock_sendmsg 把 msghdr 的数据传送至 inet 层,对于 msghdr 构造中数据区中的每个数据包,创立 sk_buff 构造,填充数据,挂至发送队列。一层层往上层协定传递。一下每层协定不再对数据进行拷贝。而是对 sk_buff 构造进行操作。
inet 套接字及以上层 数据寄存在 sk_buff 这样的数据结构里:
路由:
在 linux 的路由零碎次要保留了三种与路由相干的数据,第一种是在物理上和本机相连接的主机地址信息表,第二种是保留了在网络拜访中判断一个网络地址应该走什么路由的数据表;第三种是最新应用过的查问路由地址的缓存地址数据表。
1.neighbour 构造 neighbour_table{}是一个蕴含和本机所连贯的所有邻元素的信息的数据结构。该构造中有个元素是 neighbour 构造的数组,数组的每一个元素都是一个对应于邻机的 neighbour 构造,零碎中因为协定的不同,会有不同的判断街坊的形式,每种都有 neighbour_table{}类型的实例,这些实例是通过 neighbour_table{}中的指针 next 串联起来的。在 neighbour 构造中,蕴含有与该街坊相连的网络接口设施 net_device 的指针,网络接口的硬件地址,街坊的硬件地址,蕴含有 neigh_ops{}指针,这些函数指针是间接用来连贯传输数据的,蕴含有 queue_xmit(struct * sk_buff)函数入口地址,这个函数可能会调用硬件驱动程序的发送函数。
2.FIB 构造 在 FIB 中保留的是最重要的路由规定, 通过对 FIB 数据的查找和换算,肯定可能取得路由一个地址的办法。零碎中路由个别采取的伎俩是:先到路由缓存中查找表项,如果可能找到,间接对应的一项作为路由的规定;如果不能找到,那么就到 FIB 中依据规定换算传算进去,并且减少一项新的,在路由缓存中将我的项目增加进去。
3.route 构造(即路由缓存中的构造)
数据链路层:
net_device{}构造,对应于每一个网络接口设施。这个构造中蕴含很多能够间接获取网卡信息的函数和变量,同时蕴含很多对于网卡操作的函数,这些间接指向该网卡驱动程序的许多函数入口,包含发送接收数据帧到缓冲区等。当这些实现后,比方数据接管到缓冲区后便由 netif_rx(在 net/core/dev.c 各种设施驱动程序的下层框架程序)把它们组成 sk_buff 模式挂到零碎接管的 backlog 队列而后交由下层网络协议解决。同样,对于下层协定解决下来的那些 sk_buff。便由 dev_queue_xmit 函数放入网络缓冲区,交给网卡驱动程序的发送程序处理。
在零碎中存在一张链表 dev_base 将零碎中所有的 net_device{}构造连在一起。对应于内核初始化而言,系统启动时便为每个所有可能反对的网络接口设施申请了一个 net_device{}空间并串连起来,而后对每个接点运行检测过程,如果检测胜利,则在 dev_base 链表中保留这个接点,否则删除。对应于模块加载来说,则是调用 register_netdev()注册 net_device, 在这个函数中运行检测过程,如果胜利,则加到 dev_base 链表。否则就返回检测不到信息。删除同理,调用
unregister_netdev。
2. 启动剖析
2.1 初始化过程:start-kernel(main.c)—->do_basic_setup(main.c)—->sock_init(/net/socket.c)—->do_initcalls(main.c)
void __init sock_init(void) {
int i;
printk(KERN_INFO "Linux NET4.0 for Linux 2.4/n");
printk(KERN_INFO "Based upon Swansea University Computer Society NET3.039/n");
/*
* Initialize all address (protocol) families. 每一项示意的是针对一个地址族的操作汇合,例如对于 ipv4 来说,在 net/ipv4/af_inet.c 文件中的函数 inet_proto_init()就调用 sock_register()函数将 inet_families_ops 初始化到属于 IPV4 的 net_families 数组中的一项。*/
for (i = 0; i < NPROTO; i++)
net_families[i] = NULL;
/*
* Initialize sock SLAB cache. 初始化对于 sock 构造预留的内存的 slab 缓存。*/
sk_init();
#ifdef SLAB_SKB
/*
* Initialize skbuff SLAB cache 初始化对于 skbuff 构造的 slab 缓存。当前对于 skbuff 的申请能够通过函数 kmem_cache_alloc()在这个缓存中申请空间。*/
skb_init();
#endif
/*
* Wan router layer.
*/
#ifdef CONFIG_WAN_ROUTER
wanrouter_init();
#endif
/*
* Initialize the protocols module. 向零碎注销 sock 文件系统,并且将其装置到零碎上来。*/
register_filesystem(&sock_fs_type);
sock_mnt = kern_mount(&sock_fs_type);
/* The real protocol initialization is performed when
* do_initcalls is run.
*/
/*
* The netlink device handler may be needed early.
*/
#ifdef CONFIG_NET
rtnetlink_init();
#endif
#ifdef CONFIG_NETLINK_DEV
init_netlink();
#endif
#ifdef CONFIG_NETFILTER
netfilter_init();
#endif
#ifdef CONFIG_BLUEZ
bluez_init();
#endif
/*yfhuang ipsec*/
#ifdef CONFIG_IPSEC
pfkey_init();
#endif
/*yfhuang ipsec*/
}
2.2 do_initcalls() 中做了其它的初始化,其中包含
协定初始化,路由初始化,网络接口设施初始化
(例如 inet_init 函数以_init 结尾示意是零碎初始化时做,函数完结后跟 module_init(inet_init), 这是一个宏,在 include/linux/init.c 中定义,开展为_initcall(inet_init), 示意这个函数在 do_initcalls 被调用了)
2.3 协定初始化
此处次要列举 inet 协定的初始化过程。
static int __init inet_init(void) {
struct sk_buff *dummy_skb;
struct inet_protocol *p;
struct inet_protosw *q;
struct list_head *r;
printk(KERN_INFO "NET4: Linux TCP/IP 1.0 for NET4.0/n");
if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb)) {printk(KERN_CRIT "inet_proto_init: panic/n");
return -EINVAL;
}
/*
* Tell SOCKET that we are alive... 注册 socket,通知 socket inet 类型的地址族曾经筹备好了
*/
(void) sock_register(&inet_family_ops);
/*
* Add all the protocols. 包含 arp,ip、ICMP、UPD、tcp_v4、tcp、igmp 的初始化,次要初始化各种协定对应的 inode 和 socket 变量。其中 arp_init 实现零碎中路由局部 neighbour 表的初始化
ip_init 实现 ip 协定的初始化。在这两个函数中,都通过定义一个 packet_type 构造的变量将这种数据包对应的协定发送数据、容许发送设施都做初始化。*/
printk(KERN_INFO "IP Protocols:");
for (p = inet_protocol_base; p != NULL;) {struct inet_protocol *tmp = (struct inet_protocol *) p->next;
inet_add_protocol(p);
printk("%s%s",p->name,tmp?",":"/n");
p = tmp;
}
/* Register the socket-side information for inet_create. */
for(r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
INIT_LIST_HEAD(r);
for(q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
inet_register_protosw(q);
/*
* Set the ARP module up
*/
arp_init();
/*
* Set the IP module up
*/
ip_init();
tcp_v4_init(&inet_family_ops);
/* Setup TCP slab cache for open requests. */
tcp_init();
/*
* Set the ICMP layer up
*/
icmp_init(&inet_family_ops);
/* I wish inet_add_protocol had no constructor hook...
I had to move IPIP from net/ipv4/protocol.c :-( --ANK
*/
#ifdef CONFIG_NET_IPIP
ipip_init();
#endif
#ifdef CONFIG_NET_IPGRE
ipgre_init();
#endif
/*
* Initialise the multicast router
*/
#if defined(CONFIG_IP_MROUTE)
ip_mr_init();
#endif
/*
* Create all the /proc entries.
*/
#ifdef CONFIG_PROC_FS
proc_net_create ("raw", 0, raw_get_info);
proc_net_create ("netstat", 0, netstat_get_info);
proc_net_create ("snmp", 0, snmp_get_info);
proc_net_create ("sockstat", 0, afinet_get_info);
proc_net_create ("tcp", 0, tcp_get_info);
proc_net_create ("udp", 0, udp_get_info);
#endif /* CONFIG_PROC_FS */
ipfrag_init();
return 0;
}
module_init(inet_init);
2.4 路由初始化(包含 neighbour 表、FIB 表、和路由缓存表的初始化工作)
2.4.1 rtcache 表 ip_rt_init()函数 在 net/ipv4/ip_output 中调用,net/ipv4/route.c 中定义
2.4.2 FIB 初始化 在 ip_rt_init()中调用 在 net/ipv4/fib_front.c 中定义
2.4.3 neigbour 表初始化 arp_init()函数中定义
2.5 网络接口设施初始化
在零碎中网络接口都是由一个 dev_base 链表进行治理的。通过内核的启动形式也是通过这个链表进行操作的。在系统启动之初,将所有内核可能反对的网络接口都初始化成这个链表中的一个节点,并且每个节点都须要初始化出 init 函数指针,用来检测网络接口设施。而后,零碎遍历整个 dev_base 链表,对每个节点别离调用 init 函数指针,如果胜利,证实网络接口设施可用,那么这个节点就能够进一步初始化,如果返回失败,那么证实该网络设备不存在或是不可用,只能将该节点删除。启动完结之后,在 dev_base 中剩下的都是能够用的网络接口设施。
2.5.1 do_initcalls—->net_dev_init()(net/core/dev.c)——>ethif_probe()(drivers/net/Space.c, 在 netdevice{}构造的 init 中调用,这边 ethif_probe 是以太网卡针对的调用)
3. 网络设备驱动程序(略)
4. 网络连接
4.1 连贯的建设和敞开
tcp 连贯建设的代码如下:
server=gethostbyname(SERVER_NAME);
sockfd=socket(AF_INET,SOCK_STREAM,0);
address.sin_family=AF_INET;address.sin_port=htons(PORT_NUM);
memcpy(&address.sin_addr,server->h_addr,server->h_length);
connect(sockfd,&address,sizeof(address));
连贯的初始化与建设期间次要产生的事件如下:
1)sys_socket 调用:调用 socket_creat(), 创立出一个满足传入参数 family、type、和 protocol 的 socket, 调用 sock_map_fd()获取一个未被应用的文件描述符,并且申请并初始化对应的 file{}构造。
2)sock_creat():创立 socket 构造,针对每种不同的 family 的 socket 构造的初始化,就须要调用不同的 create 函数来实现。对应于 inet 类型的地址来说,在网络协议初始化时调用 sock_register()函数中实现注册的定义如下:
struct net_proto_family inet_family_ops={
PF_INET;
inet_create
};
所以 inet 协定最初会调用 inet_create 函数。
3)inet_create: 初始化 sock 的状态设置为 SS_UNCONNECTED, 申请一个新的 sock 构造,并且初始化 socket 的成员 ops 初始化为 inet_stream_ops, 而 sock 的成员 prot 初始化为 tcp_prot。而后调用 sock_init_data, 将该 socket 构造的变量 sock 和 sock 类型的变量关联起来。
4)在零碎初始化结束后便是进行 connect 的工作,零碎调用 connect 将一个和 socket 构造关联的文件描述符和一个 sockaddr{}构造的地址对应的近程机器相关联,并且调用各个协定本人对应的 connect 连贯函数。对应于 tcp 类型,则 sock->ops->connect 便为 inet_stream_connect。
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。
6) tcp_v4_connect():调用函数 ip_route_connect(),寻找适合的路由寄存在 rt 中。ip_route_connect 找两次,第一次找到下一跳的 ip 地址,在路由缓存或 fib 中找到,而后第二次找到下一跳的具体街坊,到 neigh_table 中找到。而后申请出 tcp 头的空间寄存在 buff 中。将 sk 中相干地址数据做一些针对路由的变动,并且初始化一个 tcp 连贯的序列号,调用函数 tcp_connect(),初始化 tcp 头,并设置 tcp 解决须要的定时器。一次 connect()建设的过程就完结了。
连贯的敞开次要如下:
1)close: 一个 socket 文件描述符对应的 file{}构造中,有一个 file_operations{}构造的成员 f_ops,它的初始化敞开函数为 sock_close 函数。
2)sock_close:调用函数 sock_release(), 参数为一个 socket{}构造的指针。
3)sock_release:调用 inet_release,并开释 socket 的指针和文件空间
4)inet_release: 调用和该 socket 对应协定的敞开函数 inet_release, 如果是 tcp 协定,那么调用的是 tcp_close;最初开释 sk。
4.2 数据发送流程图
各层次要函数以及地位性能阐明:
1)sock_write:初始化 msghdr{}构造 net/socket.c
2)sock_sendmsg:net/socket.c
3)inet_sendmsg:net/ipv4/af_net.c
4)tcp_sendmsg:申请 sk_buff{}构造的空间,把 msghdr{}构造中的数据填入 sk_buff 空间。net/ipv4/tcp.c
5)tcp_send_skb:net/ipv4/tcp_output.c
6)tcp_transmit_skb:net/ipv4/tcp_output.c
7)ip_queue_xmit:net/ipv4/ip_output.c
8)ip_queue_xmit2:net/ipv4/ip_output.c
9)ip_output:net/ipv4/ip_output.c
10)ip_finish_output:net/ipv4/ip_output.c
11)ip_finish_output2:net/ipv4/ip_output.c
12)neigh_resolve_output:net/core/neighbour.c
13)dev_queue_xmit:net/core/dev.c
4.3 数据接管流程图
各层次要函数以及地位性能阐明:
1)sock_read: 初始化 msghdr{}的构造类型变量 msg,并且将须要接管的数据寄存的地址传给 msg.msg_iov->iov_base. net/socket.c
2)sock_recvmsg: 调用函数指针 sock->ops->recvmsg()实现在 INET Socket 层的数据接管过程. 其中 sock->ops 被初始化为 inet_stream_ops, 其成员 recvmsg 对应的函数实现为 inet_recvmsg()函数. net/socket.c
3)sys_recv()/sys_recvfrom(): 别离对应着面向连贯和面向无连贯的协定两种状况. net/socket.c
4)inet_recvmsg: 调用 sk->prot->recvmsg 函数实现数据接管, 这个函数对于 tcp 协定便是 tcp_recvmsg net/ipv4/af_net.c
5)tcp_recvmsg: 从网络协议栈接收数据的动作, 自上而下的触发动作始终到这个函数为止, 呈现了一次期待的过程. 函数 tcp_recvmsg 可能会被动地期待在 sk 的接收数据队列上, 也就是说, 零碎中必定有其余中央会去批改这个队列使得 tcp_recvmsg 能够进行上来. 入口参数 sk 是这个网络连接对应的 sock{}指针,msg 用于寄存接管到的数据. 接收数据的时候会去遍历接管队列中的数据, 找到序列号适合的.
但读取队列为空时 tcp_recvmsg 就会调用 tcp_v4_do_rcv 应用 backlog 队列填充接管队列.
6)tcp_v4_rcv:tcp_v4_rcv 被 ip_local_deliver 函数调用, 是从 IP 层协定向 INET Socket 层提交的 ” 数据到 ” 申请, 入口参数 skb 寄存接管到的数据,len 是接管的数据的长度, 这个函数首先挪动 skb->data 指针, 让它指向 tcp 头, 而后更新 tcp 层的一些数据统计, 而后进行 tcp 的一些值的校验. 再从 INET Socket 层中曾经建设的 sock{}构造变量中查找正在期待以后达到数据的哪一项. 可能这个 sock{}构造曾经建设, 或者还处于监听端口、期待数据连贯的状态。返回的 sock 构造指针寄存在 sk 中。而后依据其余过程对 sk 的操作状况, 将 skb 发送到适合的地位. 调用如下:
TCP 包接收器 (tcp_v4_rcv) 将 TCP 包投递到目标套接字进行接管解决. 当套接字正被用户锁定,TCP 包将临时排入该套接字的后备队列 (sk_add_backlog). 这时如果某一用户线程希图锁定该套接字(lock_sock), 该线程被排入套接字的后备解决期待队列(sk->lock.wq). 当用户开释上锁的套接字时(release_sock, 在 tcp_recvmsg 中调用), 后备队列中的 TCP 包被立刻注入 TCP 包处理器(tcp_v4_do_rcv) 进行解决, 而后唤醒期待队列中最先的一个用户来取得其锁定权. 如果套接字未被上锁, 当用户正在读取该套接字时, TCP 包将被排入套接字的预备队列(tcp_prequeue), 将其传递到该用户线程上下文中进行解决. 如果增加到 sk->prequeue 不胜利, 便能够增加到 sk->receive_queue 队列中(用户线程能够注销到预备队列, 当预备队列中呈现第一个包时就唤醒期待线程.) /net/tcp_ipv4.c
7)ip_rcv、ip_rcv_finish: 从以太网接收数据,放到 skb 里,作 ip 层的一些数据及选项查看,调用 ip_route_input()做路由解决, 判断是进行 ip 转发还是将数据传递到高一层的协定. 调用 skb->dst->input 函数指针, 这个指针的实现可能有多种状况, 如果路由失去的后果阐明这个数据包应该转发到其余主机, 这里的 input 便是 ip_forward; 如果数据包是给本机的, 那么 input 指针初始化为 ip_local_deliver 函数./net/ipv4/ip_input.c
8)ip_local_deliver、ip_local_deliver_finish: 入口参数 skb 寄存须要传送到下层协定的数据, 从 ip 头中获取是否曾经分拆的信息, 如果曾经分拆, 则调用函数 ip_defrag 将数据包重组。而后通过调用 ip_prot->handler 指针调用 tcp_v4_rcv(tcp)。ip_prot 是 inet_protocol 构造指针, 是用来 ip 层注销协定的,比方由 udp,tcp,icmp 等协定。/net/ipv4/ip_input.c
Linux 通过同时对多种通信协议的反对来提供通用的底层根底服务。它的第一个网络模型的版本是 4.3 BSD,也称为 Net/1,明天的 Linux 曾经应用 Net/4(Linux 2.2),其中大多数代码曾经齐全和 BSD 的版本不同,然而它仍然反对 UINX 平台之间程序的移植。
Linux 网络套接字实现的模式是 UNIX 下的广泛规范。同时,Net/ 4 的网络层是齐全重整旗鼓重写的。首先,新的网络层尽可能地履行并行处理,因而其伸缩性比起以前的版本,不可同日而语。其次,它包含了许多的优化,以便绕过不少风行操作系统网络实现中的不合理处(例如 Windows)。到目前为止,Linux 是惟一与 IPv4 和 IPv6 协定规范齐全放弃兼容的操作系统,而 Linux2.4 的 IPv4 伸缩性又大有进步。
Linux 反对的六种不同通信协议族:
1)TCP/IP(应用 TCP/IP 的 Internet 协定族),本文探讨的重点。
2)UNIX 域协定(一种过程间通信的协定)
3)X25 协定
4)AX25 协定(业余无线 X25)
5)IPX 协定(Novell IPX)
6)APPLETALK 协定(AppleTalk DDP)
1.1 内核源代码的组织
表 1 是本文要应用的 Linux Net/ 4 网络源代码的, 其中大部分位于目录 /usr/src/linux-2.2.x/net, 列表如下,
插口层
BSD Socket
/net/socket.c
/net/protocols.c
INET Socket
/ipv4/protocol.c
/ipv4/af_inet.c
/net/ipv4/core/sock.c
协定层
TCP/UDP
/net/ipv4/udp.c
/net/ipv4/datagram.c
/net/ipv4/tcp_input.c
/net/ipv4//tcp_output.c
/net/ipv4/tcp.c
/net/ipv4/tcp_minisocks.c
/net/ipv4/tcp_timer.c etc…
IP
/net/ipv4/ip_forward.c
/net/ipv4/ip_fragment.c
/net/ipv4/ip_input.c
/net/ipv4/ip_output.c
接口层
Ethernet
……
1.2 Linux 中 TCP/IP 网络层次结构与实现
Linux 通过一组相邻的软件层实现了 TCP/IP 模型,它由 BSD Socket 层、INET
Socket 层、传输层、网络层,和链路层形成。应用程序应用零碎调用向内核函数传递参数和数据从而进入内核空间,由内核中注册的内核函数对相应的数据结构进行解决。Linux 的 TCP/IP 层次结构和实现形式如图所示。