目录
该函数所用的全局变量
1:Dev_proc_init()
2:netdev_kobject_init()
3:register_pernet_subsys(&netdev_net_ops)
4:open_softirq(NET_TX_SOFTIRQ, net_tx_action)
5: open_softirq(NET_TX_SOFTIRQ, net_rx_action)
6:net_rx_action收到数据执行的软中断。
7: process_backlog
Net_dev_init(/net/core/dev.c)
1:dev_proc_ops
2:first_device 链表
3:net_namespace_list 网络设备链表用于寻找 net设备
4:net_ns_type_operations
其实这个函数就是在文件系统中 proc 中去得到相应的文件
设备功能初始化,其实在内部执行的为register_pernet_subsys(&dev_proc_ops)
在这个函数中其实是执行的 register_pernet_operations(first_device, ops); 这其中其实first_device其实就是一个链表,初始化一个链表的结构体。
这个函数的内部就是__register_pernet_operations(list, ops);
----- 目前假定函数宏CONFIG_NET_NS为1
执行 list_add_tail(&ops->list, list) 表明将 first_device 加入到 dev_proc_ops的list结构中
For_each_net(net)目前看是在寻找网络,在可执行的网络中,执行ops_init---
其实这个函数目的就是执行Ops->init(net), 寻找加入到net_namespace_list中的网络设备。
他就是执行dev_proc_ops下面的init 函数 dev_proc_net_init(struct net* net)
Proc_net_fops_creat( ) ------- 实际调用 proc_create(name, mode, net->proc_net, fops)
这个其实就是在proc 下面创建文件。对应的dev和 softnet_stat, ptype。
Proc/net/ 下对应的 dev, softnet_stat, ptype, wireless.
Proc/ net下面可以看到很有有用的信息 上面的几个文件对应的就是可以查看的信息。
在内部执行的为 kobj_ns_type_register(&net_ns_type_operations)
暂时没有关注。
接受网络数据的中断,先学习哈open_softirq函数
5.1:中断的下半部
对于一个中断分为上半部和下半部,上半部处理对时间特别敏感一般来说和硬件相关的,并保证不被其他中断打断的任务。而实现下半部分的方法目前有三种方法,软中断,tasklet,工作队列,此处只是用于软中断。
软中断其实包含了如下的结构:
1:注册软中断:open_softirq
2: 触发软中断: raise_softirq
3: 执行软中断: do_softirq:
4: 结束软中断
其中初始化时 有如下的init:
for_each_possible_cpu(i)
{
struct softnet_data *queue;
queue = &per_cpu(softnet_data, i);
/*初始化输入队列*/
skb_queue_head_init(&queue->input_pkt_queue);
queue->completion_queue = NULL;
/*初始化pool 链表头*/
INIT_LIST_HEAD(&queue->poll_list);
/*把backlog的轮询函数初始化为 process_backlog
该函数来处理传统接口使用的输入队列的报文*/
queue->backlog.poll = process_backlog;
/*backlog 轮询函数一次可以处理的报文上限个数*/
queue->backlog.weight = weight_p;
queue->backlog.gro_list = NULL;
queue->backlog.gro_count = 0;
}
处理上来的报文处理为process_backlog。
目前来看 网卡驱动后调用的是napi_schedule()调用的执行网络中断。
需要继续看的是ieee80211_napi_schedule 调度的执行软中断
在其中的___napi_schedule中执行的list_add_tail 把sd的数据挂在napi的polllist中
然后执行__raise_softirq_irqoff这一个软中断。这里就是执行net_dev
_init中执行的net_rx_action
Softnet_data *sd 其实就是ieee80211_napi_schedule 中加入到cpu数据的poll_list链表。
如下的表示是该函数的解释
static void net_rx_action(struct softirq_action *h)
{
/*取得本地cpu 的softnet_data 的poll_list 链表*/
struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
/*设置软中断处理程序一次允许的最大执行时间为2个jiffies*/
unsigned long time_limit = jiffies + 2;
/*设置软中断接收函数一次最多处理的报文个数为 300 */
int budget = netdev_budget;
/*关闭本地cpu的中断,下面判断list是否为空时防止硬中断抢占*/
local_irq_disable();
/*循环处理pool_list 链表上的等待处理的napi*/
while (!list_empty(list))
{
struct napi_struct *n;
int work, weight;
/*如果处理报文超出一次处理最大的个数
或允许时间超过最大时间就停止执行,
跳到softnet_break 处*/
if (unlikely(budget <= 0 || time_after(jiffies, time_limit)))
{
goto softnet_break;
}
/*使能本地中断,上面判断list为空已完成,下面调用NAPI
的轮询函数是在硬中断开启的情况下执行*/
local_irq_enable();
/* 取得softnet_data pool_list 链表上的一个napi,
即使现在硬中断抢占软中断,会把一个napi挂到pool_list的尾端
软中断只会从pool_list 头部移除一个pool_list,这样不存在临界区*/
n = list_entry(list->next, struct napi_struct, poll_list);
/*用weighe 记录napi 一次轮询允许处理的最大报文数*/
weight = n->weight;
/* work 记录一个napi总共处理的报文数*/
work = 0;
/*如果取得的napi状态是被调度的,就执行napi的轮询处理函数*/
if (test_bit(NAPI_STATE_SCHED, &n->state))
{
work = n->poll(n, weight);
}
WARN_ON_ONCE(work > weight);
/*预算减去已经处理的报文数*/
budget -= work;
/*禁止本地CPU 的中断,下面会有把没执行完的NAPI挂到softnet_data
尾部的操作,和硬中断存在临界区。同时while循环时判断list是否
为空时也要禁止硬中断抢占*/
local_irq_disable();
/*如果napi 一次轮询处理的报文数正好等于允许处理的最大数,
说明一次轮询没处理完全部需要处理的报文*/
if (unlikely(work == weight))
{
/*如果napi已经被禁用,就把napi 从 softnet_data 的pool_list 上移除*/
if (unlikely(napi_disable_pending(n)))
{
local_irq_enable();
napi_complete(n);
local_irq_disable();
}
else
{
/*否则,把napi 移到 pool_list 的尾端*/
list_move_tail(&n->poll_list, list);
}
}
}
out:
local_irq_enable();
return;
/*如果处理时间超时,或处理的报文数到了最多允许处理的个数,
说明还有napi 上有报文需要处理,调度软中断。
否则,说明这次软中断处理完全部的napi上的需要处理的报文,不再需要
调度软中断了*/
softnet_break:
__get_cpu_var(netdev_rx_stat).time_squeeze++;
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
goto out;
}
其实在这其中执行的 就是 当其处于被调度的 就执行
n->poll(n, weight);
而是否被调度 在上面ieee的函数中就是设置调度这个的函数
而设置n->poll是在ieee80211_napi_poll中去设置得到
即函数中 netif_napi_add中执行最开始调用的为
Ieee80211_register_hw中设置。
目前来看其调用的为 net_dev_iniit中设置的 process_backlog函数。
下一个小节 开始分析