本教程不是基础线程,希望有点基础的POSIX 线程知识。 高手也不用看了。。
第一篇:Linux线程的信号处理行为
大家都知道linux 2.5内核之后 引入了NPTL ,让自己的用户态线程行为更符合Poxis口味。。下面就来看看NPTL的线程对信号的行为。
当然我们得首先复习..几个linux上的函数
tkill NAME tkill - send a signal to a single process SYNOPSIS int tkill(int tid, int sig); DESCRIPTION The tkill() system call is analogous to kill(2), except when the specified process is part of a thread group (created by specifying the CLONE_THREAD flag in the call to clone). Since all the processes in a thread group have the same PID, they cannot be individually sig-nalled with kill(2). With tkill(), however, one can address each process by its unique TID.These are the raw system call interfaces, meant for internal thread library use.
我个人感觉这个man写的不怎么好: 因为你不能把他翻译成中文去理解
通俗易懂的说: tkill 可以发给特定的 LWP,比如一个进程,他产生了很多LWP, 那么我可以想发给谁发给谁。
如果他不产生,就他一个独苗,那么我用tikll对他发信号,和kill发效果是一样的。
我们都知道如果通过kill 发信号给一个PID ,如果你没有做什么的话,正常情况下,应该是main线程接受。这个时候你会想,太搓了! 我要把信号发给我指定的人!
好linux知道你有这个怪癖,提供了tkill。 问题来了 tid是啥。。2.6内核proc下面看不到了!当然你可以猜:
现在的进程是 PID, 那么我产生的应该是 PID+1,但是当你产生了1000个,就找不到北了。。
linux当然早提供了gettid ,你可能需要通过
syscall(SYS_gettid);
记住 pthread_self 得到是线程自己的一个pthread_t ,这个玩意内核是没有数据结构记录的,应该只是一个用户态的东东,对他发信号,只需要 pthread_kill();
这个时候,你是不是开始跃跃欲试的,想试试看 tkill了? 于是你会这样写
测试进程:
static void* is_receive(void* arg) { while(1) { printf("%d LWP RUNNING\n", syscall(SYS_gettid); ); sleep(5); } return (void*)(-1); } int main(int argc, char *argv[]) { pthread_t t; pthread_attr_t prattr; pthread_attr_init(&prattr); pthread_attr_setdetachstate(&prattr, PTHREAD_CREATE_JOINABLE); pthread_attr_setscope(&prattr, PTHREAD_SCOPE_SYSTEM); pthread_create(&t, &prattr, is_receive, NULL); pause(); }
然后很开心的用
syscall(__NR_tkill, tid, SIGUSR1);
发送完了,你ps -ef一看,说,cao 。。不对啊,主线程也没了。
记住,如果你用任何测试信号的时候。 请记得他在 APUE书中写的默认行为 ,改整个进程退出的他们还是会退出,不是你发给 某个LWP,就可以改变Poxis的。。
所以你最好找那种默认行为为SIG_IGN的测试。。或者你自己之前改写了如SIGPIPE这样的信号行为。
于是你会得到你想要的结果。
然后你会说,我不想用tkill ,因为是custom 发信息给我。他不知道TID,只知道PID。这个时候你只能用kill(sigqueue)
于是谁去处理信号的问题,就需要你编程手动处理。
我们先来看看thread prime上怎么说:
我觉得T_P上面很多话都没用。 关于这个话题我提炼出一句话: 你需要发给哪个LWP ,把不需要的BLOCK就行!
代码看起来可能应该这样写。
void block_it(int sig) { sigset_t sig_block; sigemptyset(&sig_block); sigaddset( &sig_block, sig); sigprocmask( SIG_BLOCK, &sig_block, NULL ); //pthread_sigmask( SIG_BLOCK, &sig_block, NULL ); } static void* receive_signal(void* arg) { //block_it(SIGUSR2); while(1) { sleep(5); } return (void*)(-1); } static void just_printf(int arg){ printf("%d RECEIVING\n",syscall(SYS_gettid)); } int main(int argc, char *argv[]) { signal(SIGUSR2,just_printf); int tid; pthread_t t; pthread_attr_t prattr; pthread_attr_init(&prattr); pthread_attr_setdetachstate(&prattr,PTHREAD_CREATE_JOINABLE); pthread_attr_setscope(&prattr, PTHREAD_SCOPE_SYSTEM); pthread_create(&t, &prattr, receive_signal, NULL); block_it(SIGUSR2);//让子线程接受信号 pause(); }
看到block_it 这个函数了吗? 没错。just do it, 在信号来之前赶快哦。 这样你用kill(getpid(),SIG___) 你会发现他们工作的很好。 就算你个pid发送,子线程也帮你处理了这个信号~。
方法告诉你了,具体几千个LWP怎么指派某一个,就要自己发挥想象啦。 P_T中告诉你。。你可以用一个专门的信号处理线程~~
同样你也可能看到注释掉的pthread_sigmask() 为啥呢。因为
The function shall be equivalent to sigprocmask(), without the restriction that the call be made in a single-threaded process.
大家考虑一下,怎么实现一个异步IO ?
>
>
>
>
没错 ,如果了解 softirq 的人 都知道通过 参考ksoftirqd 线程
A: dedicated kernel<use process> thread ; B : work queues 在加上上面说的唤醒锁机制 就ok 啦