深入理解linux网络技术之驱动层

        最近我又开始看这个《深入理解linux网络技术内幕》了。以前一直觉得这本书是一个巨无霸,昨天和前天各花了一点时间。大概是一直在搞内核的缘故吧!现在看起来倒不是很吃力了。大概看了1/3吧,虽然的确有点跑马观花了意味,但是自我感觉还是明白了主干的东西。《linux内核情景分析》里面对软中断讲得比较简略,而《深入》那书后边还图书馆了,所以导致我对软中断的理解很不够透彻。现在大致终结一下吧!

        首先,软中断的执行是由ksoftirqd完成的,这是多个内核进程,每个CPU都有一个这样的进程,申明如下:
		//linux-3.0.8/include/linux/interrupt.h
		DECLARE_PER_CPU(struct list_head [NR_SOFTIRQS], softirq_work_list);
		DECLARE_PER_CPU(struct task_struct *, ksoftirqd);
		
		static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
		
		struct softirq_action
		{
			void	(*action)(struct softirq_action *);
		};
		
       softirq_action就是一个函数指针,NR_SOFTIRQS就是软中断的个数,不信你可以看看下边。
	enum
		{
			HI_SOFTIRQ=0,
			TIMER_SOFTIRQ,
			NET_TX_SOFTIRQ,
			NET_RX_SOFTIRQ,
			BLOCK_SOFTIRQ,
			BLOCK_IOPOLL_SOFTIRQ,
			TASKLET_SOFTIRQ,
			SCHED_SOFTIRQ,
			HRTIMER_SOFTIRQ,
			RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */
		
			NR_SOFTIRQS
		};

        在每个ksoftirqd线程中,它所干的是就去执行softirq_vec函数指针数组中的函数,相当于按优先级执行。每个软中断的函数都是在内核里定义好的。比如tasklet_hi_action(), tasklet_action(),net_rx_action()。当然,这还不算完。对于不同的软中断函数,执行的方法也不一样。就tasklet_hi_action()和tasklet_action()而言,它们各自有一个链表分别叫tasklet_vec,tasklet_hi_vec。这些链表单元结构都是tasklet_struct类型的。定义如下:
		//linux-3.0.8/include/linux/interrupt.h
		struct tasklet_struct
		{
			struct tasklet_struct *next;
			unsigned long state;
			atomic_t count;
			void (*func)(unsigned long);
			unsigned long data;
		};
	//linux-3.0.8/kernel/softirq.c
		static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
		static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);
看到那个func函数指针没有,这个就是链表最重要的元素。软中断函数tasklet_hi_action()和tasklet_action()的作用就是调用这个链表中的func函数。可以根据需要练添加tasklet_struct元素进入链表。对于net_rx_action(),这函数就是真正衔接中断的函数,算是所谓的“bottom half”了。它的工作机理其实和前面两个差不多。依然是一个链表,链表的表头在softnet_data。这是一个全局的数据结构。每一个CPU都有一个对应的soft_data,它包含一个headlist polllist的指针。polllist实际连接了napi_struct类型。而这个napi_struct类型是net_device息息相关。napi_struct中的poll指针就指向了具体的驱动模块中的poll()函数。到这里,我们终于找到了和上半部衔接的地方了。下面这是e100驱动的poll函数。
		static int e100_poll(struct napi_struct *napi, int budget)
		{
			struct nic *nic = container_of(napi, struct nic, napi);
			unsigned int work_done = 0;
		
			e100_rx_clean(nic, &work_done, budget);
			e100_tx_clean(nic);
		
			/* If budget not fully consumed, exit the polling mode */
			if (work_done < budget) {
				napi_complete(napi);
				e100_enable_irq(nic);
			}
		
			return work_done;
		}
        看似清爽的函数其实并没有这么简单。主要的工作都是e100_rx_clean()在干。然后就是接收了,值得一提的是e100使用了DMA,然后调用的netif_receive_skb(skb)。这的确是历史性的一刻了。这标志着我们的数据经过千辛万苦终于是走出了驱动层,向L3迈进。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值