分类: LINUX
2008-09-05 09:37:39
内核版本:2.6.12
一、网卡驱动程序 当然,网卡驱动程序完成了从网卡接收数据的第一部份工作,以以3com501 的驱动 linux/drivers/net/3c501.c为例(因为它排在了第一个): 设备初始化函数中,依次调用: int init_module(void) ->el1_probe() -->el1_probe1() 先向内核申请注册一个以太设备,并设定设备的open函数为: dev->open = &el_open;(line 316) 在el_open函数中,注册了自己的中断处理程序:(line 350) if ((retval = request_irq(dev->irq, &el_interrupt, 0, dev->name, dev))) el_interruupt 为中断处理程序,中断实际上是一个电信号,网卡收到数据包后,就触发这个电信号给中断控制器的某个引脚,中断控制器向CPU某引脚发送相应的信号,CPU 检测到此信号后,进入内存中操作系统预设的位置的代码,即中断处理程序入口点,这样,操作系统设用do_IRQ()处理中断,该中断函数根据中断号和 dev_id,确定中断处理函数,对于我们的例子,也就是调用对应中断函数el_interrupt(),它判断网卡传递的信号,如果是接收数据,于是读 网卡寄存器……XXXXXXXXX 完成读取读取后,调用接收函数:(p654) else if (rxsr & RX_GOOD) { /* * Receive worked. */ el_receive(dev); } 参数dev 就是本身网卡的设备描述,el_receive()函数最重要的工作就是分配缓存: skb = dev_alloc_skb(pkt_len+2);(line724) 并关联设备: skb->dev = dev;(line740) 然后调用: netif_rx(skb);(line748) netif_rx是网络栈向驱动程序提供的接收数据包的接口,对于驱动程序的编写者而言,只需要知道这个函数即可,不过我们要分析整个接收流程,还要来进一步剖析它。 PS:网卡驱动的整个实现比较复杂,我这里只是做了简单的描述,对于没有网络驱动编写经验的朋友,可以参考《Linux设备驱动程序》第十七章网卡驱动方面的内容,九贱对作者提供的例子做了进一步的脚注: 独孤九贱 二、下半部 因为中断处理程序本身的一些局限性,为了提高其效率,Linux将中断处理分为两部份:上半部(中断处理程序)和下半部。 一些时间任务紧迫,与硬件相关的任务,被放在了中断处理程序中,而另外一些可以“稍微延迟”的任务被放在了下半部中。例如,响应网卡中断,读取接收数据,由网卡中断处理程序完成,而对数据包的接收的进一步处理,就由下半部来完成。 Linux中下半部的完成,有许多种实现,对于网络栈来说,采用了“软中断”机制。也就是说,当从网卡接收到数据后,网络子系统触发软中断,网卡中断处理程序就返回去继续它的工作了,而内核会在尽可能快的时间内,调用网络子系统的软中断函数,进一步处理数据包。 关于下半部和软中断的介绍,《Linux内核设计与实现》第二版第七章中,有详细的介绍。 三、队列 由于中断处理的接收函数netif_rx同软中断函数不是连续处理的。也就是说,netif_rx不是等上层协议栈处理完一个数据包后,再返回处理另一个 数据包,所以,在netif_rx与软中断函数中间,自然需要一个缓冲区:netif_rx往里面写,而软中断函数从里边取。所以,从性能的角度考虑,建 立这样一个缓冲区,是非常必要的。Linux实现这一缓冲的算法是队列。 Linux用struct softnet_data结构描述了队列: struct softnet_data { int throttle; int cng_level; int avg_blog; struct sk_buff_head input_pkt_queue; struct list_head poll_list; struct net_device *output_queue; struct sk_buff *completion_queue; struct net_device backlog_dev; /* Sorry. 8) */ }; throttle 用于拥塞控制,当拥塞发生时,throttle将被设置,后续进入的数据包将被丢弃; cng_level描述一个拥塞级别,由netif_rx函数返回。 input_pkt_queue成员即为包的输入队列,在后面的分析中,我们可以看它网络子系统,如果进行入队和出队操作; poll_list维护一个设备列表,特别的,我们并不需要针对每一个物理设备进行特别处理,比如,eth0接收一个包,eth1也接收一个包,eth0 ->eth1,再分别调用它们的处理函数poll,从队列中取出数据包并送住上层。而且采用了一个通用伪设备backlog_dev(struct softnet_data的最后一个成员),来进行处理,它的poll函数是process_backlog,在后面队列初始化中,我们会看到。 特别地,队列是一个per_cpu变量,也就是说,第一个CPU都有一个: DECLARE_PER_CPU(struct softnet_data,softnet_data);(net/core/dev.c line:576) 这样,每一次,都可以通过: for (i = 0; i < NR_CPUS; i++) { struct softnet_data *queue; queue = &per_cpu(softnet_data, i); …… } 来遍历每一个CPU的队列,并处理,或者是: struct softnet_data *queue; queue = &__get_cpu_var(softnet_data); 取得当前CPU的队列。 四、上层协议的处理 最后一个要讨论的问题是,如何把相应的协议送往相应的上层栈,如IP协议交由tcp/ip栈,arp协议交由arp处理程序…… 以太网帧头有一个类型字段,最简单的办法莫过于 switch(type) case 0x0800 do_ip(); case 0x0806: do_arp(); …… 呵呵,当然Linux不会用我的笨办法了 内核用ruct packet_type结构来描述每一类协议: struct packet_type { __be16 type; /* This is really htons(ether_type). */ struct net_device *dev; /* NULL is wildcarded here */ int (*func) (struct sk_buff *, struct net_device *, struct packet_type *); void *af_packet_priv; struct list_head list; }; type即为协议类型值,func函数指针为对应的包处理句柄,list成员用于维护协议链表。 以ip协议(net/ipv4/af_inet.c)为例: static struct packet_type ip_packet_type = { .type = __constant_htons(ETH_P_IP), .func = ip_rcv, }; 对于IP协议来讲,类型是ETH_P_IP,接收处理函数是ip_rcv。接下来,就是要把ip_packet_type添加进协议链表中去: inet_init(void)(line:1012) -> ip_init(); void __init ip_init(void) { dev_add_pack(&ip_packet_type); …… } void dev_add_pack(struct packet_type *pt) { int hash; spin_lock_bh(&ptype_lock); if (pt->type == htons(ETH_P_ALL)) { netdev_nit++; list_add_rcu(&pt->list, &ptype_all); } else { hash = ntohs(pt->type) & 15; list_add_rcu(&pt->list, &ptype_base[hash]); } spin_unlock_bh(&ptype_lock); } 可见,最后添加进的是一个以ptype_bash数组为首的链表,用协议值& 15,来取hash值,定位数组入口。 OK,有了这些基础,我们就可以来分析netif_rx函数了!! 未完,待续…… [ 本帖最后由 独孤九贱 于 2006-11-14 16:01 编辑 ] |
您对本贴的看法: |
__________________________________ 擦掉眼泪,抚平伤口,站起来重建家园,一定要坚强!!! |
| | 致电800-858-2903,了解DELL如何为你量身订制笔记本 | |
广告杀手-老法王
侠客 UID:14893 注册:2002-8-12 最后登录: 2008-09-05 帖子: 精华: 可用积分:15469 (大富大贵) 信誉积分: 空间积分:0 (白手起家) 专家积分: (本版) 状态:...在线... [] [] [博客] |
|
(九贱)
天使
UID:83191 注册:2003-8-12 最后登录: 2008-09-04 帖子: 精华: 可用积分:1336 (家境小康) 信誉积分: 空间积分:0 (白手起家) 专家积分: (本版) 来自:山城重庆 状态:...离线... [] [] [博客] |
|
(九贱)
天使
UID:83191 注册:2003-8-12 最后登录: 2008-09-04 帖子: 精华: 可用积分:1336 (家境小康) 信誉积分: 空间积分:0 (白手起家) 专家积分: (本版) 来自:山城重庆 状态:...离线... [] [] [博客] |
|