从分析uip代码,到学习设备驱动 ,到分析linux网络原码,俺这么些天这么辛苦 ,其实是为了解决如何将自己的私有协议栈跟linux系统紧密的进行衔接的问题 ,这其中涉及网卡的收发数据 ,阻塞的处理 ,协议的实现(基本就是状态机)等。今天终于觉得好像可以将这些东东贯穿到一起了 。
底层协议栈跟驱动的接口是netif_rx ,实际的数据处理是在botton half 函数net_rx_action 函数中进行的 ,下面我们对net_rx_action 进行分析 。不好意思不知道netif_rx的,看不懂什么是botton_half的恕俺不详细解答 。说了也没有用呵呵 ,不是一下子能够说明白的 ,虽然俺也不太懂呵呵
首先还是有必要介绍一下net_device结构 ,这个结构包含了网络设备的各种信息 ,有些类似于一个类 ,其中有很多属性和方法 ,其中有一个poll方法要特别注意
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 budget = netdev_max_backlog; ;/*表示队列的最大长度*/
br_read_lock(BR_NETPROTO_LOCK);
/*禁止中断*/
local_irq_disable();
/*检查POLL队列(poll_list)上是否有设备在准备等待轮询取得数据*/
while (!list_empty(&queue->poll_list)) {
struct net_device *dev;
/*这里保证执行当前的 POLL 过程的时间不超过一个时间片,这样不至于被软中断占用太多的时间*/
if (budget <= 0 || jiffies - start_time > 1)
goto softnet_break;
//开中断
local_irq_enable();
/*从公共的 softnet_data 数据结构中的轮循队列上获得等待轮循的设备结构*/
dev = list_entry(queue->poll_list.next, struct net_device,
poll_list);
/*调用设备的POLL方法从NIC上的Ring Buffer中读入数据*/
if (dev->quota <= 0 || dev->poll(dev, &budget)) {
//因为要对poll_list进行操作这个操作也不能被中断所以要关中断
local_irq_disable();
/*完成一次POLL过程的数据的接收,重新定义设备接收数据的"配额"
(事实上就是sk_buff缓冲区的数量,每次调用POLL方法的时候可以创建并且最
多可以向上层提交的sk_buff缓冲区数目,这个参数很重要在高速处理的时候有需要慎重优化这个数值,在有大量数据接收的情况下,需要增加该数值)*/
list_del(&dev->poll_list);
list_add_tail(&dev->poll_list, &queue->poll_list);
if (dev->quota < 0)
dev->quota += dev->weight;
else
dev->quota = dev->weight;
} else {
dev_put(dev);
local_irq_disable();
}
}
local_irq_enable();
//完成更新放开中断
br_read_unlock(BR_NETPROTO_LOCK);
return;
softnet_break: //如果上次操作被中断 ,再来一次
netdev_rx_stat[this_cpu].time_squeeze++;
__cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);
local_irq_enable();
br_read_unlock(BR_NETPROTO_LOCK);
}
dev->poll_list好像是指向第几个设备
真正的数据处理是在poll中进行的 。poll方法在net_dev_init的时候被指定为process_backlog ,这个函数就在net_rx_action紧挨的上方,它调用netif_receive_skb对skb数据进行了接收
这个是俺第一次的分析 ,可能有些地方不清楚或者错误的地方 ,以后会慢慢的修改
希望大家指出问题呵呵
增加1 :
/*#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
如果将函数定义写成
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member -0))) 将好理解很多 ,对于一个固定的结构体,其各个成员之间偏移是固定的,该函数用在知道一个结构体的成员地址 ,需要得到这个结构体的地址的时候 ,此处用来得到ptr的所有者的地址
*/