|
大家好. bh应该是开中断运行而且没有进程上下文, 我想问一下bh被中断时它的现场保存在何处? 谢谢 !
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
当然是在被中断之前进程的内核栈中
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
在2.6kernel中,softirq,包括BH,只在ksoftirqd的上下文中运行, 已经不在中断返回,调度时运行了, 因此,2.6中它的现场只能是ksoftirqd内核线程的上下文中。
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
感谢两位老大的解答. 我还是有一点疑惑, 请指点.
do_IRQ ==> irq_exit() ==> do_softirq() /* * We restart softirq processing MAX_SOFTIRQ_RESTART times, * and we fall back to softirqd after that. * * This number has been established via experimentation. * The two things to balance is latency against fairness - * we want to handle softirqs as soon as possible, but they * should not be able to lock up the box. */ #define MAX_SOFTIRQ_RESTART 10
我感觉应该是在中断退出前先最多执行softirq MAX_SOFTIRQ_RESTART次, 然后如果还有softirq需要处理才由ksoftirqd来处理. 因此前面几个softirq的处理是没有进程上下文的, 如果解释为将中断现场保存在被中断之前进程的内核栈中还是可以解释通的.
另外linux对中断嵌套如何处理 ? 中断处理时, 低优先级的中断被高优先级的中断中断, 好像中断现场也只有放在最开始被中断的进程的内核栈中.
有错误的地方请大家指正, 谢谢!
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
你说的很对,我没有注意到irq_exit(),谢谢。
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
还有一个问题, 请解答. 谢谢. 对于一部分没由ksoftirqd执行的softirq, 以net_rx_action为例, 由于没有进程上下文, 是不可以被抢占的, 而对于由ksoftirqd执行的softirq, 则可以被抢占. 对于2.6.5 net_rx_action() 去掉preempt_enable() , 而在ksoftirqd() 中加入preempt_disable(); 可不可以这样解释 ?
while (local_softirq_pending()) { /* Preempt disable stops cpu going offline. If already offline, we'll be on wrong CPU: don't process */ preempt_disable(); if (cpu_is_offline((long)__bind_cpu)) goto wait_to_die; do_softirq(); preempt_enable(); cond_resched(); }
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
应该是吧.
但是我有点迷惑,networking部分难道真的不能preempt吗? 会引起状态不一致?包延迟?包乱序?
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
可能是考虑时延吧, 毕竟有一些拥塞算法是不断的采集时间, 来作为动作依据的, 如果被抢占, 下一次被调度运行的时间就说不准了, 可能会导致误操作.
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
>> 但是我有点迷惑,networking部分难道真的不能preempt吗?
为何得出如此结论,networking怎么不可以抢占?
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
>为何得出如此结论,networking怎么不可以抢占? 接收处理被preempt_disable()/preempt_enable()包围.
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
系统调用部分是可以被抢占的, 但softirq部分应该考虑RTT的测量等等问题. 如果被抢占可能会得出错误结论, 如超时重传的计算会由于RTT的不准确而不准确, 从而影响tcp的性能.
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
这个问题应该分为两个部分分析: 第一:一部分softirq是isr处理之后调用的,对于这部分代码,由于是灾底半处理中运行,必须是是在运行进程系统调用之前返回的.所以实际上preempt_disable(); preempt_enable();代码对于他们来说是没有意义的.
第二:一部分softirq是在ksoftirqd的内核线程运行的,因为这个相当于运行在进程的内核空间,softirq都有这样一个需求.由于软中断都是对中断上半部的继续,所以这些工作都需要尽快的完成.所以在softirqd运行的时候,禁止了preempt,这样就可以保证softirq运行完之后才会调度下一个进程,因为softirq里面的所有函数都不会睡眠.
我想2.6中网络代码中的preempt_enable/disable移到softirqd调用的地方原因是这样的.
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
又見到getmoon兄了 請教, 在2.5/2.6里挂入softirq對列的時機都有哪些? 我覺得在irq中挂入的softirq應該在irq_exit()中都處理掉了(2.6 has not the constant of "MAX_SOFTIRQ_RESTART"),那麼還有誰的softirq會在ksoftirqd里執行呢?(網絡的先不說)
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
/////////////////////////////////////////// 在2.6kernel中,softirq,包括BH,只在ksoftirqd的上下文中运行, 已经不在中断返回,调度时运行了, 因此,2.6中它的现场只能是ksoftirqd内核线程的上下文中。 //////////////////////////////////////////
我觉得不对,请看看local_bh_enable()中的代码:if(unlikely(!in_interrupt() && local_softirq_pending())) invoke_softirq();而invoke_softirq()被定义为do_softirq();于是softirq可在任意线程上下文运行!
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
处理的时机有很多地方, 1.irq_exit 2.local_bh_enable 3.softirqd 4.any task kernel stack (except in_interrupt)can call the do_softirq
在irq_exit中并没有都处理调,例如:来了3次中断,放入3个结构到队列,但是只调用了一次do_softirq . (在执行softirq时发生了中断).那么另外2次什么时候处理呢,这就引入了ksoftirqd .2.6(具体不知道那个版本加入的)的代码中又在local_bh_enable加入了调用do_softirq , 因为这个函数调用是,就相当于允许softirq运行,他们之间不需要互斥了.所以也可以安排一次do_softirq . 后面的加入都是为了提高do_softirq调用的次数,提供性能.
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
static void tasklet_action(struct softirq_action *a) { struct tasklet_struct *list;
local_irq_disable(); list = __get_cpu_var(tasklet_vec).list; __get_cpu_var(tasklet_vec).list = NULL; local_irq_enable();
while (list) { struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) { if (!atomic_read(&t->count)) { if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) BUG(); t->func(t->data); tasklet_unlock(t); continue; } tasklet_unlock(t); }
local_irq_disable(); t->next = __get_cpu_var(tasklet_vec).list; __get_cpu_var(tasklet_vec).list = t; ////////////////////////////////////////////////////////////////// __raise_softirq_irqoff(TASKLET_SOFTIRQ); ////////////////////////////////////////////////////////////////// local_irq_enable(); } } 這裡為什麼最後要用__raise_softirq_irqoff()把TASKLET_SOFTIRQ重新置位?這樣不會白跑一趟嗎?
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable();
list = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = NULL;
local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {//试图锁住该tasklet
if (!atomic_read(&t->count)) {//该tasklet没有被disable
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
//如果到达这里,说明t没有执行,或者因为没有锁住,或者因为该tasklet被disable
//因此需要将t,及t后面的tasklet重新加入队列,重新调度。
local_irq_disable();
t->next = __get_cpu_var(tasklet_vec).list;//重新加入队列
__get_cpu_var(tasklet_vec).list = t;
__raise_softirq_irqoff(TASKLET_SOFTIRQ);//重新调度
local_irq_enable();
}
}
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
2.6.5还有MAX_SOFTIRQ_RESTART呀.
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
/////////////////////////////////////////// 在2.6kernel中,softirq,包括BH,只在ksoftirqd的上下文中运行, 已经不在中断返回,调度时运行了, 因此,2.6中它的现场只能是ksoftirqd内核线程的上下文中。 //////////////////////////////////////////
我觉得不对,请看看local_bh_enable()中的代码:if(unlikely(!in_interrupt() && local_softirq_pending())) invoke_softirq();而invoke_softirq()被定义为do_softirq();于是softirq可在任意线程上下文运行!
呵呵,现在看当然不对,简直是不对之极。
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
这个问题应该分为两个部分分析: 第一:一部分softirq是isr处理之后调用的,对于这部分代码,由于是灾底半处理中运行,必须是是在运行进程系统调用之前返回的.所以实际上preempt_disable(); preempt_enable();代码对于他们来说是没有意义的.
第二:一部分softirq是在ksoftirqd的内核线程运行的,因为这个相当于运行在进程的内核空间,softirq都有这样一个需求.由于软中断都是对中断上半部的继续,所以这些工作都需要尽快的完成.所以在softirqd运行的时候,禁止了preempt,这样就可以保证softirq运行完之后才会调度下一个进程,因为softirq里面的所有函数都不会睡眠.
我想2.6中网络代码中的preempt_enable/disable移到softirqd调用的地方原因是这样的.
道理是这样的, 但是我始终觉得,单单从代码上说,网络部分理论上是可重入的,是可以preempt的,你说呢?
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
处理的时机有很多地方, 1.irq_exit 2.local_bh_enable 3.softirqd 4.any task kernel stack (except in_interrupt)can call the do_softirq
不看不知道,一看吓一跳。 看来只是将schedule()里对do_softirq()的调用转移到ksoftirqd中去了,呵呵。
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
bx_bird , wheelz 已经回答了你的问题了.
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
还有一个问题, ksoftirqd的nice 是19, 如果允许它在运行softirq时可以被抢占, 那么大多数情况下应该被别的进程抢占, 网络部分的softirq比较漫长, 不像时钟部分比较短小, 考虑比较极端的情况, 如乱序到达执行n条防火墙规则, 路由查找, slowpath等等, 如果禁止被抢占, 对系统的实时性有很大影响, 如果不禁止被抢占又会影响协议栈的计时等等方面的准确性. 我个人认为应该单独启动一个内核线程来处理网络部分的softirq, 此线程应该不禁止具有实时优先级的进程抢占, 而又比普通的进程优先级高.
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
to wheelz:
运行情况应该是这样的: 在同一个CPU上,网络代码是不能嵌套的,就是说在同一个CPU上,必须一次做完完整的网络调用. 才能重头开始进行下一次.
在不同的CPU下, 几个CPU可以同时调用网络部分的代码, 因为spin_lock机制保证了代码同时执行的安全性.
不知道咱们对重入的理解一样不一样,我觉得重入就应该能够支持嵌套,所以按照我的理解,严格的说网络部分的代码是不可重入的.
至于能不能preempt , 从理论上来讲是可以的.
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
严格意义说,确实不能叫可重入,至少有一些全局变量应该说不符合理论上的“可重入”,但是,
>在同一个CPU上,网络代码是不能嵌套的,就是说在同一个CPU上,必须一次做完完整的网络调用. 才能重头开始进行下一次.
???为什么不能? spin_lock()都是隐含一个preempt_disable()的, 只要不在临界区嵌套,和SMP就是一样的,应该没有问题 (read copy update如何?需要看一下代码,呵呵)。 你说呢?
>在不同的CPU下, 几个CPU可以同时调用网络部分的代码, 因为spin_lock机制保证了代码同时执行的安全性.
//nod
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
asmlinkage void do_softirq(void) { int max_restart = MAX_SOFTIRQ_RESTART; __u32 pending; unsigned long flags;
//判断之前是否是在中断中(硬件中断和软件中断),如果是,返回. if (in_interrupt()) return;
local_irq_save(flags);
pending = local_softirq_pending();
if (pending) { struct softirq_action *h;
local_bh_disable(); //申明现在进入软中断处理.
从上面的代码可以看出,单软中断执行的时候,例如ksoftirqd执行软中断函数,调用之前都会利用local_bh_disable来申明已经进入软中断处理. 如果此时来了一个中断,irq_exit中可以看到判断是否in_interrupt来决定是否需要调用do_softirq . 可以看到很多调用do_softirq之前都进行了in_interrupt的判断.
并且do_softirq函数自己也做了一下判断,防止重入. 那么什么时候继续之前被打断的softirq函数呢, 在entry.s中,系统在中断返回之前判断如果之前是在内核,继续回到该进程,按照堆栈排列,改好回到之前的softirq函数.
|
|
Re: 请教: bh被中断时它的现场保存在何处? | |
我知道softirq有自己的防嵌套机制,我的问题不在于探究现在的内核是如何防止嵌套的,因为现有的内核有诸多的因素在防止TCP/IP的接收处理preemptable,我的问题是,在现有的代码基础上,或者作很少的改动,能不能做到preemptable?我认为现有的代码是可以的,不存在什么技术障碍,除非有语义上的考虑,比如考虑到包的延迟和乱序。
|