Linux网络系统底层机制分析(3)
----报文接收
前面介绍了两种接口:老接口,NAPI接口。两者在报文接收的处理机制是不一样的,下面结合内核中实际的驱动仔细分析。
1.sk_buff结构
sk_buff结构可能是贯穿整个子系统的结构,它表明了接收到的或者要发送的数据的一些重要信息。其包含的域非常多,定义在include/linux/skbuff..h中。内核利用其next,prev指针把所有的sk_buff结构组织成一个双向链表,但其结构比一般的传统链表要复杂得多。为保证每一个sk_buff都能快速地找到链表头,另外加了一个类似的结构:sk_buff_head来表明整个链表的表头,另外每一个sk_buff有一个list域指向此头结构。在sk_buff_head中有一个锁防止并发(但是锁整个链表是不是太大了?)。
其他的重要的域有以下几个(参考《Understanding LINUX network internal》P22):
Struct sock *sk 指向拥有此缓冲区(sk_buff)的sock结构。但要注意此结构并不一定有效。他的存在主要是为了为四层及以上和用户空间程序提供一些信息,所以只有那些本地产生或者由本地进程接收到的数据才有。对于那些被转发(我的理解是在三层或者二层就被处理,但是书中的解释似乎不是,没大看懂),此域为NULL。
Unsigned int len 指示了缓冲区中的数据大小,只是表明了当前的有效数据长度,他会随着在协议栈中的处理位置不同而变化。
Unsigned char *head, *end,*data,*tail 此四个指针表明了缓冲区的边界(head,end),在其中的数据边界(data,tail)。在每一层的处理中,可能会分配一些额外的缓冲区,这时的指针可能就需要改动。
Struct net_device *dev 当包是接收到的,那么dev表明了收到该包的网络接口net_device,由接口卡的驱动复杂更新;当包是要被发送出去的,那么它将要从此dev代表的设备发送出去,对于支持虚拟设备驱动的话,那么他的意义会有一些小小的变化。
Struct net_device *input_dev input_dev指明了接收到该包的设备。如果是本地产生的报文,input_dev为NULL。对于以太设备,在eth_type_trans函数中设置该值。在流量控制中会比较频繁地使用该值。
Struct net_device *real_dev 此域仅对虚拟设备有效。在Bonding,VLAN接口中会使用。
Union {...} h, Union {...} nh, Union {...} mac 这些指向在TCP/IP协议栈中的协议头。其中h用于L4,nh用于L3,mac用于L2。
Struct dst_entry dst 在路由子系统中使用。
Char cb[40] 这是一个控制缓冲,供各层内部使用,自己维护,用以保存本层一些私有数据。比如tcp协议处理中可能会用它来保存一个称之为tcp_skb_sb的结构。当前该私有缓冲的大小固定为40字节。
Unsigned char pkt_type 基于二层目的地址给出该报文的类型:PACKET_HOST,PACKET_MULTICAST,PACKET_BROADCAST,PACKET_OTHERHOST,PACKET_OUTGOING,PACKET_LOOPBACK等。
Unsigend short protocol 该字段的值是相对L2来说的上层协议(next-higher protocol),比如IP,IPV6,ARP等(完整的列表可以参考include/linux/if_ether.h)。该字段被驱动用来通知上层该用哪一个协议来处理该帧。在驱动中调用netif_rx函数来触发上层处理函数,所以在此之前此字段必须被初始化。
上面提到了struct sock结构,在内核代码中的解释是:sockets在网络层的表示,是不是指和socket是相关的?以后接触到了再仔细看看。
2.中断处理