+ -
当前位置:首页 → 问答吧 → signal hanler 与 user lever thread library

signal hanler 与 user lever thread library

时间:2010-12-29

来源:互联网

本帖最后由 baozhao 于 2010-12-29 08:08 编辑

诸位, 我最近在了解用户级线程库(即M:1模型),发现了一个令我困惑的问题。

用户级线程库必须有user level scheduler,假定我们按轮转方式,每隔一段时间就发一个信号给进程,然后signal hanler 里面实现scheduler,切换到另一个用户态线程(使用longjump 或者setcontext之类的函数)。

但是这里面存在的问题是, 内核栈里面还是上一个用户态线程的信息,尽管用户态能切换到另一个用户态线程,但是内核栈上一个线程的信息并没有清除(Linux正常情况是是在signal handler处理尾部插入sigreturn重新返回内核进行清理,longjmp已经无法达到此目的),这样下去的话,必然会导致问题(反复接到信号,内核栈溢出?)

我发现别人有类似的疑惑,但是没有得到回答,见
http://www.spinics.net/lists/linux-c-programming/msg00623.html

并且
https://www.securecoding.cert.org/confluence/display/seccode/SIG32-C.+Do+not+call+longjmp%28%29+from+inside+a+signal+handler
有明确意见,但是不少线程库的实现确实在signal handler调用了longjmp。

请指出我的理解有何问题?

作者: baozhao   发布时间: 2010-12-29

我的理解是:

进入signal handler以后,并没有什么信息留在内核栈上。被信号中断的那个用户态的上下文是被保存在用户栈上的。而sigreturn要做的事情就是,让调用者指定一个sig上下文信息,然后内核负责把用户恢复到那个上下文去。(还有就是去掉相应的sig mask,这一点siglongjmp也会做。)
在此过程中,内核不需要保存任何中间状态。

关于LZ贴的第一篇文章,longjmp后,应该就不在signal handle context上面了。
进入signal handle的时候,用户态的栈应该是这样的:
=> setjmp时的栈 => ... => 被信号中断时的栈 => 内核放置的sigframe => signal handle的返回地址(去调用sigreturn) => signal handle的栈

如果signal handle正常返回,那么sigreturn被调用,栈上的sigframe被作为参数传递。然后经过sigreturn的折腾,返回用户态时,就回到了“被信号中断的栈”。
而如果signal handle里面longjmp了,那么用户态已经回到了setjmp时的栈上。后面的一系列信息都失效掉了。

当然,对于用户态多线程的情况,并不是这样。因为不同的用户态线程会有不同的栈空间,这时的longjmp会jmp到其他的栈空间,而不是自己栈空间里面靠前的栈。

对于第二篇文章,它要表达的应该是setjmp/longjmp导致函数被重入的问题。线程在自己的栈空间范围内做longjmp,的确有这样的问题,需要小心。
但是longjmp到其他线程的栈空间上呢?逻辑上说,longjmp以后,我们已经到了另一个线程。setjmp/longjmp导致的是一个函数在两个线程里面被并发的访问。如果这个函数不能支持这么做,那么解决办法应该是线程间同步。

作者: kouu   发布时间: 2010-12-29