当前位置: 首页 > news >正文

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的优先级高。

转载于:https://www.cnblogs.com/hehehaha/p/6332635.html

相关文章:

  • 忘记mysql数据库root密码
  • SqlServer快捷键整理
  • 分析cocos2d-x中的CrystalCraze示例游戏
  • ios开发之导航控制器的原理
  • Windows下安装使用curl命令
  • paip.数据库发邮件通知配置
  • 20145328 《Java程序设计》实验二实验报告
  • UNIX网络编程——经常使用的套接字选项
  • 【Mysql 学习】SQL服务器模式
  • struts-default.xml解读
  • 用POP动画编写带富文本的自定义动画效果
  • 〖Linux〗不知谁写的,很实用的Linux命令
  • 带动画渐进效果与颜色渐变的圆弧进度控件设计
  • [Android]Tool-Systrace
  • 抱歉,我不接私单了
  • 【跃迁之路】【519天】程序员高效学习方法论探索系列(实验阶段276-2018.07.09)...
  • 5、React组件事件详解
  • Akka系列(七):Actor持久化之Akka persistence
  • cookie和session
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • Koa2 之文件上传下载
  • Node 版本管理
  • php的插入排序,通过双层for循环
  • Python_OOP
  • 后端_MYSQL
  • 前端之React实战:创建跨平台的项目架构
  • 数组大概知多少
  • 数组的操作
  • 微信小程序开发问题汇总
  • 终端用户监控:真实用户监控还是模拟监控?
  • No resource identifier found for attribute,RxJava之zip操作符
  • 你对linux中grep命令知道多少?
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • ​卜东波研究员:高观点下的少儿计算思维
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • (pojstep1.1.2)2654(直叙式模拟)
  • (Redis使用系列) SpringBoot 中对应2.0.x版本的Redis配置 一
  • (差分)胡桃爱原石
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (二)正点原子I.MX6ULL u-boot移植
  • (篇九)MySQL常用内置函数
  • (四)c52学习之旅-流水LED灯
  • (转)平衡树
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • .CSS-hover 的解释
  • .NET Core 和 .NET Framework 中的 MEF2
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .Net MVC + EF搭建学生管理系统
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .NET 材料检测系统崩溃分析
  • .Net转Java自学之路—SpringMVC框架篇六(异常处理)
  • /run/containerd/containerd.sock connect: connection refused
  • [ 渗透工具篇 ] 一篇文章让你掌握神奇的shuize -- 信息收集自动化工具
  • [Android Pro] android 混淆文件project.properties和proguard-project.txt