如果线程在内核中实现,可以使用内核信号量对同一个进程中的两个线程进行同步吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如果线程在内核中实现,可以使用内核信号量对同一个进程中的两个线程进行同步吗?相关的知识,希望对你有一定的参考价值。

如果线程在用户空间实现呢?假设在其他进程中没有线程必须访问该信号量。

对于内核线程,线程可以在信号量上阻塞,而内核可以运行该进程中的其它线程。因而,使用信号量没有问题。而对于用户级线程,当某个线程在信号量上阻塞时,内核将认为整个进程都被阻塞,而且不再执行它。因此,进程失败。 参考技术A 内核线程可以,因为内核里面有不同线程的thread table。也就是内核能看见内核线程。
但是对于用户空间线程来说,不行。因为内核只能看见用户空间的进程,进程里面的线程是看不见的。
参考技术B 我觉的可以,在同一进程的控制下,只是用户不同至于为什么,去问微软吧,这是他们的mimi 或等什么时候windows开源了,我告诉你

内核线程对信号的处理策略

一、引出

大家都知道,信号是在进程返回用户态的时候触发执行的一种机制,但是对于内核线程来说,它们并不会返回用户态。这就好像《大话西游》里打劫脚底板的时候那位坐轿的官人没有脚底板一样尴尬。另一方面,通过sigprocmask是不能屏蔽掉SIGKILL和SIGSTOP两个信号的,所以如果我们通过kill -9 来杀死一个内核线程的话,内核线程是否会被杀死,如果会,它又是何时如何处理信号的呢?

二、内核线程对信号的处理

1、屏蔽

daemonize:

 /* Block and flush all signals */
 sigfillset(&blocked);
 sigprocmask(SIG_BLOCK, &blocked, NULL);

可以看到,内核线程首先会禁用掉所有的信号。这里直接调用的是sigprocmask,而不是sys_rt_sigprocmask,因为后者中会强制禁掉对SIGKILL和SIGSTOP的屏蔽

asmlinkage long
sys_rt_sigprocmask(int how, sigset_t __user *set, sigset_t __user *oset, size_t sigsetsize)
{
 int error = -EINVAL;
 sigset_t old_set, new_set;

 /* XXX: Don‘t preclude handling different sized sigset_t‘s.  */
 if (sigsetsize != sizeof(sigset_t))
  goto out;

 if (set) {
  error = -EFAULT;
  if (copy_from_user(&new_set, set, sizeof(*set)))
   goto out;
  sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));

这样内核就可以忽略掉所有的信号了。从而绕过了对SIGKILL和SIGSTOP的屏蔽。

[[email protected] ArgABI]$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   2004   624 ?        Ss   Oct05   0:02 /sbin/init
root         2  0.0  0.0      0     0 ?        S<   Oct05   0:00 [kthreadd]

SigQ: 1/8192
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: ffffffffffffffff

从2.6.31内核开始,看来这个信号是被忽略了,从而避免了内核信号队列中有信号挤压的问题。在至少2.6.21内核中,内核态的信号将会被一只挤压在内核的队列中,永远无法释放。

2、信号的处理

虽然这里是屏蔽了所有的信号,但是如果有些愣头青就是不识趣,就是要给内核发信号,比如sigkill,那么内核线程又将如何应对呢?相当于说,我已经声明闭门谢客,但是就是有人非要往里面闯,那看一下线程是如何处理的。

static int sig_ignored(struct task_struct *t, int sig)
{
 void __user * handler;

 /*
  * Tracers always want to know about signals..
  */
 if (t->ptrace & PT_PTRACED)
  return 0;

 /*
  * Blocked signals are never ignored, since the
  * signal handler may change by the time it is
  * unblocked.
  */
 if (sigismember(&t->blocked, sig))
  return 0;

 /* Is it explicitly or implicitly ignored? */
 handler = t->sighand->action[sig-1].sa.sa_handler;
 return   handler == SIG_IGN ||

可以看到,这里直接返回了SIG_IGN,从而表示这个信号将会被忽略到,这样这个信号就不会放置到线程的信号队列中,从而避免对内核空间的永久占用。

int
__group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
 int ret = 0;

 assert_spin_locked(&p->sighand->siglock);
 handle_stop_signal(sig, p);

 /* Short-circuit ignored signals.  */
 if (sig_ignored(p, sig))
  return ret;这里将会直接返回,从而避免添加到线程信号队列中,以为即使添加到那里也不会有人来处理,所以根本就来个“本来无一物、何处惹尘埃”。

对于早期通过SIGBLOCK屏蔽的信号,它将会在

static void
__group_complete_signal(int sig, struct task_struct *p)
{
 struct task_struct *t;

 /*
  * Now find a thread we can wake up to take the signal off the queue.
  *
  * If the main thread wants the signal, it gets first crack.
  * Probably the least surprising to the average bear.
  */
 if (wants_signal(sig, p))
  t = p;
 else if (thread_group_empty(p))
  /*
   * There is just one thread and it does not need to be woken.
   * It will dequeue unblocked signals before it runs again.
   */

  return;

这里返回,同样不会惊动内核线程。

3、显式处理

如果有些内核的线程比较多愁善感,他可能也会关心民间的疾苦,它可能会处理用户态发送的信号,以示“亲民”。那么他就要主动的通过allow_signal来打开自己可以接受的信号,从而可以让信号发送过来。该函数的实现

int allow_signal(int sig)
{
 if (!valid_signal(sig) || sig < 1)
  return -EINVAL;

 spin_lock_irq(&current->sighand->siglock);
 sigdelset(&current->blocked, sig);
 if (!current->mm) {
  /* Kernel threads handle their own signals.
     Let the signal code know it‘ll be handled, so
     that they don‘t get converted to SIGKILL or
     just silently dropped */
  current->sighand->action[(sig)-1].sa.sa_handler = (void __user *)2;
 }
 recalc_sigpending();
 spin_unlock_irq(&current->sighand->siglock);
 return 0;
}

这样,当信号过来的时候,它通过kill就可以穿过__group_send_sig_info最后的__group_complete_signal,从而将这个内核线程唤醒。但是此时内核就需要自力更生、自己主动判断信号的类型,由于默认是禁止的,所以它自己allow的信号就容易判断了,所以他们就可以在自己被唤醒之后主动判断自己是不是有允许的信号发送过来,如果有的话就执行相关的处理。比如说,如果收到SIGKILL就自觉了断。

以上是关于如果线程在内核中实现,可以使用内核信号量对同一个进程中的两个线程进行同步吗?的主要内容,如果未能解决你的问题,请参考以下文章

内核线程对信号的处理策略

用户空间实现线程 内核实现线程 线程的调度

linux互斥锁简介(内核态)

线程的实现方式

是否有一个单独的内核级线程来处理用户进程的系统调用?

Delphi中纤程的使用