为什么中断上下文不可以休眠

不了解2.6,但是在2.4中,中断“不能”睡眠。如果你非要在中断中睡眠,则有可能造成各种各样的问题,也可能什么事也没有。这个问题讨论了很多次,你可以在论坛中搜索“睡眠”关键字。 
我这里再多说一些自己的理解: 

2.4内核是非抢占式的,除非自己原意,一个进程进入内核态后不会被意外替换为另一个进程。大家都默认这样的规则,很多内核函数就可以简化设计。“在中断中睡眠”打破了这一假定,因此可能会引起不可知的问题。我说“可能”是意思是,中断睡眠不一定肯定出问题,比如当进程正在用户空间时发生这种可以睡眠的中断,我认为不会造成什么严重后果。 
但是如果进程调用了一个系统调用并进入内核态时发生了这个中断,则有可能造成一些问题。 
假设在中断发生前该进程正在修改某个内核数据结构,假设该结构是一个list吧,由于内核认为这个数据结构不存在几个kernel control path会同时修改的可能,就没有加同步保护。正常情况下中断结束后控制权会回来,完成剩下的操作,保证数据完整性。但如果中断睡眠了,另一个进程获得cpu,假设该新进程也恰巧要修改同一个内核数据(前面那个list),就会发生大家都了解的事情:race condition。比如要往这个list中添加一个新节点,如果不保护,后果是什么谁也说不清。 

2.6我没有研究过,也许中断睡眠不再是问题了。但无论怎样,大多数实际的应用都不会在中断中获取资源,一般只会在中断中释放资源。中断是一个通知机制,通知进程应该干什么,不应该做其它的事情。也许中断也可以做更复杂的事情,我说的不过是自己的浅见。

 

 

 

http://linux.chinaunix.net/bbs/viewthread.php?tid=902033&extra=page%3D1%26amp%3Bfilter%3Ddigest 
CU上曾经讨论过, 还不错.

 

 

 

 

 

 

看邻居帖子,大家回答的问题不大。但也有不同的回答和解释。有点乱。

有必要讨论,得出固定的结论。

 

确实,大家说了很多,有些地方还是不是很清楚。
希望高手们能给出正解

 

 

 

QUOTE:
原帖由  思一克 于 2007-6-27 09:19 发表于 1楼  
看邻居帖子,大家回答的问题不大。但也有不同的回答和解释。有点乱。

有必要讨论,得出固定的结论。





其实这只是一个设计上的问题, 而并不是强制的. 只是针对Linux内核而言, 这是规矩. 
LKD2上面说, 切换出去之后, 何时才能调度回来? 就是中断回来之后可能不会回到之前所依俯的那个进程了. 
中断不能睡眠的的最大好处就是可以简化内核的设计. 如果说中断随时可以睡眠的话, 那么就必须考虑很多其它方面的事情, 比如睡眠之后又出现了
同一IRQ号的中断又该怎么办, 等等. 
其实, 如果自己能把握好, 中断中睡眠也是可以的. 但是必须能够保证这段代码具有足够的安全性与可靠性. 
这是我的理解.

 

 

 

呵呵,我最喜欢这种讨论了。先来献丑了,说说我的看法。
先把中断处理流程给出来

  1. 1.进入中断处理程序--->2.保存关键上下文---->3.开中断(sti指令)--->4.进入中断处理程序的handler--->5.关中断(cli指令)---->6.写EOI寄存器(表示中断处理完成)---->7.开中断。
复制代码


硬中断:
对应于上图的1、2、3步骤,在这几个步骤中,所有中断是被屏蔽的,如果在这个时候睡眠了,操作系统不会收到任何中断(包括时钟中断),系统就基本处于瘫痪状态(例如调度器依赖的时钟节拍没有等等……)

软中断:
对应上图的4(当然,准确的说应该是4步骤的后面一点,先把话说保险点,免得思一克又开始较真 )。这个时候不能睡眠的关键是因为上下文。
大家知道操作系统以进程调度为单位,进程的运行在进程的上下文中,以进程描述符作为管理的数据结构。进程可以睡眠的原因是操作系统可以切换不同进程的上下文,进行调度操作,这些操作都以进程描述符为支持。
中断运行在中断上下文,没有一个所谓的中断描述符来描述它,它不是操作系统调度的单位。一旦在中断上下文中睡眠,首先无法切换上下文(因为没有中断描述符,当前上下文的状态得不到保存),其次,没有人来唤醒它,因为它不是操作系统的调度单位。
此外,中断的发生是非常非常频繁的,在一个中断睡眠期间,其它中断发生并睡眠了,那很容易就造成中断栈溢出导致系统崩溃。

如果上述条件满足了(也就是有中断描述符,并成为调度器的调度单位,栈也不溢出了,理论上是可以做到中断睡眠的),中断是可以睡眠的,但会引起很多问题.例如,你在时钟中断中睡眠了,那操作系统的时钟就乱了,调度器也了失去依据;例如,你在一个IPI(处理器间中断)中,其它CPU都在死循环等你答复,你确睡眠了,那其它处理器也不工作了;例如,你在一个DMA中断中睡眠了,上面的进程还在同步的等待I/O的完成,性能就大大降低了……还可以举出很多例子。所以,中断是一种紧急事务,需要操作系统立即处理,不是不能做到睡眠,是它没有理由睡眠。

好了,罗嗦了一大堆,大家见仁见智,不要骂人就好。

 

 

QUOTE:
原帖由  思一克 于 2007-6-27 10:21 发表于 6楼  
那么LINUX有在中断中睡眠的地方吗



据我所知没有。但有没有人在他自己写的驱动程序的中断处理handler中睡眠就不知道了

任何os,不管是分时os,还是实时os,不管是微内核,还是巨内核,在ISR中都不能进行进程切换。因为ISR不属于任何进程,而切换只能发生在进程上下文中。虽然ISR在执行过程中要使用进程的系统堆栈,但那只是借用,堆栈并不属于isr,而是属于进程。
也可以从优先级角度来理解。任何进程,不论其优先级多高,也不能高过isr,所以不能剥夺isr对cpu的占有权去运行进程
QUOTE:
原帖由  gta 于 2007-6-27 13:29 发表于 8楼  
任何os,不管是分时os,还是实时os,不管是微内核,还是巨内核,在ISR中都不能进行进程切换。因为ISR不属于任何进程,而切换只能发生在进程上下文中。虽然ISR在执行过程中要使用进程的系统堆栈,但那只是借用, ...




呵呵,Solaris的低优先级中断是可以睡眠的,时钟中断也可以睡眠。尽管系统会尽量保证中断线程不去睡眠。例如,磁盘驱动的ISR里使用DRIVEDR mutex, 实际上系统把它当作自适应锁,当获取锁失败时,它先看其它处理器上有没有锁的拥有者在运行,如果有,就SPIN,没有就去睡眠,睡眠前,会把打断的内核线程恢复运行,保存自己的上下文。
QUOTE:
原帖由  zx_wing 于 2007-6-27 10:09 发表于 4楼  
呵呵,我最喜欢这种讨论了。先来献丑了,说说我的看法。
先把中断处理流程给出来

1.进入中断处理程序--->2.保存关键上下文---->3.开中断(sti指令)--->4.进入中断处理程序的handler--->5.关中 ...



写得很好。赞一个。

不过中断睡眠与否只是设计上的问题,不同的系统是不一样的设计。

看看我自己机器上注册的ISR, IPL是10以下的都可以睡眠,里面包括了显卡,磁盘,网卡的常见外设的中断处理:

bash-3.00# mdb -k
Loading modules: [ unix genunix specfs dtrace uppc pcplusmp scsi_vhci ufs ip hook neti sctp arp usba uhci nca lofs zfs random audiosup sppp crypto ptm ipc ]
> ::interrupts
IRQ  Vect IPL Bus    Trg Type   CPU Share APIC/INT# ISR(s) 
1    0x41 5   ISA    Edg Fixed  0   1     0x0/0x1   i8042_intr
6    0x44 5   ISA    Edg Fixed  0   1     0x0/0x6   fdc_intr
9    0x81 9   PCI    Lvl Fixed  0   1     0x0/0x9   acpi_wrapper_isr
12   0x42 5   ISA    Edg Fixed  0   1     0x0/0xc   i8042_intr
14   0x40 5   ISA    Edg Fixed  0   1     0x0/0xe   ata_intr
15   0x43 5   ISA    Edg Fixed  0   1     0x0/0xf   ata_intr
16   0x82 9   PCI    Lvl Fixed  0   1     0x0/0x10  nv_intr
21   0x20 1   PCI    Lvl Fixed  0   4     0x0/0x15  uhci_intr, uhci_intr, uhci_intr, ehci_intr
22   0x83 9   PCI    Lvl Fixed  0   1     0x0/0x16  audiovia823x_intr
23   0x60 6   PCI    Lvl Fixed  0   1     0x0/0x17  gem_gld_intr
208  0xd0 14         Edg IPI    all 1     -         kcpc_hw_overflow_intr
209  0xd1 14         Edg IPI    all 1     -         cbe_fire
224  0xe0 15         Edg IPI    all 1     -         apic_error_intr
QUOTE:
原帖由  zx_wing 于 2007-6-27 10:09 发表于 4楼  
呵呵,我最喜欢这种讨论了。先来献丑了,说说我的看法。
先把中断处理流程给出来

1.进入中断处理程序--->2.保存关键上下文---->3.开中断(sti指令)--->4.进入中断处理程序的handler--->5.关中 ...


我觉得主要是调度的问题,引入中断线程问题就基本解决了。
LINUX上中断中不能睡眠(调用schedule,直接或间接)和“被中断的进程和中断本身无关系”无关。

前面有人说“被中断的进程和中断本身无关系”是不能睡眠的原因。我不理解,觉得说法不对。
QUOTE:
原帖由  思一克 于 2007-6-27 16:43 发表于 12楼  
LINUX上中断中不能睡眠(调用schedule,直接或间接)和“被中断的进程和中断本身无关系”无关。

前面有人说“被中断的进程和中断本身无关系”是不能睡眠的原因。我不理解,觉得说法不对。




这没什么不对,中断和自陷在执行的路径上,没有丝毫不同。如果说什么“中断是因为没有进程上下文,只有中断上下文”而不能睡眠的话;那么自陷同样“没有进程上下文,只有自陷上下文”。

但是自陷却可以睡眠,最明显的就是缺页异常,睡眠再正常不过了。就是因为当前发生的异常和当前进程有关系,把当前进程睡眠掉并不冤枉它。
QUOTE:
原帖由  Solaris12 于 2007-6-27 13:58 发表于 9楼  



呵呵,Solaris的低优先级中断是可以睡眠的,时钟中断也可以睡眠。尽管系统会尽量保证中断线程不去睡眠。例如,磁盘驱动的ISR里使用DRIVEDR mutex, 实际上系统把它当作自适应锁,当获取锁失败时,它先看其 ...


ISR(interrupt service routine)是不能睡眠的,我觉得你说的那个不是ISR,是IST(interrupt service thread).IST平时处于睡眠态,发生中断后,cpu执行ISR,在ISR中唤醒IST,中断服务的主体在IST中完成。引入IST后,一来可以减小内核体积,二来可以减小不可剥夺窗口,增强调度的实时性。WIN CE就是这样做的。不知Solaris的做法是否与此类似。请Solaris12兄指教

QUOTE:
原帖由  gta 于 2007-6-27 17:52 发表于 14楼  
呵呵,Solaris的低优先级中断是可以睡眠的,时钟中断也可以睡眠。尽管系统会尽量保证中断线程不去睡眠。例如,磁盘驱动的ISR里使用DRIVEDR mutex, 实际上系统把它当作自适应锁,当获取锁失败时,它先看其 ...




退一万步说,假如中断可以睡眠,那么也决不是低优先级中断可以睡眠;而是当前进程为低优先级进程时能睡眠。不然那些进程调度算法不白写了?高优先级的进程有什么用?只要碰到个低优先级中断就被调度出去了。

虽然我不知道Solaris长的什么摸样。
自陷就是TRAP,LINUX的EXCEPTION-异常,有进程上下文。

TRAP,相当于一个软中断(INT 1, INT 3, 断点,单步等),和软中断调用的系统调用(INT 21, INT 80)几乎一样,属于当前进程,进入内核使用进程的内核栈。唯一不同的是,系统调用的软中断在用户程序中的位置相对固定,而TRAP相对不固定。

假定INT 0是被0除TRAP,你在USER中执行A = 1/0和执行INT 0是一样的,而INT 0 和INT 80也是一样的。用户程序执行INT 80有进程CONTEXT, 执行INT 0也一样有进程CONTEXT.

可以看出,TRAP(比如INT 0)的PROCESS CONTEXT和执行系统调用INT 80后的PROCESS CONTEXT是一样的。所以TRAP中如果睡眠了,是可以回来的。

而中断没有进程上下文。调度后无法回来. 

如果不想回来了, 中断中也是可以睡眠的.

正因为TRAP和中断有如此不同,LINUX系统中才对于INTERRUPT 和 EXCEPTION的处理才是非常不同。

我理解的前面的说的中断和进程无关大概就是这个意思(?)

QUOTE:
原帖由  gta 于 2007-6-27 17:52 发表于 14楼  
ISR(interrupt service routine)是不能睡眠的,我觉得你说的那个不是ISR,是IST(interrupt service thread).IST平时处于睡眠态,发生中断后,cpu执行ISR,在ISR中唤醒IST,中断服务的主体在IST中完成。引入IST后,一来可以减小内核体积,二来可以减小不可剥夺窗口,增强调度的实时性。WIN CE就是这样做的。不知Solaris的做法是否与此类似。请Solaris12兄指教



Cool!

你说的很准确。的确是你说的情况。

举例来说, 写网卡驱动程序,我们通常把中断服务函数说成ISR。

但在Solaris里,每个级低优先级中断都有个IST,中断服务线程。


我说的ISR是驱动程序的中断服务函数,即下面的e1000g`e1000g_intr,而你说的是下面调用栈里的_interrupt。

具体机制和你说的一样,下面的调用栈是我观察网卡驱动e1000g调用栈得到的:

[6]> ffffff001ddabc80::threadlist -v
            ADDR             PROC              LWP CLS PRI            WCHAN
ffffff001ddabc80 fffffffffbc24b30 fffffffec8551170   0 165                0
  PC: thread_start    THREAD: thread_create_intr()
  stack pointer for thread ffffff001ddabc80: ffffff001ddabbc0
    e1000g`e1000g_intr+8(2)
    av_dispatch_autovect+0x78()
    dispatch_hardint+0x2f()
    switch_sp_and_call+0x13()
    do_interrupt+0xa0()
    _interrupt+0xba()


QUOTE:
原帖由  motalelf 于 2007-6-27 19:35 发表于 15楼  
退一万步说,假如中断可以睡眠,那么也决不是低优先级中断可以睡眠;而是当前进程为低优先级进程时能睡眠。不然那些进程调度算法不白写了?高优先级的进程有什么用?只要碰到个低优先级中断就被调度出去了。

虽然我不知道Solaris长的什么摸样。




Solaris的中断服务线程有独立的调度优先级,一般是160-169。其中时钟中断最高,是169。


在Solaris里时钟中断是最高的调度优先级。

当然,高优先级中断一来,就立即执行,根本不涉及到调度问题。

我曾经给Solaris调度器提交过一个bug,其中就涉及到时钟中断睡眠的情形 

中断sleep, preempt和 实时性瓶颈介绍.



QUOTE:
原帖由  zx_wing 于 2007-6-27 10:09 发表于 4楼  
呵呵,我最喜欢这种讨论了。先来献丑了,说说我的看法。
先把中断处理流程给出来

1.进入中断处理程序--->2.保存关键上下文---->3.开中断(sti指令)--->4.进入中断处理程序的handler--->5.关中 ...



里面很多说法不是很同意, 个人认为中断处理handler不能sleep原因应该不是上面那些.

我们都是从理论讲下面这些问题, 因为linux在很多地方做了保护, 所以直接sleep或者schedule()会导致内核异常.

首先分清楚, 我们讨论的是不能sleep, 而不是不能preempt.

1. 毫无疑问, 在关中断的时候不能sleep, 这点大家都知道, 因为时钟中断无法触发. 但不是所有情况下, 在关中断时sleep都会导致系统死掉, 在SMP的情况下, 可能系统不会死掉.

2.  中断的handler能否sleep? 
     这其实和"中断没有自己的上下文"无关. CPU没有关中断, 中断有自己的上下文, 中断的上下文就是抢占的任务A的上下文.
     和栈溢出也没有关系, 现在的中断都是可以嵌套的, 如果中断sleep只会让后面的中断抢占其他任务, 根本不存在栈溢出问题, 不过现在内核的4K中断单独栈会有问题. 这会导致栈被破坏.

假设中断sleep了, 在调度的时候, 内核将中断的CS:eip和SS:esp保存在被抢占任务A的thread_info中, 当任务A被重新唤醒的时候, 任务A从中断的CS:eip开始执行, 这也能正常执行下去, 中断执行完后, 从ret_from_intr中返回. 可以恢复任务A的抢占前的场景. 

Linux内核要实现成这样, 必须解决下面问题:
    中断sleep会增加普通任务的不确定性, 普通任务执行的时间, 实时性都得不到保障.
    和中断共享中断号的中断会受到影响, 现在的内核设置了INPROGRESS标志.
    中断因为借用了被抢占任务的上下文, 所以中断的处理受到任务上下文属性的限制.
    等等很多其他问题, 总之, 中断sleep会导致被抢占任务的不确定性, 并可能导致其他中断受影响.

总结:
    异步异常(中断)handler不是没有上下文, 而是没有固定的上下文,  如果使用被抢占的任务作为上下文, 一,自身的处理无法得到实时保障,导致系统不确定性, 二,任务受到影响.

如何解决:
    给中断handler提供固定的内核线程上下文!!
    这样, 中断不能sleep, 但中断的handler可以sleep! 
    为每个中断号创建一个内核任务, 中断入口函数do_irq只是唤醒相应的中断任务, 中断任务去执行相应的handler.

好处: 
     提高了系统的实时性. 后面可以详细讲.
坏处:
     降低了中断, 软中断的实时性, 所以不是所有的中断handler都可以在固定内核任务上下文中处理. 一般来说, 时钟中断必须保证其实时性, 所以留在中断上下文中.

  1. 介绍: Linux系统的实时性瓶颈在哪里??
  2.     一个实时性系统, 必须保证: 系统中优先级高的任务, 被唤醒后, 在很小的可控的延时内, CS:eip指令得到执行.
  3.     一个好的实时性系统, 必须保证: 系统中的所有等待运行的任务, 可以在一个固定的可接受的延时内, CS:eip指令得到执行.
  4.     这些延时包括 中断响应延时, 中断处理延时, 调度响应和调度处理延时.
复制代码


试想现有的系统, 一个任务可能在以下上下文中被唤醒:
1. 中断上下文, 如dma数据传输完成等等设备驱动.

        中断上下文唤醒任务后, 任务被加入到running队列, 返回, 继续执行中断, 中断执行完成后, 执行软中断, 中间可能会出现中断嵌套. 直到最后ret_from_intr, 才会判断是否需要抢占当前任务. 然后调用schedule(). 从队列被加入running队列到schedule()函数真正开始执行, 这段时间是中断处理延时+调度响应延时. 

  如何缩短这部分延时和我们前面讨论的东西很有关.
    正是因为中断,软中断不能preempt, 不能sleep, 导致了系统的实时性变差. 而在这些时间中, 中断handler和软中断handler消耗绝大部分时间. 
    设想一下, 如果中断handler和软中断handler放在专门的内核任务中执行, 中断handler中唤醒任务A, wakeup中通过优先级判断handler任务是否需要被任务A抢占, 如果需要, 设置handler任务的NEED_RESCHEDULE标志, 调用resched_task()可以马上进入schedule(), 其中的延时非常小, 而且非常稳定. 当然, 可能wakeup后, 立即有中断到来, 但因为中断执行路径变得非常短, 只是唤醒响应的handler任务, 可能只需要100行以内的代码.所以这些时间可以忽略不计. 这种做法可以极大提高系统的实时性.
    
2. 软中断上下文: 如定时器到期, 目的地是本地的报文送到socket层. 等等
        软中断的情况和中断类似, 可以通过将软中断线程化, 提高系统实时性.

3. 普通任务上下文, 如任务间通信等等.
   普通任务一旦在内核中执行了spin_lock(), 其他任务无法抢占这个CPU, 即使另外优先级高的任务不和它共享资源, 也无法及时得到调度. 当CPU变多, 内核代码变大的时候, 这个问题也变得非常突出, 所以spin_lock()也是一个影响系统实时性的设计. 如果将spin_lock变成可以抢占的锁, 会是怎样? 如果spin_lock可以抢占, 一旦任务B抢占了任务A, 而任务A执行了spin_lock(xxx), 恰好任务B也执行spin_lock(xxx), 必然导致内核死锁. 如果spin_lock()可以抢占, 且可以sleep(), 结果又会怎样. 任务B执行spin_lock(xxx)必然导致自己sleep, 这时任务A可以接着执行spin_unlock(xxx),唤醒任务B. 这时又有一个优先级反转问题需要解决, 假设任务B的优先级最高, 任务A最低, 任务C中等, B因为拿不到xxx锁而sleep, 重新调度, 结果调度到任务C执行, 任务B因此丧失了优先级优势, 这种情况在嵌入式系统中可能会导致很严重的问题. 所以任务A必须继承任务B的优先级, 重新调度的时候才能调度到任务A先运行.
   
--------------------------------------------------------------------------------------
上面这些系统问题的改正可以提高系统的实时性, 但整个内核的编程模型会变得更复杂. 这些东西就是Ingo Molnar在2005年实现的Realtime Patch中的核心思想, 但在2006年的Kernel Summit中, 引发了下面的讨论:

http://lwn.net/Articles/191782/

注意下面这段话.
The question was asked: why bother with sleeping locks? Making locks preemptible is seen by some as a way of papering over the real problem: long lock hold times. Why not simply fix those? The answer comes in a couple of parts:

    * Extensive efforts have been expended toward fixing lock problems for many years, and those efforts will continue into the future. The use of sleeping locks is not being used as an excuse to avoid fixing code which holds locks for too long.

    * Ensuring realtime response in the absence of preemptible locks requires auditing the entire body of kernel source - all eight million lines or so. That's a big job, and one which is hard to keep up with in an environment where nearly ten thousand lines of code are being changed every day. Sleeping locks reduce the audit requirements to a couple thousand lines - a much more tractable problem. For those who need realtime response, guaranteed, that is a good deal. 

因为众多的争议, 中断和软中断的线程化和spin_lock的可sleep化没有合入主流内核中.

如果realtime patch合并到主流内核中, 可以满足: 系统中优先级高的任务, 被唤醒后, 在很小的可控的延时内, CS:eip指令得到执行.

但不能保证低优先级的时延, 这正是CFS要解决的问题, 设想一下, 要让系统中的任何优先级的任务在一定的时间内得到执行, 必然要求等待时间长的任务优先得到执行, CFS就是用等待时间来作为调度的依据, 而优先级居次. 有时间在讨论CFS的实现.


QUOTE:
原帖由  rwen2012 于 2007-6-27 22:04 发表于 20楼  
http://hi.baidu.com/rwen2012/blo ... 28f1b4c9eaf4d0.html



简单看了一下前面的回复, 不能认同.

问题的焦点大家都集中在异步异常(中断)没有自己的上下文?

中断抢占了当前任务后, 可以通过current获得当前的task结构, 将当前任务的寄存器信息压入栈中, 替换成自己的eip和esp. 你完全可以理解为当前任务调用了handler()函数. 当然, 如果中断使用了自己的栈, 就会有问题!!

如果handler()函数调用了
schedule_timeout_interruptible(1000)
这样当前任务就被加入到定时器中,  定时器软中断到了1000ms就会把handler抢占的任务唤醒.

当然上述代码要正常工作, 要修改内核一些地方, 如schedule()里面的很多检查, 中断中的很多标志位和锁, entry.s中的很多判断.

只是想说明, 中断不能sleep, 不是因为没有上下文!

QUOTE:
原帖由  xiaozhaoz 于 2007-6-27 22:36 发表于 21楼  


简单看了一下前面的回复, 不能认同.

问题的焦点大家都集中在异步异常(中断)没有自己的上下文?

中断抢占了当前任务后, 可以通过current获得当前的task结构, 将当前任务的寄存器信息压入栈中, 替换成自己的eip和esp. 你完全可以理解为当前任务调用了handler()函数. 当然, 如果中断使用了自己的栈, 就会有问题!!

如果handler()函数调用了
schedule_timeout_interruptible(1000)
这样当前任务就被加入到定时器中,  定时器软中断到了1000ms就会把handler抢占的任务唤醒.

当然上述代码要正常工作, 要修改内核一些地方, 如schedule()里面的很多检查, 中断中的很多标志位和锁, entry.s中的很多判断.

只是想说明, 中断不能sleep, 不是因为没有上下文!




的确不光是上下文的问题,比如说还有调度的问题。

假设按你说的方式中断睡眠后,被打断线程也跟着睡眠显然会导致很多不可预知的问题。

中断产生是随机的,假设某次中断一个内核线程,而且按照你说的方案,这个线程的task结构就会被借用,线程就会去睡眠。被打断线程的优先级如果太低,那么它很难有机会再执行,某些情况下可能造成系统hang。如果临时提高被打断线程的优先级,那么又需要设计新的唤醒机制来保证阻塞同一锁上的高优先级的线程被先唤醒。同时,要实现临时提高被打断线程的优先级,又需要再锁的获取流程增加改变优先级的算法。


总之,没有考虑的问题还很多。

QUOTE:
原帖由  Solaris12 于 2007-6-27 23:05 发表于 22楼  



的确不光是上下文的问题,比如说还有调度的问题。

假设按你说的方式中断睡眠后,被打断线程也跟着睡眠显然会导致很多不可预知的问题。

中断产生是随机的,假设某次中断一个内核线程,而且按照你说的 ...



所见略同. 

19楼描述的就是这个问题.

而realtime Patch解决的也是这个问题.

PS. 最近在做一个多内核的项目, 包括solaris, 请问你们讨论solaris内核一般在哪里? OpenSolaris的mail list感觉不太热闹.

QUOTE:
1. 毫无疑问, 在关中断的时候不能sleep, 这点大家都知道, 因为时钟中断无法触发. 但不是所有情况下, 在关中断时sleep都会导致系统死掉, 在SMP的情况下, 可能系统不会死掉.

2.  中断的handler能否sleep?
     这其实和"中断没有自己的上下文"无关. CPU没有关中断, 中断有自己的上下文, 中断的上下文就是抢占的任务A的上下文.
     和栈溢出也没有关系, 现在的中断都是可以嵌套的, 如果中断sleep只会让后面的中断抢占其他任务, 根本不存在栈溢出问题, 不过现在内核的4K中断单独栈会有问题. 这会导致栈被破坏.

假设中断sleep了, 在调度的时候, 内核将中断的CS:eip和SS:esp保存在被抢占任务A的thread_info中, 当任务A被重新唤醒的时候, 任务A从中断的CS:eip开始执行, 这也能正常执行下去, 中断执行完后, 从ret_from_intr中返回. 可以恢复任务A的抢占前的场景.



看着脑子里嗡嗡作响啊,共鸣啊
LINUX 2。6 内核中断(软中断)自己使用自己的堆栈。
LINUX调度的单位是进程,中断中睡眠后因没有CONTEXT回不来。

还有2楼说的我同意,在中断中不让调度主要是设计问题。所以就LINUX具体看。

如果一定要改动系统,设计的中断可以调度,是可以办到的。
QUOTE:
原帖由  xiaozhaoz 于 2007-6-27 22:00 发表于 19楼  


里面很多说法不是很同意, 个人认为中断处理handler不能sleep原因应该不是上面那些.

我们都是从理论讲下面这些问题, 因为linux在很多地方做了保护, 所以直接sleep或者schedule()会导致内核异常.

首先分 ...


>>这其实和"中断没有自己的上下文"无关. CPU没有关中断, 中断有自己的上下文, 中断的上下文就是抢占的任务A的上下文.

“中断没有自己的上下文”,呵呵,我没有说这句话哈。lz很多立论在这句话上,估计是看贴的时候看错了。

>>假设中断sleep了, 在调度的时候, 内核将中断的CS:eip和SS:esp保存在被抢占任务A的thread_info中, 当任务A被重新唤醒的时候, 任务A从中断的CS:eip开始执行, 这也能正常执行下去, 中断执行完后, 从ret_from_intr中返回. 可以恢复任务A的抢占前的场景.

这里在中断sleep了,由谁来唤醒呢?关键还是没有一个中断描述符来作为内核调度的单位。
前面也说到了,这是个设计问题,不是能不能实现的问题。
如果说把中断的handler作为一组内核线程,当然是可以让中断睡眠的。但我始终任为中断是紧急事务,必须立即处理,我想不出有任何理由推迟中断的处理。
QUOTE:
原帖由  Solaris12 于 2007-6-27 20:25 发表于 17楼  


Cool!

你说的很准确。的确是你说的情况。

举例来说, 写网卡驱动程序,我们通常把中断服务函数说成ISR。

但在Solaris里,每个级低优先级中断都有个IST,中断服务线程。


我说的ISR是驱动程序的 ...


看来Solaris中的IST有点类似用内核线程来实现中断的handler,这种情况下应该是可以睡眠的。
Solaris的实现果然和linux有很多不同,要多多请教了。

QUOTE:
原帖由  xiaozhaoz 于 2007-6-27 22:36 发表于 21楼  


简单看了一下前面的回复, 不能认同.

问题的焦点大家都集中在异步异常(中断)没有自己的上下文?

中断抢占了当前任务后, 可以通过current获得当前的task结构, 将当前任务的寄存器信息压入栈中, 替换成自 ...


此外,我想lz对上下文一词还有点误解。
上下文(context)表示当前cpu的状态,包括各种标志及寄存器的值。这里中断是运行在自己的上下文而不是进程的上下文。如果是运行在进程上下文的话,中断处理就不需要在开始就保存上下文,在离开的时候又恢复。
当然,有一些先进的处理器(例如安腾),它为中断处理程序提供了一套专门的寄存器供中断处理程序使用,这个时候很多上下文是不需要保存的(但并非说就不保存上下文,只是某些不用保存)
中断当然有自己的CONTEXT。但没有(准确说经常没有)进程相关的CONTEXT。
你看2.6.13 KERNEL,IRQ使用 自己的STACK,如果在中断中schedule()了,能正确回来吗?




QUOTE:
原帖由  zx_wing 于 2007-6-28 10:56 发表于 29楼  

此外,我想lz对上下文一词还有点误解。
上下文(context)表示当前cpu的状态,包括各种标志及寄存器的值。这里中断是运行在自己的上下文而不是进程的上下文。如果是运行在进程上下文的话,中断处理就不需要在 ...
QUOTE:
原帖由  zx_wing 于 2007-6-28 10:37 发表于 27楼  

>>这其实和"中断没有自己的上下文"无关. CPU没有关中断, 中断有自己的上下文, 中断的上下文就是抢占的任务A的上下文.

“中断没有自己的上下文”,呵呵,我没有说这句话哈。lz很多立论在这 ...



中断sleep了, 由谁来唤醒?
在关中断的情况下, 不能sleep, 19楼已经描述了.
在中断handler中, 绝大部分都是开中断的, 这个时候为什么不能sleep? 时钟中断可以正常触发, 要不中断嵌套怎么实现? 当然由时钟中断的软中断来唤醒. 如我说到的:
如果内核修改好了, handler中调用schedule_timeout_interrupt().的情况.

中断都很紧急, 这不一定正确, 

不是所有的中断handler都紧急, 就如solaris中描述的, 时钟中断最紧急, 其他的中断不一定. 可能有的实时任务比中断紧急.

中断没有上下文确实不是你说的, sorry.
QUOTE:
原帖由  思一克 于 2007-6-28 11:05 发表于 31楼  
中断当然有自己的CONTEXT。但没有(准确说经常没有)进程相关的CONTEXT。
你看2.6.13 KERNEL,IRQ使用 自己的STACK,如果在中断中schedule()了,能正确回来吗?




不好意思,我看贴也不仔细,把lz“总结:
    异步异常(中断)handler不是没有上下文, 而是没有固定的上下文,  如果使用被抢占的任务作为上下文, 一,自身的处理无法得到实时保障,导致系统不确定性, 二,任务受到影响.”这句话看成中断使用进程上下文了。
QUOTE:
原帖由  思一克 于 2007-6-28 11:05 发表于 31楼  
中断当然有自己的CONTEXT。但没有(准确说经常没有)进程相关的CONTEXT。
你看2.6.13 KERNEL,IRQ使用 自己的STACK,如果在中断中schedule()了,能正确回来吗?





你再编译的时候, 把4K的stack的选项不要选上, 中断和软中断就不会使用自己的栈, 而是使用被抢占任务的栈了.

所以栈不是中断不能sleep的原因.
QUOTE:
原帖由  xiaozhaoz 于 2007-6-28 11:13 发表于 32楼  


中断sleep了, 由谁来唤醒?
在关中断的情况下, 不能sleep, 19楼已经描述了.
在中断handler中, 绝大部分都是开中断的, 这个时候为什么不能sleep? 时钟中断可以正常触发, 要不中断嵌套怎么实现? 当然由时钟中 ...


>>在中断handler中, 绝大部分都是开中断的, 这个时候为什么不能sleep? 时钟中断可以正常触发, 要不中断嵌套怎么实现? 当然由时钟中断的软中断来唤醒. 如我说到的:
如果内核修改好了, handler中调用schedule_timeout_interrupt().的情况.
当然,如果你把handler用内核进程来实现,当然是可以睡眠和被调度的。这只是个实现问题。我这里的指的是linux下不能睡眠的原因。


>>中断都很紧急, 这不一定正确,
紧急是个相对概念,我认为中断都比进程紧急
QUOTE:
原帖由  zx_wing 于 2007-6-28 10:56 发表于 29楼  

此外,我想lz对上下文一词还有点误解。
上下文(context)表示当前cpu的状态,包括各种标志及寄存器的值。这里中断是运行在自己的上下文而不是进程的上下文。如果是运行在进程上下文的话,中断处理就不需要在 ...



nod, 在书里都是这样写的, 
但是实际情况下比这个要复杂, 内核中光有上下文无法完成调度切换的工作, 

所以在Linux Kernel中, 我一般将上下文指作可以协助Kernel完成调度的东西, 就是内核task, thread_info结构, + CPU寄存器.

QUOTE:
原帖由  zx_wing 于 2007-6-28 11:25 发表于 35楼  

>>在中断handler中, 绝大部分都是开中断的, 这个时候为什么不能sleep? 时钟中断可以正常触发, 要不中断嵌套怎么实现? 当然由时钟中断的软中断来唤醒. 如我说到的:
如果内核修改好了, handler中调用sch ...



我想你还没有真正理解我的意思, 19楼的总结说得很清楚, 中断handler不能sleep, 
不是因为没有"我说的上下文", 而是因为没有固定的上下文, 他会随机抢占当前任务, 使用当前任务的上下文作为调度单元. 既然有了调度单元, 他为什么不能sleep?

solaris12和我的想法差不多, 真正愿意那是调度问题和系统问题. 怎么解决这些问题, 就是19楼介绍的内核实时性瓶颈的问题.
在中断跟内核用的是同一个栈的情况下,如果系统允许某个handler中可以sleep,那么在不可预知的任何时候,任何一个内核线程/进程在执行的时候,恰好来了上面的中断并执行到了handler,则这个内核线程/进程就"中奖"了,就会sleep,而只要这个handler是在开中的情况下运行的,它是可以被其他线程/进程唤醒的,关键是系统中所有的线程/进程都有这个"中奖"机会,哈哈

这样理解对吗?
QUOTE:
原帖由  augustusqing 于 2007-6-28 11:41 发表于 38楼  
在中断跟内核用的是同一个栈的情况下,如果系统允许某个handler中可以sleep,那么在不可预知的任何时候,任何一个内核线程/进程在执行的时候,恰好来了上面的中断并执行到了handler,则这个内核线程/进程就&quo ...



对, 就是这个意思, 
所以这种系统的不确定性就很大.  
而现在的中断除了不sleep, 不能preempt之外, 对任务的影响也想你说的那样"中奖"式的, 所以现在系统的实时性是无法保证的!!

解决这个问题的方法就是让handler只能在固定的上下文中sleep, 当然能不sleep最好不要sleep. 所以就有了将handler放到专门内核线程中处理的想法.

如果内核的实时性足够好, 那么handler线程的执行也可以得到保证.
QUOTE:
原帖由  xiaozhaoz 于 2007-6-28 12:19 发表于 39楼  


对, 就是这个意思, 
所以这种系统的不确定性就很大.  
而现在的中断除了不sleep, 不能preempt之外, 对任务的影响也想你说的那样"中奖"式的, 所以现在系统的实时性是无法保证的!!

解决这个问 ...


>>总结: 异步异常(中断)handler不是没有上下文, 而是没有固定的上下文,  如果使用被抢占的任务作为上下文, 一,自身的处理无法得到实时保障,导致系统不确定性, 二,任务受到影响.

linux是通用操作系统,并不是专门为嵌入式设计的实时操作系统,所以影响系统的实时性并不是中断不能睡眠的原因。
诚然,中断不像进程那样有固定的上下文,所以它在linux中不是调度器的调度单位,所以不能睡眠,因为睡眠、唤醒都是调度器的任务,你不被调度器调度,当然不能睡眠。如你所说,它是可以被实现成可睡眠的,但我这里讲述的是它在linux中不能睡眠的原因。如果我的理解还不对的话,请lz再用一句总结一下 在linux中中断不能睡眠的原因。因为你文字比较多,我看的有点乱了。
LZ发帖子是讨论,不是有什么总结性的一句话能概括什么。我也是在学习中。

我觉得,LINUX设计不让中断中睡眠是经过充分考虑的。seelp on interrupt(SOI)不允许是内核设计的原因,而不是有什么绝对本质的无法更改的原因。

我同意zx_wing的说法,没有和进程关联的CONTEXT,而调度的基本单位是进程,因此KERNEL设计者无法容忍sleep on interrrupt(SOI).

下面的相同的讨论说的也很清楚:

think this should clear all confusions, apologies if this is too big
or irrelevant and doesnt make much sense, which I think is not the
case.

Note : The following explaination assumes that the isr and the
interrupted proceess share the stack, afaik isr can seperate stacks,
which is configurable, but I dont know how this explaination holds
good when the isr is having a seperate stack.

1. Why you are not allowed to sleep in an interrupt handler?Was this a
design decision or is there a fundamental reason that makes sleeping
interrupt handlers simply impossible? What about the page fault
handler - it (probably) sleeps when it has to swap in a page, why is
it possible to sleep for the page fault handler and not for an
interrupt handler?

-> Sleeping is implemented using scheduler. Scheduler only schedules
tasks (design decision to keep it simple). So you need a task context
to sleep in. Interrupt is not tied to a process (and uses stack of
whichever happens to be scheduled ) so it can't use the context
because it does not have to be in a sane state.

2. But why.....

You cannot sleep in an interrupt handler because interrupts do not have
a backing process context, and thus there is nothing to reschedule
back into. In other words, interrupt handlers are not associated with
a task, so there is nothing to "put to sleep" and (more importantly)
"nothing to wake up". They must run atomically.
The reason the page fault handler can sleep is that it is invoked only
by code that is running in process context. Because the kernel's own
memory is not pagable, only user-space memory accesses can result in a
page fault. Thus, only a few certain places (such as calls to
copy_{to,from}_user()) can cause a page fault within the kernel. Those
places must all be made by code that can sleep (i.e., process context,
no locks, etc).

3. However can't you consider the interrupt handler as running in the
context of the task A, that was incidentially running when the
interrupt occurred (the interrupt handler
uses the stack of task A, this is not always true, isr might have a
seperate stack). So wouldn't it conceptually be conceivable to put the
interrupt handler to sleep by saving the current CPU state (register
contents) with task A, putting task A asleep, and resume processing of
the interrupt once task A is rescheduled by the scheduler?
Of course this could be considered 'unfair' with respect to task A, in
that the interrupt has no relation to task A besides the fact that
they happend to be on the same CPU at the same time. But I am
interested in learning if there is any fundamental reason against
sleeping in an interrupt handler.

->There are bigger problems. The process is in an arbitrary state when
interrupt is invoked. So it might hold arbitrary spinlocks. It might
be on arbitrary wait_queues. Possibly other funny things might happen.
These either disallow sleeping or could interact badly with the
interrupt trying to sleep.

This is part of conversation happened on the same list before few
years, not verbatim though.
--

这里也说到了如果IRQ不用自己的STACK,而利用被中断任务(任务A)的STACK是否就可以SOI了的问题。
因为中断发生的CONTEXT和A无关,而是碰巧在同一个CPU上的一个任务A被中断了,如果中断睡了,A就回被殃及,系统就彻底乱了。
(和调度发生冲突了)。

至于schedule()会将任务在不同CPU之间分,而IRQ回来后如何?(假定IRQ不使用自己的STACK,可以回来的话)。
还有最后一段说的其他各种复杂的问题。

总之,如果中断不THREAD化,应该无法SOI。所以LINUX(2。6)中shedule()中有检查不允许。

如果改动LINUX,如何改动最小使得可以SOI?(虽然没有大意义,但作为讨论可以从讨论中学习其他的)。





QUOTE:
原帖由  zx_wing 于 2007-6-28 12:38 发表于 40楼  

>>总结: 异步异常(中断)handler不是没有上下文, 而是没有固定的上下文,  如果使用被抢占的任务作为上下文, 一,自身的处理无法得到实时保障,导致系统不确定性, 二,任务受到影响.

linux是通用操作系统 ...
QUOTE:
原帖由  思一克 于 2007-6-28 13:16 发表于 41楼  
LZ发帖子是讨论,不是有什么总结性的一句话能概括什么。我也是在学习中。

我觉得,LINUX设计不让中断中睡眠是经过充分考虑的。seelp on interrupt(SOI)不允许是内核设计的原因,而不是有什么绝对本质的无法 ...


我说的lz是xiaozhaoz兄,因为他概括的观点有点多,我看的不是很明白,所以想让他总结一下。
呵呵,版主以为我在叫你啦

QUOTE:
原帖由  zx_wing 于 2007-6-28 13:59 发表于 42楼  

我说的lz是xiaozhaoz兄,因为他概括的观点有点多,我看的不是很明白,所以想让他总结一下。
呵呵,版主以为我在叫你啦



我个人的看法, 相信大家会有更多的想法.

一句话的总结:
中断sleep会导致系统工作异常. 

三句话总结:
    1. 中断sleep会增加 任务, 系统的不确定性
    2. 和中断共享中断号的中断会受到影响.
    3. 中断的处理受到被抢占任务上下文属性的限制.

解释和解决的方法看19楼.

总之, 我们都是在玩文字游戏, 中断没有固定上下文(context), 而不是没有上下文.

PS, 正是因为Linux是一个通用的操作系统, 所以必须让用户可以配置成嵌入式实时模式, 所以必须考虑实时性因素!
我看了19楼的帖子,

觉得
中断和软中断的线程化和spin_lock的可sleep化这两个并不能提高系统的实时性。

比如spinlock, 就是为了短暂需要lock的时候让CPU空等待。这时比用可以sleep的锁要节省CPU而不是浪费。因为调度的耗费可能要比SPIN的耗费多的多。

linux的中断是半THREAD化的。你可以增加工作在THREAD(softirqd)中的比重,增加后,系统反映更慢了。比如你打一个字,一个网络包的处理,如果都用THREAD做,响应应该是慢一些。因为调度的原因 。

如果不对,请批驳。
QUOTE:
原帖由  思一克 于 2007-6-28 14:51 发表于 46楼  
我看了19楼的帖子,

觉得
中断和软中断的线程化和spin_lock的可sleep化这两个并不能提高系统的实时性。

比如spinlock, 就是为了短暂需要lock的时候让CPU空等待。这时比用可以sleep的锁要节省CPU而不是浪 ...



好几个同事问过同样的问题. 

这个问题已经脱离了本贴的原意, 如果有兴趣, 可以另外开贴讨论.

实时性的必须保证,  系统在任何情况下, 可以在一定的时间内执行到某个任务.

1. 你的比较尺度有问题, 你是拿现有系统的最好情况和realtime patch系统的最坏情况比较.
2. 现有系统的最坏情况, 延时可能有几十毫秒, 察看19楼的描述. 而realtime patch的系统只有几十us(不要低估了我们CPU的处理能力 , 而且上限制是可以计算的.

具体你可以在网上找到大量的测试报告.
LINUX中到是有中断还没有完全返回就调用schedule()而睡眠过去的例子。

可以猜是哪里。
我觉得,中断和异常不同,中断是异步的,异常和系统调用是同步的。
异常比如缺页异常发生时,当前任务在异常处理完成之前不能继续运行,该异常处理过程和当前任务天然相联系,运行在当前进程的上下文中。
中断的发生很可能是与当前任务无关的,如果把中断处理实现为强行与当前任务相关联,就会造成当前任务由于和他毫无关系的过程的执行而被剥夺了运行的权利。这样的调度实现是不符合公平合理的原则的,是错误的。
中断本来就是用来处理紧急/突发事件的, 而进程睡眠是因为没有事情可以做才去睡眠的,
两者本来就属于对立的情形. 所以,为什么要让中断处理程序去睡眠呢?
看了半天,头都大了。这问题看一下操作系统的原理或设计类书就明白了。

1.中断是可以睡眠的;如果你要这样设计的话;

传统UNIX:
2.中断是没有必要睡眠的,因为它和所有进程无关(事实上也有关,不然什么地方来的中断,比如磁盘中断总是和哪个读写磁盘的进程有关的
3.更重要的是,它能很快完成,不睡眠被认为能“充分利用资源”;

实时系统:
4.在实时系统中,中断应当被设计成能够睡眠,并被高优先级的进程抢占;
5.中断优先级,是指2.中的相关进程的优先级;保存上下文不是问题,你可以将其设计成中断任务(或线程);
6.555,很难实现.
QUOTE:
原帖由  albcamus 于 2007-6-28 14:30 发表 
我怎么觉得你们3位彼此都懂对方说什么,只不过主张不同?




有点像FREE BSD,LINUX,OPEN SOLARIS之间的关系。。。。

我觉得linux在将来的版本会实现kthread并把interrupt routine也归入其中。
就像solaris。在进程和进程调度方面,solaris确实是走在前面。

并不是说把中断以kthread形势呈现就是为了调度它,而是在内核真正抽象出thread概念。
QUOTE:
原帖由  scutan 于 2007-6-27 09:38 发表 



其实这只是一个设计上的问题, 而并不是强制的. 只是针对Linux内核而言, 这是规矩. 
LKD2上面说, 切换出去之后, 何时才能调度回来? 就是中断回来之后可能不会回到之前所依俯的那个进程了. 
中断不能睡眠 ...


睡眠是进程调度的概念,中断若睡眠上下文保持[进程的概念]与中断的概念冲突.要讨论为何要睡眠?软中断中也不需要睡眠,可用不同分支处理不同情况,没有睡眠的必要.完全可用网络中CPU上下文保留一些缓冲等与睡眠有些类似.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值