TCP/IP协议在Linux2.4上的实现 之 网卡驱动篇二 接着上面分析net_rx()函数,此函数实在网卡的驱动中断服务函数里边调用 接收函数:1 确定网络硬件收到的数据包的状态和数据大小 2 dev_alloc_skb,申请套接字缓存,并将数据拷贝到其中 3 将套接字缓存通过netif_rx()传递给内核 4 更新数据处理后的统计信息 static void net_rx(struct net_device *dev) { struct net_local *lp = (struct net_local *)dev->priv; struct sk_buff *skb; int status, length; int ioaddr = dev->base_addr; status = inw(ioaddr + RX_FRAME_PORT);//读取接收的数据状态 length = inw(ioaddr + RX_FRAME_PORT);//读取收到的数据包长度 if ((status & RX_OK) == 0) { count_rx_errors(status, lp);//统计出错信息 return; } /* Malloc up new buffer. */ skb = dev_alloc_skb(length + 2);// 申请sk_buff结构空间,作为套接字缓存空间 skb_reserve(skb, 2); /* longword align L3 header */ skb->dev = dev; insw(ioaddr + RX_FRAME_PORT, skb_put(skb, length), length >> 1);//将数据拷贝到套接字缓存中 if (length & 1) skb->data[length-1] = inw(ioaddr + RX_FRAME_PORT); skb->protocol=eth_type_trans(skb,dev); //此函数定义在net/ethernet/eth.c unsigned short eth_type_trans(struct sk_buff *skb, struct net_device *dev) //这个函数抽取协议标识( ETH_P_IP, 在这个情况下 )从以太网头; 它也赋值 skb->mac.raw, 从报文 data (使用 skb_pull)去掉硬件头部, 并且设置 //skb->pkt_type. 最后一项在 skb 分配是缺省为 PACKET_HOST(指示报文是发向这个主机的), eth_type_trans 改变它来反映以太网目的地址: 如果这个地址不匹配 //接收它的接口地址, pkt_type 成员被设为 PACKET_OTHERHOST. 结果, 除非接口处于混杂模式或者内核打开了报文转发, netif_rx 丢弃任何类型 //PACKET_OTHERHOST 的报文. 因为这样, snull_header 小心地使目的硬件地址匹配接收接口., netif_rx(skb);//向内核传送套接字缓存 //统计信息 dev->last_rx = jiffies; lp->stats.rx_packets++; lp->stats.rx_bytes += length; } int netif_rx(struct sk_buff *skb)的函数在/net/core/dev.c netif_rx会逐步调用netif_rx_schedule -->__netif_rx_schedule, __netif_rx_schedule函数 会调用__raise_softirq_irqoff(NET_RX_SOFTIRQ);触发网络接收数据包的软中断函数net_rx_action。 int netif_rx(struct sk_buff *skb) { int this_cpu = smp_processor_id(); struct softnet_data *queue; unsigned long flags; if (skb->stamp.tv_sec == 0) do_gettimeofday(&skb->stamp);//数据包的时间戳 queue = &softnet_data[this_cpu]; local_irq_save(flags); netdev_rx_stat[this_cpu].total++; if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {//如果目前队列的长度小于支持的最大队长 if (queue->input_pkt_queue.qlen) {//如果队列长度不为零 if (queue->throttle) goto drop; enqueue://排队,将skb放到queue->input_pkt_queue队列的尾部 dev_hold(skb->dev); __skb_queue_tail(&queue->input_pkt_queue,skb); /* Runs from irqs or BH's, no need to wake BH */ cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);//触发软中断,调用net_rx_action() local_irq_restore(flags); return softnet_data[this_cpu].cng_level;//返回congestion level,拥塞程度 } if (queue->throttle)// 若队列的阀门是关闭的 { queue->throttle = 0;//开启阀门 } goto enqueue; } if (queue->throttle == 0) { queue->throttle = 1; netdev_rx_stat[this_cpu].throttled++; } drop:// 超出长度,将此包丢弃 netdev_rx_stat[this_cpu].dropped++; local_irq_restore(flags); kfree_skb(skb); return NET_RX_DROP; } net_rx_action()定义在/net/core/dev.c下,主要的作用就是根据skb中的协议类型,把skb结构传递给相应的协议处理函数。 net_rx_action()根据数据包的协议类型在数组ptype_base[16]里找到相应的协议,并从中知道了接收的处理函数,然后把数据包交给处理函数 此函数工作的IP层,在ptype_base[16]里定义的函数,一般为ip_recv static void net_rx_action(struct softirq_action *h) { int this_cpu = smp_processor_id(); struct softnet_data *queue = &softnet_data[this_cpu]; unsigned long start_time = jiffies;//开始时间 int bugdet = netdev_max_backlog;//网络驱动的最大的积压数 br_read_lock(BR_NETPROTO_LOCK); for (;;) {//死循环,符合某些条件才会跳出循环 struct sk_buff *skb; struct net_device *rx_dev; local_irq_disable(); skb = __skb_dequeue(&queue->input_pkt_queue);//取得输入队列的头部 local_irq_enable(); if (skb == NULL) break; skb_bond(skb);//用(skb->master)重新绑定包所在设备 rx_dev = skb->dev; skb->h.raw = skb->nh.raw = skb->data;//这时(skb->data)指向帧头尾部,为包头的开始 { struct packet_type *ptype, *pt_prev; unsigned short type = skb->protocol;//获得本skb的协议类型 pt_prev = NULL; for (ptype = ptype_all; ptype; ptype = ptype->next) {//先找ptype_all里边的处理函数,找到相应的处理函数 if (!ptype->dev || ptype->dev == skb->dev) { if (pt_prev) { if (!pt_prev->data) { deliver_to_old_ones(pt_prev, skb, 0); } else { atomic_inc(&skb->users); pt_prev->func(skb, skb->dev, pt_prev); } } pt_prev = ptype; } } for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) //在数组ptype_base[16]里找到相应的协议 { if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev)) //如果类型匹配并且设备匹配 { if (pt_prev) { if (!pt_prev->data)//如果skb不存在数据域,往旧式设备传递 deliver_to_old_ones(pt_prev, skb, 0); else { atomic_inc(&skb->users);//增加skb的使用用户 pt_prev->func(skb, skb->dev, pt_prev);//执行在ptype_base[16]注册的处理函数,此处为往上的接口 } } pt_prev = ptype; } } if (pt_prev) { if (!pt_prev->data) deliver_to_old_ones(pt_prev, skb, 1); else pt_prev->func(skb, skb->dev, pt_prev); } else//如果没找到对应的协议类型 kfree_skb(skb); } dev_put(rx_dev); if (bugdet-- < 0 || jiffies - start_time > 1)//等待时间按超时,或者bugdet用完 goto softnet_break; } br_read_unlock(BR_NETPROTO_LOCK); local_irq_disable();//关闭中断 if (queue->throttle)//再次将queue的阀门开启,可以接收数据 { queue->throttle = 0; } local_irq_enable();//开启中断 NET_PROFILE_LEAVE(softnet_process); return; softnet_break://未正常处理完 br_read_unlock(BR_NETPROTO_LOCK); local_irq_disable(); netdev_rx_stat[this_cpu].time_squeeze++; /* This already runs in BH context, no need to wake up BH's */ cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);//未正常处理完,再次调用NET_RX_SOFTIRQ软中断 local_irq_enable(); NET_PROFILE_LEAVE(softnet_process); return; }