igb_write_itr只是记录一下硬件中断频率(据说目的是在减少对CPU的中断频率时用到) 。顺着napi_schedule调用一路跟踪下去,__napi_schedule=>____napi_schedule
/* Called with irq disabled */static inline void ____napi_schedule(struct softnet_data *sd, struct napi_struct *napi){ list_add_tail(&napi->poll_list, &sd->poll_list); __raise_softirq_irqoff(NET_RX_SOFTIRQ);}
这里我们看到,list_add_tail修改了CPU变量softnet_data里的poll_list,将驱动napi_struct传过来的poll_list添加了进来 。其中softnet_data中的poll_list是一个双向列表,其中的设备都带有输入帧等着被处理 。紧接着__raise_softirq_irqoff触发了一个软中断NET_RX_SOFTIRQ,这个所谓的触发过程只是对一个变量进行了一次或运算而已 。
void __raise_softirq_irqoff(unsigned int nr){ trace_softirq_raise(nr); or_softirq_pending(1UL << nr);}//file: include/linux/irq_cpustat.h#define or_softirq_pending(x) (local_softirq_pending() |= (x))
我们说过,Linux在硬中断里只完成简单必要的工作,剩下的大部分的处理都是转交给软中断的 。通过上面代码可以看到,硬中断处理过程真的是非常短 。只是记录了一个寄存器,修改了一下下CPU的poll_list,然后发出个软中断 。就这么简单,硬中断工作就算是完成了 。
3.2 ksoftirqd内核线程处理软中断
文章插图
图9 ksoftirqd内核线程
内核线程初始化的时候,我们介绍了ksoftirqd中两个线程函数ksoftirqd_should_run和run_ksoftirqd 。其中ksoftirqd_should_run代码如下:
static int ksoftirqd_should_run(unsigned int cpu){ return local_softirq_pending();}#define local_softirq_pending() __IRQ_STAT(smp_processor_id(), __softirq_pending)
这里看到和硬中断中调用了同一个函数local_softirq_pending 。使用方式不同的是硬中断位置是为了写入标记,这里仅仅只是读取 。如果硬中断中设置了NET_RX_SOFTIRQ,这里自然能读取的到 。接下来会真正进入线程函数中run_ksoftirqd处理:static void run_ksoftirqd(unsigned int cpu){ local_irq_disable(); if (local_softirq_pending()) { __do_softirq(); rcu_note_context_switch(cpu); local_irq_enable(); cond_resched(); return; } local_irq_enable();}
在__do_softirq中,判断根据当前CPU的软中断类型,调用其注册的action方法 。asmlinkage void __do_softirq(void){ do { if (pending & 1) { unsigned int vec_nr = h - softirq_vec; int prev_count = preempt_count(); ... trace_softirq_entry(vec_nr); h->action(h); trace_softirq_exit(vec_nr); ... } h++; pending >>= 1; } while (pending);}
在网络子系统初始化小节,我们看到我们为NET_RX_SOFTIRQ注册了处理函数net_rx_action 。所以net_rx_action函数就会被执行到了 。这里需要注意一个细节,硬中断中设置软中断标记,和ksoftirq的判断是否有软中断到达,都是基于smp_processor_id()的 。这意味着只要硬中断在哪个CPU上被响应,那么软中断也是在这个CPU上处理的 。所以说,如果你发现你的Linux软中断CPU消耗都集中在一个核上的话,做法是要把调整硬中断的CPU亲和性,来将硬中断打散到不同的CPU核上去 。
我们再来把精力集中到这个核心函数net_rx_action上来 。
static void net_rx_action(struct softirq_action *h){ struct softnet_data *sd = &__get_cpu_var(softnet_data); unsigned long time_limit = jiffies + 2; int budget = netdev_budget; void *have; local_irq_disable(); while (!list_empty(&sd->poll_list)) { ...... n = list_first_entry(&sd->poll_list, struct napi_struct, poll_list); work = 0; if (test_bit(NAPI_STATE_SCHED, &n->state)) { work = n->poll(n, weight); trace_napi_poll(n); } budget -= work; }}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 三种使用AI攻击网络安全的方法
- 理解了Linux I/O机制,才能真的明白“什么是多线程”
- Linux网络配置
- 带你重新认识Linux系统的inode
- 古剑奇谭网络版野外pvp 古剑奇谭ol剧情
- 从Linux源码看Socket的listen及连接队列
- linux网络编程常见API详解
- 了解神经网络和模型泛化
- TCP/IP 基础知识总结
- 如何在电脑上安装网络打印机?详细教程全部教给你