网络协议栈16:connect函数分解之链路层接收数据
Connect函数在发送了数据之后,就进入了休眠等待的状态,等待远方发过来的数据来确认链接是否成功。那么数据是如何从网卡那里传递到链路层?
数据在网卡的内存中,形成了以256字节为1页的环形缓冲链,每当有数据到达,网卡就会发生一个中断信号,告诉CPU已经有数据到达,此时,CPU通过DMA的方式,去读取网卡内存中的数据,并把数据存放在一个专门开辟来接收这个数据包的skb_buff中,之后,就把接收数据的skb_buff进行排列,并且累加所有skb_buff的个数,如果超过一定的数量在排队等待被取走,则后面不能再继续从网卡中读取数据包,以防止系统内存被过度的消耗掉。因此,在数据链路层,skb_buff是被排成一个队列,达到FIFO的目的。
一般接收数据都是在中断中完成,而中断时需要快速的进行处理,以免消耗系统过多的资源,因此,把数据进行队列排队后,就要离开中断处理函数了,此时,需要启动中断程序的后半部来进行剩余的数据的处理。
net_bh()函数是接收中断处理函数的后半部处理函数,其主要的工作是
1. 把还在链路层中排队,还没有发送出去的数据包发送送出去(可见发送数据包是很重要的)。
2. 把在接收队列中排队的数据包的上层协议类型都找出来,准备把数据发送往对应的协议层(遍历协议类型链表,即struct packet_type所组成的链表,来找到需要发送的数据包所对应的上层协议)。
3. 调用相应的上层协议的接收函数来接收数据包。
上层协议的接收函数,是在操作系统初始化的时候,就已经初始化好了,其实就是注册
struct packet_type {
unsigned short type; /* This is really htons(ether_type). */
struct device * dev;
int (*func) (struct sk_buff *, struct device *,
struct packet_type *);
void *data;
struct packet_type *next;
};
这样的结构体(比如static struct packet_type ip_packet_type,static struct packet_type arp_packet_type),其中的func指针所指向的,就是type所确定的协议类型的接收函数,type可取类型如下
#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */
#define ETH_P_ECHO 0x0200 /* Ethernet Echo packet */
#define ETH_P_PUP 0x0400 /* Xerox PUP packet */
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
#define ETH_P_ARP 0x0806 /* Address Resolution packet */
#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */
#define ETH_P_X25 0x0805 /* CCITT X.25 */
#define ETH_P_ATALK 0x809B /* Appletalk DDP */
#define ETH_P_IPX 0x8137 /* IPX over DIX */
#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */
#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */
#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */
#define ETH_P_802_2 0x0004 /* 802.2 frames */
#define ETH_P_SNAP 0x0005 /* Internal only */