为什么可能导致睡眠的函数都不能在中断上下文中使用呢?【转】

转自:https://www.cnblogs.com/hoys/archive/2012/06/28/2567622.html

转自:http://blog.chinaunix.net/u1/49093/showart_1910189.html

这个问题有很多人问过,我看了下Linux得内核代码,原因如下:(当然我不能保证一定对,如果有牛人理解得更好,欢迎指正)

1、 中断处理的时候,不应该发生进程切换,因为在中断context中,唯一能打断当前中断handler的只有更高优先级的中断,它不会被进程打断,如果在 中断context中休眠,则没有办法唤醒它,因为所有的wake_up_xxx都是针对某个进程而言的,而在中断context中,没有进程的概念,没 有一个task_struct(这点对于softirq和tasklet一样),因此真的休眠了,比如调用了会导致block的例程,内核几乎肯定会死。

2、schedule()在切换进程时,保存当前的进程上下文(CPU寄存器的值、进程的状态以及堆栈中的内容),以便以后恢复此进程运行。中断发生后,内核会先保存当前被中断的进程上下文(在调用中断处理程序后恢复);

但在中断处理程序里,CPU寄存器的值肯定已经变化了吧(最重要的程序计数器PC、堆栈SP等),如果此时因为睡眠或阻塞操作调用了schedule(),则保存的进程上下文就不是当前的进程context了.所以不可以在中断处理程序中调用schedule()。

3、2.4内核中schedule()函数本身在进来的时候判断是否处于中断上下文:

if(unlikely(in_interrupt()))

BUG();

因此,强行调用schedule()的结果就是内核BUG,但我看2.6.18的内核schedule()的实现却没有这句,改掉了。

4、中断handler会使用被中断的进程内核堆栈,但不会对它有任何影响,因为handler使用完后会完全清除它使用的那部分堆栈,恢复被中断前的原貌。

5、处于中断context时候,内核是不可抢占的。因此,如果休眠,则内核一定挂起。

-------------------------------------------------------

 

原帖地址:http://bbs.chinaunix.net/thread-2115820-1-1.html

 

这里引用个人认为比较OK的解析:

 

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

 

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

复制代码

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

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


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

======================================================

 

另一篇:

 

http://blog.openrays.org/blog.php?do=showone&tid=455

 

其结论:

 

5. 中断处理时可否睡眠问题

Linux 设计中,中断处理时不能睡眠,这个内核中有很多保护措施,一旦检测到内核会异常。


一个进程A因为中断被打断时,中断处理程序会使用 A 的内核栈来保存上下文,因为是“抢”的 A 的CPU,而且用了 A
的内核栈,因此中断应该尽可能快的结束。如果 do_IRQ 时又被时钟中断打断,则继续在 A 的内核栈上保存中断上下文,如果发生调度,则
schedule 进 switch_to,又会在
A 的 task_struct->thread_struct 里保存此时时种中断的上下文。

假如其是在睡眠时被时钟中断打断,并 schedule 的话,假如选中了进程 A,并 switch_to 过去,时钟中断返回后则又是位于原中断睡眠时的状态,抛开其扰乱了与其无关的进程A的运行不说,这里的问题就是:该如何唤醒之呢??

另外,和该中断共享中断号的中断也会受到影响。

 

======================================================

 

再一篇,也分析的很到位:

 

http://blog.csdn.net/maray/article/details/5770889

 

其结论:

Linux是以进程为调度单位的,调度器只看到进程内核栈,而看不到中断栈。在独立中断栈的模式下,如果linux内核在中断路径内发生了调度(从技术上讲,睡眠和调度是一个意思),那么linux将无法找到“回家的路”,未执行完的中断处理代码将再也无法获得执行机会。

时间: 2024-10-14 14:40:19

为什么可能导致睡眠的函数都不能在中断上下文中使用呢?【转】的相关文章

中断中处理延时及一些函数的调用规则(中断调i2c驱动有感)--中断中的延迟delay与printk函数的冲突【转】

转自:http://blog.csdn.net/psvoldemort/article/details/8222371 1,中断处理程序中不能使用有睡眠功能的函数,如ioremap,kmalloc,msleep等,理由是中断程序并不是进程,没有进程的概念,因此就没有休眠的概念: 2,中断处理程序中的延时可以用忙等待函数来代替,如ndelay,udelay,mdelay等,这些函数在实现上本质是根据CPU频率进行一定次数的循环:最好不要使用mdelay,因为毫秒延时对内核来说已经是非常大了.但是在

asp net session-cookieless设置为true后导致的sessionID每次都变化的问题

问题描述 cookieless设置为true后导致的sessionID每次都变化的问题 问大神们一个问题,我把cookless设置为true了,现在sessionID存在URL里面了,可是为什么刷新页面这个sessionID都会变呢,不是说一个会话的sessionID是不变的吗 解决方案 可能是没有在Session里面保存任何信息引起的,即程序中任何地方都没有使用Session.当Session中保存信息之后SessionID将一直和浏览器相关,此时的SessionID将不会在变化. 解决方案二

c++不允许使用抽象类类型对象,我明明每个函数都实现了的

问题描述 c++不允许使用抽象类类型对象,我明明每个函数都实现了的 上代码: Stack.h: #pragma once //栈定义 template class Stack { public: const int maxSize = 50; Stack(); virtual void Push(const T& x) = 0; virtual bool Pop(T& x) = 0; virtual bool getTop(T& x)const = 0; virtual bool

安卓有没有一个对所有函数都有说明的网址?

问题描述 安卓有没有一个对所有函数都有说明的网址? 有没有对安卓的开发包里所有的函数都有说明的一个网站,或者软件? 解决方案 不需要这样的软件,google就很好用. 解决方案二: 最新Android SDK开发文档(CHM格式)

指针-这段代码中的插入函数编译没有错误,运行就崩溃?其它函数都经过编译了没有错误?不知道是什么问题?

问题描述 这段代码中的插入函数编译没有错误,运行就崩溃?其它函数都经过编译了没有错误?不知道是什么问题? #include #include #include typedef struct POINT { int row; int col; struct POINT *next; }POINT; void initPointLink(POINT **pointLinkHeadPointer); void showPoints(POINT *pointLinkHead); void showOne

服务器禁止百度爬虫的文件给删了,导致很多测试页都给百度爬了!

问题描述 服务器禁止百度爬虫的文件给删了,导致很多测试页都给百度爬了! 被百度抓了好多的测试页,怎么样在不影响网站正常收录的情况下批量提交删除?

求助高手DataGridView的问题 (操作无效,原因是它导致对 SetCurrentCellAddressCore 函数的可重入调用。)

问题描述 voidHandleReceive(inteventID,stringdate,stringtime,intdspID,stringmessage,Imagei1,Imagei2){while(alertTable.Rows.Count>=50)//DataGridView中的报警信息超过50条就删除第一条,不断地往上顶{alertTable.Rows[0].Delete();}DataRowdr=alertTable.NewRow();dr[0]=eventID.ToString()

c++-C++中成员函数、静态成员函数、虚函数都是怎么存储的?他们哪一个先被调用?

问题描述 C++中成员函数.静态成员函数.虚函数都是怎么存储的?他们哪一个先被调用? 今天参加了一个面试,面试官问道这个问题:C++中成员函数.静态成员函数.虚函数都是怎么存储的?他们哪一个先被调用? 对于这个问题我还真没有总结过,答得不是很好,求大神能不能回答一下?或者给点资料看看. 解决方案 成员函数.静态成员函数.虚函数 都是存储在独立的位置,跟类的对象存储空间是分隔的,这样对象都是通过函数地址来访问. 哪一个被调用取决于调用对象是类,还是类实例,还是类的对象指针,然后决定是哪个函数调用

javascript-在 JavaScript 中每个函数都是一个Function对象。

问题描述 在 JavaScript 中每个函数都是一个Function对象. Function 构造器会创建一个新的 Function 对象. 在 JavaScript 中每个函数都是一个Function对象. 这句话应该怎么理解? 解决方案 因为每一个函数都是function name(){},,声明一个函数就创建一个function对象 解决方案二: 不知道你问的是不是这个概念,每当你创建一个函数对象时,js引擎都会调用所有函数对象的父类,也就是Function的构造函数来创建一个函数对象,