UNIX环境高级编程——线程和信号
每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。这意味着尽管单个线程可以阻止某些信号,但当线程修改了与某个信号相关的处理行为以后,所有的线程都必须共享这个处理行为的改变。这样如果一个信号选择忽略某个信号,而其他的线程可以恢复信号的默认处理行为,或者为信号设置一个新的处理程序,从而可以撤销上述线程的信号选择。
进程中的信号是送到单个线程的,如果信号与硬件故障或者计时器超时有关,该信号就被发送到引起该事件的线程中去,而其他的信号则被发送到任意一个线程。
sigprocmask的行为在多线程的进程中没有定义,线程必须使用pthread_sigmask。
int pthread_sigmask(int how,const sigset_t * newmask, sigset_t * oldmask);//返回值:若成功则返回0,否则返回错误编号
pthread_sigmask函数与sigprocmask函数基本相同,除了pthread_sigmask工作在线程中,并且失败时返回错误码,而不像sigprocmask中那样设置errno并返回-1.
线程可以通过调用sigwait等待一个或多个信号发生:
int sigwait(const sigset_t * set, int * signop);//返回值:若成功则返回0,否则返回错误编号
set参数指出了线程等待的信号集,signop指向的整数将作为返回值,表明发送信号的编号(即SIGINT对应2号)。
注意:sigwait函数所等待的信号集里的信号在之前必须被阻塞(即等待的信号需要添加到信号屏蔽字里面)。
sigwait函数将阻塞调用它的线程,直到收到它等待的信号发生了,然后sigwait将其从未决队列中取出(因为被阻塞了,所以肯定是未决了),但是有一点需要注意的是:它从未决队列取出之后,并不影响那个被取出的信号原来被阻塞的状态(即原来的信号屏蔽字不会改变的)。
它所做的工作只有两个:
- 等待它所等待的信号;
- 如果所等待的信号产生了,则将其从未决队列中移出来
如果多个线程调用sigwait时,且等待的是同一个信号,这时就会出现线程阻塞。当信号递送的时候,只有一个线程可以从sigwait中返回。如果信号被捕获(例如进程通过使用sigaction建立了一个信号处理程序)而且线程正在sigwait调用中等待同一个信号,那么则时候由操作系统实现来决定以何种方式递送信号。在这种情况下,操作系统实现可以让sigwait返回,也可以激活信号处理程序,但不能出现两者皆可的情况。(下面有代码验证,验证结果是:在Ubuntu 10.04系统下,sigwait的优先级高一些)
要把信号发送到进程,可以调用kill;要把信号发送到线程,可以调用pthread_kill:
#include <signal.h>
int pthread_kill(pthread_t thread,int signo);//返回值:若成功则返回0,否则返回错误编号。 线程不存在:ESRCH 信号不合法:EINVAL
可以传一个0值的signo来检查线程是否存在。如果信号的默认处理动作是终止进程,那么把信号传递给某个线程仍然会杀掉整个进程,所以我们用0号信号,这是一个保留信号,一个作用是用来判断线程是否还活着的。
kill()向进程发送信号,由哪个线程处理该信号是未知的。可能发生的情况是,进程本身屏蔽了该信号,而某个线程没有屏蔽改信号,进而该线程处理了该信号。
注意闹钟定时器是进程资源,并且所有的线程共享相同的alarm。所以进程中的多个线程不可能不干扰(或互不合作)的使用闹钟定时器。
示例代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h> void sig_thread_func(int sig)
{
printf("sig_thread_func : sig = %d\n", sig);
}
void sig_func(int sig)
{
printf("sig_func : sig = %d\n",sig);
} void *func1(void *arg)
{
signal(SIGUSR1, sig_thread_func); //线程1先运行,设置了signal sigset_t set;
sigfillset(&set);
sigdelset(&set, SIGUSR1);
pthread_sigmask(SIG_SETMASK, &set, NULL);//线程1屏蔽了除了SIGUSR1外的所有信号 printf("pthread 1 run\n");
int i;
for(i = 0; i < 7; ++i)
{
printf("1...\n");
sleep(1);
}
return 0;
}
void *func2(void *arg)
{
printf("pthread 2 run\n");
int i;
for(i = 0; i < 7; ++i)
{
printf("2...\n");
sleep(1);
}
return 0;
} int main()
{
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, func1, NULL);
pthread_create(&tid2, NULL, func2, NULL); sleep(1);
signal(SIGUSR1, sig_func); //覆盖了线程1设置的signal //向线程1发送SIGUSR1,SIGUSR2
sleep(1);
pthread_kill(tid1, SIGUSR1);//调动handler
sleep(1);
pthread_kill(tid1, SIGUSR2);//屏蔽了,无响应 //向线程2发送SIGUSR1,SIGUSR2
sleep(1);
pthread_kill(tid2, SIGUSR1);//调用handler
sleep(1);
//pthread_kill(tid2, SIGUSR2);//会终止进程,是进程! sigset_t set;
sigfillset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);//进程屏蔽了所有信号 sleep(1);
kill(getpid(), SIGUSR1);//调动handler?其实是线程1响应的 pthread_join(tid1, NULL);
pthread_join(tid2, NULL); return 0;
}
运行结果:
huangcheng@ubuntu:~$ ./a.out
pthread 2 run
2...
pthread 1 run
1...
2...
1...
2...
sig_func : sig = 10
1...
2...
1...
sig_func : sig = 10
2...
1...
2...
1...
2...
sig_func : sig = 10
1...
示例代码2:
#include<stdio.h>
#include<pthread.h>
#include<signal.h> static void sig_alrm(int signo);
static void sig_init(int signo);
int
main()
{
sigset_t set;
int sig;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_SETMASK, &set, NULL);//阻塞SIGALRM信号 signal(SIGALRM, sig_alrm);
signal(SIGINT, sig_init);
sigwait(&set, &sig);//sigwait只是从未决队列中删除该信号,并不改变信号掩码。也就是,当sigwait函数返回,它监听的信号依旧被阻塞。
switch(sig){
case 14:
printf("sigwait, receive signal SIGALRM\n");
/*do the job when catch the sigwait*/
break;
default:
break;
}
// sigdelset(&set, SIGALRM);
// pthread_sigmask(SIG_SETMASK, &set, NULL); for(;;)
{}
return 0;
} static void
sig_alrm(int signo)
{
printf("after sigwait, catch SIGALRM\n");
fflush(stdout);
return ;
} static void
sig_init(int signo)
{
printf("catch SIGINT\n");
return ;
}
执行命令:
第一个终端:
huangcheng@ubuntu:~$ ./a.out
第二个终端:
huangcheng@ubuntu:~$ kill -2 2682
huangcheng@ubuntu:~$ kill -2 2682
huangcheng@ubuntu:~$ kill -14 2682
huangcheng@ubuntu:~$ kill -14 2682
huangcheng@ubuntu:~$ kill -14 2682
huangcheng@ubuntu:~$ kill -14 2682
执行结果:
huangcheng@ubuntu:~$ ./a.out
catch SIGINT
catch SIGINT
sigwait, receive signal SIGALRM
结果说明:sigwait不会改变屏蔽字,仅仅是从未决队列中删除其等到的信号。
示例代码3:
#include<stdio.h>
#include<pthread.h>
#include<signal.h> static void sig_alrm(int signo);
static void sig_init(int signo);
int
main()
{
sigset_t set;
int sig;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_SETMASK, &set, NULL);//阻塞SIGALRM信号 signal(SIGALRM, sig_alrm);
signal(SIGINT, sig_init);
sigwait(&set, &sig);//sigwait只是从未决队列中删除该信号,并不改变信号掩码。也就是,当sigwait函数返回,它监听的信号依旧被阻塞。
switch(sig){
case 14:
printf("sigwait, receive signal SIGALRM\n");
/*do the job when catch the sigwait*/
break;
default:
break;
}
sigdelset(&set, SIGALRM);
pthread_sigmask(SIG_SETMASK, &set, NULL); for(;;)
{}
return 0;
} static void
sig_alrm(int signo)
{
printf("after sigwait, catch SIGALRM\n");
fflush(stdout);
return ;
} static void
sig_init(int signo)
{
printf("catch SIGINT\n");
return ;
}
运行命令:
第一个终端:
huangcheng@ubuntu:~$ ./a.out
第二个终端:
huangcheng@ubuntu:~$ kill -2 2704
huangcheng@ubuntu:~$ kill -2 2704
huangcheng@ubuntu:~$ kill -2 2704
huangcheng@ubuntu:~$ kill -14 2704
huangcheng@ubuntu:~$ kill -14 2704
huangcheng@ubuntu:~$ kill -14 2704
huangcheng@ubuntu:~$ kill -14 2704
执行结果:
huangcheng@ubuntu:~$ ./a.out
catch SIGINT
catch SIGINT
catch SIGINT
sigwait, receive signal SIGALRM
after sigwait, catch SIGALRM
after sigwait, catch SIGALRM
after sigwait, catch SIGALRM
结果说明:如果我们同时注册了信号处理函数,同时又用sigwait来等待这个信号,谁会取到信号?经过实验,Ubuntu 10.04上sigwait的优先级高。
UNIX环境高级编程——线程和信号的更多相关文章
- (八) 一起学 Unix 环境高级编程 (APUE) 之 信号
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- UNIX环境高级编程——线程属性
pthread_attr_t 的缺省属性值 属性 值 结果 scope PTHREAD_SCOPE_PROCESS 新线程与进程中的其他线程发生竞争. detachstate PTHREAD_CREA ...
- UNIX环境高级编程——线程同步之条件变量以及属性
条件变量变量也是出自POSIX线程标准,另一种线程同步机制.主要用来等待某个条件的发生.可以用来同步同一进程中的各个线程.当然如果一个条件变量存放在多个进程共享的某个内存区中,那么还可以通过条件变量来 ...
- UNIX环境高级编程——线程
线程包含了表示进程内执行环境必需的信息,其中包括进程中标示线程的线程ID.一组寄存器值.栈.调度优先级和策略.信号屏蔽字.errno变量以及线程私有数据. 进程的所有信息对该进程的所有线程都是共享的, ...
- UNIX环境高级编程——线程和fork
当线程调用fork时,就为子进程创建了整个进程地址空间的副本.子进程通过继承整个地址空间的副本,也从父进程那里继承了所有互斥量.读写锁和条件变量的状态.如果父进程包含多个线程,子进程在fork返回以后 ...
- UNIX环境高级编程——线程同步之互斥锁、读写锁和条件变量(小结)
一.使用互斥锁 1.初始化互斥量 pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;//静态初始化互斥量 int pthread_mutex_init( ...
- Unix 环境高级编程---线程创建、同步、
一下代码主要实现了linux下线程创建的基本方法,这些都是使用默认属性的.以后有机会再探讨自定义属性的情况.主要是为了练习三种基本的线程同步方法:互斥.读写锁以及条件变量. #include < ...
- 《UNIX环境高级编程》笔记--信号集
1.信号集基本操作 我们需要有一个能表示多个信号--信号集(signal set)的数据类型.POSIX.1定义了数据类型sigset_t以包含一个信号 集,并且定义了一下五个处理信号处理信号集函数. ...
- UNIX环境高级编程——线程私有数据
线程私有数据(Thread-specific data,TSD):存储和查询与某个线程相关数据的一种机制. 在进程内的所有线程都共享相同的地址空间,即意味着任何声明为静态或外部变量,或在进程堆声明的变 ...
随机推荐
- 谷歌发布 TensorFlow Serving
TensorFlow服务是一个灵活的,高性能的机器学习模型的服务系统,专为生产环境而设计. TensorFlow服务可以轻松部署新的算法和实验,同时保持相同的服务器体系结构和API. TensorFl ...
- 单页应用动态设置页面title
1.适用场景:所有通过router路由的单页应用. 2.示例代码:本文以vue-router为例. 在router.js中: let router = new Router({ routes: [ { ...
- vue通过id从列表页跳转到对应的详情页
1. 列表页:列表页带id跳转到详情页 详情页:把id传回到后台就可以获取到数据了 2.列表页跳转到详情页并更改详情页的标题 列表页:带id和页面标题的typeid跳转到详情页 详情页:在html绑定 ...
- Vue结合slot插槽分发父组件内容实现高度复用、更加灵活的dialog组件
之前写过一篇关于vue实现dialog会话框组件的文章(http://www.cnblogs.com/fozero/p/8546883.html)[http://www.cnblogs.com/foz ...
- ListView常见的优化方式简述
ListView的优化 对于ListView来说,应该算是布局中几种最常用的组件之一了,使用也十分方便,下面个大家介绍一下两种常见的优化方式. 1.条目复用优化 其实listview的工作原理就是,l ...
- 剑指Offer——企业级项目中分层的含义与依据及多态的优势
剑指Offer--企业级项目中分层的含义与依据及多态的优势 关于以上两点,由于项目经验较少,自己不是很明白,特整理如下. 常见分层架构模式 三层架构 3-tier architecture 微 ...
- java创建线程
创建一个线程 Java提供了两种创建线程方法: 通过实现Runable接口: http://blog.csdn.net/duruiqi_fx/article/details/52187275 通过继承 ...
- C语言如何在两个文件中访问同一个全局变量
方法一: 不使用头文件. 1.c 中 int var; 2.c 中 extern int var; 方法二: 使用头文件. 1.c 中 int var; 不必添加#include "1.h& ...
- ASCII 大文字生成器
display text in large ASCII art fonts 显示大ASCII艺术字体 这种东西在源码声明或者软件初始化控制台打印时候很有用. 例如打开: http://www.oran ...
- 分析MapReduce执行过程+统计单词数例子
MapReduce 运行的时候,会通过 Mapper 运行的任务读取 HDFS 中的数据文件,然后调用自己的方法,处理数据,最后输出.Reducer 任务会接收 Mapper 任务输出的数据,作为自己 ...