papaya内核笔记
读了两天的linux代码,一点点心得跟大家分享,高手轻拍~【感觉到对BH的需求了】
之前写IDE驱动的时候就有感觉,不过还好。这两天写网卡驱动,更加感到BH的重要。不只是提高中断的响应能力,有些代码放在BH里会特别舒服。像发包中断(TOK)的handler可能要调用netif_wake_queue,这个函数会调用驱动接口start_xmit发包,而start_xmit()我在设计的时候,是默认硬中断已打开,如果不推迟到BH里调用它,而是放在do_IRQ的action里,那start_xmit里就要做irq_save了,但我的irq_save的实现又是依赖于task_struct的,这样一来,就必须进程才能调用网卡驱动,内核本身不能。
【BH没那么糟】
“一旦一个bh在执行,其它所有的bh要等它执行完。” 开始看到这种话,觉得是bh的缺点,现在不禁一笑————不等着能怎么办呢?2.0时期的linux,cpu只有一个核。
tasklet只是多核版本的bh,真正了不起的是softirq才对,那些驱动程序员处理各种并发时,应该很有存在感。
【任务队列的效率似乎不高】
我开始觉得bh太简单,想实现的是task queue。(我其实想做softirq的,可惜我的内核不支持多核)
但发现用链表实现bh机制太笨重。我几乎快想好了,就是临界区的时候,把head和tail复制到局部变量,然后遍历。这样BH过程中随便硬中断怎么mark_bh()都行。
但还是不行,因为mark_bh()(不知道是它是什么函数,就用mark_bh指代吧)的时候,这些tq_struct会从inactive队列,跑到active队列。遍历会失败。
看linux的源码,它竟然是每执行完一个tq_struct,就直接从队列里删掉。那估计它mark_bh()的时候,也是新生成一个tq_struct插进去。有点儿恐怖,跟BH相比,重的像砖头。
2.6里它已经被work queue取代了,由一个线程负责,可以延时,休眠的。
【BH更像一个hook】
觉得bh更像一个“钩子”,在内核routine的某些点调用用户(通常是驱动程序员)注册的函数。要支持这种钩子,两个难点:1, 它全程基本上是开中断的,在次级中断里,用户又会注册一些钩子。我们面临一些变量的“并发”(某种意义上的)读写。第二点,就是内核设计着要对do_BH()前后,内核的流程很熟悉。我们在这里概述一下:
一个硬件中断的流程: 硬件自动保存断点; 软件SAVE_ALL; 进入DO_IRQ; DO_IRQ调用这个中断通道上注册的所有action; 执行SOFTIRQ(如果有的话); 执行完SOFTIRQ,DO_IRQ也就完了,routine流向ret_from_intr,内核在此处会瞄一眼,看是要回哪儿去。用户空间(ring 3)?那先调到ret_with_reschedue,因为硬中断引发的进程切换是再正常不过的了。仍旧是回到内核空间(ring 0)?那就那就直接跳到restore_all了。linux内核的原则是,内核态的中断不会引发进程调度。(2.4以前)
【linux的do_softirq源码分析】
这段do_softirq,我都是当做do_bh()去读的,竟然觉得每一句都读懂了。
所以下文有一些语病,像比mark_bh,朋友们就自动联想吧,我懒得去找softirq里它叫什么了。
---------------kernel/softirq.c-----
50 asmlinkage void do_softirq()
51 {
52 int cpu = smp_processor_id();
53 __u32 active, mask;
54
55 if (in_interrupt())
56 return;
57
58 local_bh_disable();
59
60 local_irq_disable();
61 mask = softirq_mask(cpu);
62 active = softirq_active(cpu) & mask;
63
64 if (active) {
65 struct softirq_action *h;
66
67 restart:
68 /* Reset active bitmask before enabling irqs */
69 softirq_active(cpu) &= ~active;
70
71 local_irq_enable();
72
73 h = softirq_vec;
74 mask &= ~active;
75
76 do {
77 if (active & 1)
78 h->action(h);
79 h++;
80 active >>= 1;
81 } while (active);
82
83 local_irq_disable();
84
85 active = softirq_active(cpu);
86 if ((active &= mask) != 0)
87 goto retry;
88 }
89
90 local_bh_enable();
91
92 /* Leave with locally disabled hard irqs. It is critical to close
93 * window for infinite recursion, while we help local bh count,
94 * it protected us. Now we are defenceless.
95 */
96 return;
97
98 retry:
99 goto restart;
100 }
首先,第52行int cpu = smp_processor_id()获取当前是在哪个核,softirq在设计上,为每个核分配了专属的mask和active,每个核只需要关心自己本地的softirq就行了。
借用网上的一段话:整个softirq机制的设计与实现中自始自终都贯彻了一个思想:“谁触发,谁执行”(Who marks,Who runs),也即触发软中断的那个CPU负责执行它所触发的软中断,而且每个CPU都由它自己的软中断触发与控制机制。
接着local_bh_disable()。我们知道,执行do_softirq过程中(几乎)是全程开中断的,我们把发生于这个过程中的硬中断叫“次级中断”,中断的routine都是千篇一律的,当次级中断的routine走到do_IRQ里的do_softirq时,我们要阻止它进入。这就是local_bh_disable()的作用。
紧接着是local_irq_disable()。因为接下来要操作mask,active这两个关键变量,次级中断里可能会竞争访问(通过mark_bh()),没有别的办法,只能关掉硬中断。
到71行,再local_irq_enable(),就进入do_irq经典的情景了,开中断干活儿。
注意61,62行,我们把active和mask两个全局变量复制到局部变量,仍旧是怕次级中断。但硬中断不能再关下去了,只好先复制下来:次级中断,active和mask变量就交给你随便折腾了,我先处理完这一批active bits再说。
76~81行是执行active bits对应的action,代码很轻巧,不赘述。
到83行又local_irq_disable(),因为又要读全局变量active了,它反映自local_bh_disable以来,又产生的softirq。复制下来,循环处理。注意,上一批的active bits已经在69行给清掉了。
最后,第90行,很重要的一句:
local_bh_enable()。
系统从do_softirq出去时,硬中断从此就关了,直到iret。此时已接近这次中断routine的尾声。
中断当然要关,因为接下来的操作都是entry.S里非常脆弱的操作,像restore_all时,是不可能允许中断发生的。
PS: 等测试过了,我再把我的源码贴出来,因为网卡驱动没写完,内核现在还编译不了。 笔记很到位,期待更新:victory: BH是什么鬼。。。。。。。。。。。。 bottom half papaya=? 一种os? papaya的设计思路跟linux有什么不一样的地方?能否简单的介绍一下?
缺乏这些背景的话,不太好理解楼主的某些design point。 karma303 发表于 2016-06-15 11:44 static/image/common/back.gif
读了两天的linux代码,一点点心得跟大家分享,高手轻拍~
【感觉到对BH的需求了】
像发包中断(TOK)的handler可能要调用netif_wake_queue
你调用netif_wake_queue干嘛?
另外硬中断里很多地方是开中断的 本帖最后由 mordorwww 于 2016-06-18 22:24 编辑
karma303 发表于 2016-06-15 11:44 static/image/common/back.gif
读了两天的linux代码,一点点心得跟大家分享,高手轻拍~
【感觉到对BH的需求了】
【任务队列的效率似乎不高】
我开始觉得bh太简单,想实现的是task queue。(我其实想做softirq的,可惜我的内核不支持多核)
但发现用链表实现bh机制太笨重。我几乎快想好了,就是临界区的时候,把head和tail复制到局部变量,然后遍历。这样BH过程中随便硬中断怎么mark_bh()都行。
但还是不行,因为mark_bh()(不知道是它是什么函数,就用mark_bh指代吧)的时候,这些tq_struct会从inactive队列,跑到active队列。遍历会失败。
看linux的源码,它竟然是每执行完一个tq_struct,就直接从队列里删掉。那估计它mark_bh()的时候,也是新生成一个tq_struct插进去。有点儿恐怖,跟BH相比,重的像砖头。
2.6里它已经被work queue取代了,由一个线程负责,可以延时,休眠的。
你这什么鬼,work queue是由内核线程处理的。
难道新的内核里软中断完全都是在内核线程里做的?线程化也有她的不足 karma303 发表于 2016-06-15 11:44 static/image/common/back.gif
读了两天的linux代码,一点点心得跟大家分享,高手轻拍~
【感觉到对BH的需求了】
谁触发,谁执行
对于网卡和网络协议栈来讲,早就不是这样的了
页:
[1]