系统编程三:信号处理+信号阻塞属性

一、关于信号的函数接口

1、如何发送信号给另外一个进程呢?(kill)

命令

kill -信号值  进程的ID号
killall -信号值  进程的名字 

函数 

       kill - send signal to a process
        //发送一个信号给一个进程
SYNOPSIS
       #include <sys/types.h>
       #include <signal.h>

       int kill(pid_t pid, int sig);

参数:
        pid 你要给哪个进程发送信号,将这个进程的ID号传递进来
        sig 你要发送哪个信号,将这个信号的名字  或者 信号值 传递过来

返回值:
        成功 返回  0
        失败 -1

2、捕捉信号  signal() ---->man  2 signal

#include <signal.h>

    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum, sighandler_t handler); --man 2 signal

    上面两行展开可写成: void (*signal(int sig, void (*func)(int)))(int);  --man 3 signal

    
解析:     返回值类型                    这个是一个函数          返回值类型
        void (*      signal(int sig, void (*func)(int))    )(int);
    
    
函数作用: 捕捉一个指定的信号,去执行 信号响应函数 
参数:
    signum    你要捕捉哪个信号,将这个信号值 或者 信号的名字传递过来
    handler    捕捉到这个信号之后,你要去做的事情(去执行这个函数,将这个函数的名字传递过来)
                这个函数 的 类型 必须是  void  func(int a)

返回值:
    成功 返回 处理函数的地址(第二个参数)
    失败 返回  SIG_ERR

注意:
1、所谓的捕捉信号 就是 获取当这个信号来之后,去执行 信号响应函数,原本的信号默认动作就不会执行了。
2、当调用signal 函数之后 ,程序不会阻塞,而是往下面代码执行,但是这个捕捉设置 是 全局有效
3、SIGKILL 、SIGSTOP 不能被捕捉,只能执行默认的动作

练习

进程1:发送信号

#include<stdio.h>
#include <sys/types.h>
#include <signal.h>

int main(int argc, char*argv[]) // ./a.out 3445
{
	//获取你要发送的信号的进程的ID号
	int id = atoi(argv[1]);
	//发送信号
	kill(id,SIGINT);
	
	return 0;
}

进程二:

捕捉信号SIGUSR1 和 SIGUSR2,执行 对应的 信号响应 函数。

#include<stdio.h>
#include <sys/types.h>
#include <signal.h>

/*
练习1:

进程1: 发送信号 

进程2: 捕捉信号SIGUSR1 和 SIGUSR2,执行 对应的 信号响应 函数 
*/

//信号响应函数 ,也就是说signal捕捉到指定的信号之后,去执行这个函数
void signalHandle1(int arg) //arg 信号值
{
	printf("arg:%d\n",arg);
	printf("signalHandle1 听说你想杀死我\n");
}

void signalHandle2(int arg) //arg 信号值
{
	printf("arg:%d\n",arg);
	printf("signalHandle2 听说你想杀死我\n");
}

int main(int argc, char*argv[]) // ./a.out 3445
{
	//捕捉信号SIGUSR1,去执行  信号响应函数
	signal(SIGUSR1,signalHandle1); 

	//捕捉信号SIGUSR2,去执行  信号响应函数
	signal(SIGUSR2,signalHandle2); 
	
	while(1)
	{
		
	}

	return 0;
}

3、挂起进程,直到收到一个信号为止。 pause() --->man 2 pause

NAME
       pause - wait for signal  将当前进程挂起,直到收到一个信号为止

SYNOPSIS
       #include <unistd.h>

       int pause(void);

#include<stdio.h>
#include <sys/types.h>
#include <signal.h>



int main(int argc, char*argv[]) // ./a.out 3445
{
	printf("main start\n");
	//将当前进程挂起,直到收到一个信号,程序才会往下面执行
	pause();
	
	printf("end\n");

	return 0;
}

4、自己给自己发送信号。----》raise

#include <signal.h>

    int raise(int sig);

#include<stdio.h>
#include <sys/types.h>
#include <signal.h>

//信号响应函数 ,也就是说signal捕捉到指定的信号之后,去执行这个函数
void signalHandle1(int arg) //arg 信号值
{
	printf("arg:%d\n",arg);
	printf("signalHandle1 听说你想杀死我\n");
}


int main(int argc, char*argv[]) // ./a.out 3445
{
	printf("main start\n");

	//捕捉信号SIGUSR1,去执行  信号响应函数
	signal(SIGUSR1,signalHandle1); 
	
	while(1)
	{
		sleep(1);
		//自己给自己发送信号SIGUSR1
		raise(SIGUSR1);
	}
	
	

	return 0;
}

二、信号的处理(除了SIGKILL SIGSTOP)

1)忽略(将信号丢弃)
    signal(signum,SIG_IGN)
2)缺省(执行默认的动作)
    signal(signum,SIG_DFL)
3)捕捉(去执行 信号响应函数)
    signal(signum,signalHandle)
4)阻塞(将信号挂起)
    设置阻塞之后,来了阻塞的指定信号,并不是将信号丢弃,而且将信号挂起来,等到解除阻塞之后才去响应这个信号
    
注意: SIGKILL 和 SIGSTOP 这两个信号不能被忽略 、阻塞、捕捉    ,必须是执行默认动作

//忽略信号
#include<stdio.h>
#include <sys/types.h>
#include <signal.h>


int main(int argc, char*argv[]) // ./a.out 3445
{
	printf("main start\n");

	//捕捉信号SIGINT,然后 忽略掉这个信号
	signal(SIGINT,SIG_IGN); 
	
	while(1)
	{

	}
	
	

	return 0;
}

三、linux系统 信号集

1、什么是信号集

        信号集是一个集合,这个集合中每个成员都是 信号,通过将信号加入到信号集中,再设置阻塞状态给信号集,那么整个信号集中里面所有的信号都会变成阻塞的状态。

2、信号阻塞 与 信号忽略 有什么区别?

信号响应: 收到信号之后,去执行 信号响应函数 
信号忽略: 收到这个信号之后,直接丢弃这个信号。
信号阻塞: 进程在阻塞某一个信号的前提下,收到了这个信号之后,不会马上响应,而是挂起来,要等到解除阻塞之后,才会响应这个信号。
            (放在一个挂起队列中)

四、信号集处理函数?

1、信号集变量 如何去定义 ?

        类似定义一个数组 ,存储 里面要阻塞的多个信号 

        信号集 数据类型   sigset_t
    比如 定义一个信号集变量    sigset_t  set;

#include <signal.h>

int sigemptyset(sigset_t *set); //清空信号集

int sigfillset(sigset_t *set);  //将Linux下的所有的信号都加入到信号集中set

int sigaddset(sigset_t *set, int signum); //在指定的信号集set中,添加一个指定的信号signum到集合中

int sigdelset(sigset_t *set, int signum);//在指定的信号集set中,从集合中删除一个指定的信号signum

int sigismember(const sigset_t *set, int signum);//判断一个指定的信号 signum 是否 在 集合 中 


返回值 : 
        成功 返回 0
        失败返回 -1

        sigismember  :如果有这个信号 返回 1
                       如果没有 返回  0

2、例子

例子:写一个程序,先清空信号集,再把SIGUSR1  SIGUSR2 信号添加到集合中,判断SIGUSR2信号是否在集合中

1)先定义一个信号集变量 

2) 初始化(清空)

3)将信号 添加到集合中 SIGUSR1  SIGUSR2

4) 判断 SIGUSR2信号是否在集合中,如果在打印yes  ,否则 打印 no

#include<stdio.h>
#include <sys/types.h>
#include <signal.h>

/*
例子:写一个程序,先清空信号集,再把SIGUSR1  SIGUSR2 信号添加到集合中,判断SIGUSR2信号是否在集合中

1)先定义一个信号集变量 

2) 初始化(清空)

3)将信号 添加到集合中 SIGUSR1  SIGUSR2

4) 判断 SIGUSR2信号是否在集合中,如果在打印yes  ,否则 打印 no
*/

int main(int argc, char*argv[]) // ./a.out 3445
{
	//1)先定义一个信号集变量 
	sigset_t  set;
	//2) 初始化(清空)
	sigemptyset(&set); //清空信号集
	//3)将信号 添加到集合中 SIGUSR1  SIGUSR2
	sigaddset(&set,SIGUSR1); //在指定的信号集set中,添加一个指定的信号signum到集合中
	sigaddset(&set,SIGUSR2); 
	
	//4) 判断 SIGUSR2信号是否在集合中,如果在打印yes  ,否则 打印 no
	// sigismember 信号在集合中 返回1  否则 返回 0
	if(sigismember(&set, SIGUSR2))
		printf("yes\n");
	else 
		printf("no\n");
	

	return 0;
}

五、如何将信号集中的信号 全部设置为 阻塞状态?---》man 2 sigprocmask

#include <signal.h>

    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

函数作用: 将信号集中的信号 设置 为 阻塞 状态  或者 解除 阻塞 

参数: 
        how:
            SIG_BLOCK-----》设置为阻塞的属性

            SIG_UNBLOCK-----》解除阻塞
        set: 你要设置 哪个信号集 
        oldset:原来的状态,如果不想获取原先设置的状态,一般我们设置为NULL
返回值: 
        成功 返回   0
        失败 返回  -1
    
比如:将一个信号集设置为阻塞状态 ,注意对象是信号阻塞,而不是进程阻塞
        sigset_t  set;
        sigprocmask(SIG_BLOCK,&set, NULL);
    
比如:解除阻塞
        sigset_t  set;
        sigprocmask(SIG_UNBLOCK,&set, NULL);

1、练习1

创建一个子进程,父进程将SIGUSR1 加入到集合中,判断信号在不在集合中,再设置该信号为阻塞状态,
该状态持续10S(那么在10S内,对SIGUSR1都是阻塞的),10S后,解除阻塞,看看会不会响应信号?
子进程在10S内发送一个SIGUSR1给父进程。
            主要看:信号发过去之后,如果是马上响应-------------信号响应 
                                    如果是10S后信号响应--------信号阻塞 ,对 
                                    如果是永远都不会响应 ------信号忽略

#include<stdio.h>
#include <sys/types.h>
#include <signal.h>

/*
例子:写一个程序,先清空信号集,再把SIGUSR1  SIGUSR2 信号添加到集合中,判断SIGUSR2信号是否在集合中

1)先定义一个信号集变量 

2) 初始化(清空)

3)将信号 添加到集合中 SIGUSR1 

4) 判断 SIGUSR2信号是否在集合中,如果在打印yes  ,否则 打印 no
*/

void signalHandle(int arg)
{
	printf("%d signalHandle\n",arg);
}

int main(int argc, char*argv[]) // ./a.out 3445
{
	//将SIGUSR1信号   设置 一个信号响应函数 
	signal(SIGUSR1, signalHandle);
	
	
	//1)先定义一个信号集变量 
	sigset_t  set;
	//2) 初始化(清空)
	sigemptyset(&set); //清空信号集
	//3)将信号 添加到集合中 SIGUSR1  
	sigaddset(&set,SIGUSR1); //在指定的信号集set中,添加一个指定的信号signum到集合中
	
	//4) 判断 SIGUSR1信号是否在集合中,如果在打印yes  ,否则 打印 no
	// sigismember 信号在集合中 返回1  否则 返回 0
	if(sigismember(&set, SIGUSR1))
		printf("yes\n");
	else 
		printf("no\n");
	
	//将信号集中的所有的信号设置为 阻塞状态 
	//注意:设置的对象是信号,而不是进程,所以调用这个函数之后,程序还是会往下面走
	sigprocmask(SIG_BLOCK,&set,NULL);
	
	sleep(20);//延时20S 
	
	//再解除阻塞,也就是说,上面的20S之内,如果收到了信号SIGUSR1,那么进程会将这个信号挂起来,等到解除阻塞之后再执行信号响应函数
	sigprocmask(SIG_UNBLOCK,&set,NULL);
	

	return 0;
}

2、练习

#include<stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>

/*
练习4:验证阻塞属性会被子进程继承。
		(假设父进程对某一个信号 SIGUSR1是阻塞的,那么带着这个状态产生的小孩,这个小孩是不是也对这个信号是阻塞的。)

*/

void signalHandle(int arg)
{
	printf("%d signalHandle\n",arg);
}

int main(int argc, char*argv[]) // ./a.out 3445
{	
	//将SIGUSR1信号   设置 一个信号响应函数 
	signal(SIGUSR1, signalHandle);
	
	//1)先定义一个信号集变量 
	sigset_t  set;
	//2) 初始化(清空)
	sigemptyset(&set); //清空信号集
	//3)将信号 添加到集合中 SIGUSR1  
	sigaddset(&set,SIGUSR1); //在指定的信号集set中,添加一个指定的信号signum到集合中
	
	//4) 判断 SIGUSR1信号是否在集合中,如果在打印yes  ,否则 打印 no
	// sigismember 信号在集合中 返回1  否则 返回 0
	if(sigismember(&set, SIGUSR1))
		printf("yes\n");
	else 
		printf("no\n");
	
	//将信号集中的所有的信号设置为 阻塞状态 
	//注意:设置的对象是信号,而不是进程,所以调用这个函数之后,程序还是会往下面走
	sigprocmask(SIG_BLOCK,&set,NULL);
	

	
	//上面把信号 设置 成 阻塞状态之后,再创建子进程 
	pid_t id = fork();
	if(id >0)//父进程
	{
		
	}
	else if(id == 0)//子进程
	{
		printf("child :%d\n",getpid());
		sleep(20);//延时20S 
	
		//再解除阻塞,也就是说,上面的20S之内,如果收到了信号SIGUSR1,那么进程会将这个信号挂起来,等到解除阻塞之后再执行信号响应函数
		sigprocmask(SIG_UNBLOCK,&set,NULL);
		
		exit(0);		
	}
	//等待子进程退出 
	wait(NULL);

	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值