linux内核线程对信号的处理过程(转)

上一篇 / 下一篇  2012-08-04 22:26:32 / 个人分类:linux内核

linux中的线程分为用户线程和内核线程,用户线程是标准的线程,完全的自主性,完全的抢占性;但是内核线程就不那么好了,某种意义上没有用户线程那么清闲,这个怎么理解呢?用户线程的编写者只需要实现应用逻辑就可以,至于调度,信号处理等工作完全有内核代劳,用户进程根本不需要操这些心,比如说调度, 在2.6完全可抢占内核之前,内核线程必须显式放弃处理器,否则它将永远不会被抢占,这就给了内核线程的开发者很大的限制,不过谁让他们写的是内核程序呢?应该的! 
关于调度就不说那么多了,现在看一下信号的处理。linux规定,只有在进程返回用户空间的前夕才处理pending未决的信号,这就产生了一个问题,内 核线程如何处理信号?它永远不会返回用户空间是否意味着它就不能处理信号了,如果真的这样的话,linux内核也就太不灵活了,当然不是这样,这不是 linux的性格。这只是问题之一;还有一个问题就是论坛上很多朋友在问如何杀死内核线程,linux中线程结束有三种方式:1.自己结束;2.异常结 束;3.被信号结束。本质上2也是靠发送信号结束线程的,这样就只剩下两种方式,一个是自主结束一个是靠信号结束,如果内核线程根本就不能处理信号的话,那还谈什么靠信号结束?于是,种种理由迫使内核线程不应该不能处理信号,内核线程虽然没有用户空间,不存在返回用户空间一说,也没有必要就为了内核线程就 更改linux内核信号处理架构,那么唯一的办法就是由内核线程自己处理信号,这个策略和非抢占内核时期由内核线程自己放弃处理器是一样的,毕竟它是内核的东西,多让它作些事情并不多。那么这一切具体是怎么实现的呢?内核线程到底能不能杀死呢?可以先试一下,执行以下命令: 
[root@localhost zhaoy]# ps -e |grep khubd 
然后杀死它,果然,khubd被杀了,那么可以肯定地说,内核线程是可以杀死的!内核线程是怎么被杀死的呢?我们来看一下这个khubd: 
static int hub_thread(void *__unused) 

         daemonize("khubd");  //这个函数很重要,凡是调用了这个函数该内核线程就放弃了继承的或者sys_fork时分配的用户地址空间mm_struct,而且阻塞了一切信号,包括SIGKILL(-9)信号。 
         allow_signal(SIGKILL);  //又允许了SIGKILL信号 
         do { 
                 hub_events(); 
                 wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list)); 
                 if (current->flags & PF_FREEZE) 
                         refrigerator(PF_FREEZE); 
         } while (!signal_pending(current)); //一轮循环结束就监测是否有信号pending,一旦有就退出循环。(因为只可能是SIGKILL信号) 
         pr_debug ("%s: khubd exiting/n", usbcore_name);  //执行到这里就说明khubd将要退出了,被杀死了 
         complete_and_exit(&khubd_exited, 0); 

以上仅仅用khubd说明一下内核线程怎么处理信号和内核线程被杀的问题,当然还可能有更复杂的控制逻辑,也可能是类似于用户进程的do_signal的逻辑,但是原理一模一样,就不赘述了。 

上面介绍了内核线程处理信号和被杀死的原理,顺带地提了一嘴非抢占内核中内核线程的协作多任务原理(必须自己放弃cpu),这种设计让我觉得很不对称,内 核线程和用户线程没有统一地进行对待,而在solaris里面一切线程都是被统一对待的。这实际上并不是美中不足,而正是linux的灵活性之所在,内核 只提供用户接口,而具体怎么实现用户不必过问,谁要是觉得不好怎么的,完全可以提供一个好的,这就是linux的性格。


signal_pending(current)

检查当前进程是否有信号处理,返回不为0表示有信号需要处理。
返回 -ERESTARTSYS 表示信号函数处理完毕后重新执行信号函数前的某个系统调用。也就是说,如果信号函数前有发生系统调用,在调度信号处理函数之前,内核会检查系统调用的返回值,看看是不是因为这个信号而中断了系统调用.

如果返回值-ERESTARTSYS,并且当前调度的信号具备-ERESTARTSYS属性,系统就会在用户信号函数返回之后再执行该系统调用。 

内核线程不会被强制响应signal. 一旦你进入死循环, 只会有一下两种情况: 

1. 在non-preemptive的内核如2.4里面, 该内核线程会一直占用CPU, 不会释放. 如果在SMP的机器上, 由于还有别的CPU可以被调度, 所以系统不会死. 但如果在uni-processor的系统里面, 内核就死掉了. 这时候只有中断会被响应. 

2. 在preemptive的内核如2.6里面, 该线程可以被强制剥夺CPU, 并被调度. 因此系统不会死掉, 但它也不会响应signal. 

内核线程响应signal必需靠自己检测, 或是以interruptible的方式进入睡眠. 对于死循环的线程, 你应该在循环体内合适的地方进行类似以下的检测: 

if (signal_pending(current)) { 
spin_lock_irq(&current->sighand->siglock); 

/* 检查你感兴趣的signal是否在等待响应 */ 
if(sigismember(&current->pending.signal, SIGKILL)) { 
/* 清空等待的signal */ 
flush_signals(current); 
spin_unlock_irq(&current->sighand->siglock); 
/* 跳出 */ 



以上只是内核线程响应signal的一种模式之一. 但理论上讲, 内核线程无论如何是不能也不应该进入死循环的!


TAG:

 

评分:0

我来说两句

日历

« 2024-05-12  
   1234
567891011
12131415161718
19202122232425
262728293031 

数据统计

  • 访问量: 54955
  • 日志数: 40
  • 建立时间: 2012-07-17
  • 更新时间: 2012-10-01

RSS订阅

Open Toolbar