欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

信号

程序员文章站 2022-07-12 10:27:39
...

目录

7.1 信号的概念

7.2 进程处理信号行为

7.3 信号集处理函数

7.4 阻塞信号集

7.5 信号捕捉设定

7.6 C标准库信号处理函数

7.7 SIGCHLD信号处理




7.1 信号的概念

什么是信号:

信号是UNIX系统响应某些状况而产生的事件,进程在接收到信号时会采取相应的行动。

信号的特点:

简单,不能携带大量信息,满足特定条件才会发生。信号也叫软中断,有可能会有延迟。

信号的实现机制:

信号实际上是由内核发送,内核来处理收到的信号。收到信号的进程,必须对信号做出处理(忽略,捕获,默认动作都行)

信号的产生:

信号

信号状态:

产生
递达:信号被捕捉并处理
未决:信号被阻塞

信号四要素:

编号、事件、名称、默认处理动作

7.2 进程处理信号行为
1、默认动作
2、忽略
3、捕捉
(后面两种处理行为就需要涉及到信号集了)
7.3 信号集处理函数

(1)首先要了解什么是信号集:

信号集被定义为一种数据类型:

typedef struct {

                       unsigned long sig[_NSIG_WORDS]} sigset_t

//信号集用来描述信号的集合,每个信号占用一位(64位)。
//Linux所支持的所有信号可以全部或部分的出现在信号集中,主要与信号阻塞相关函数配合使用。

//下面是为信号集操作定义的相关函数:

#include <signal.h>

int sigemptyset(sigset_t *set)int sigfillset(sigset_t *set)int sigaddset(sigset_t *set, int signum)int sigdelset(sigset_t *set, int signum)int sigismember(const sigset_t *set, int signum)

(2)信号集处理函数:

那我们一个一个来看

int sigemptyset(sigset_t *set); //清空信号集嘛,传参传什么也应该很清楚了
初始化由set指定的信号集,信号集里面的所有信号被清空,相当于64为置0;

int sigfillset(sigset_t *set);//填满信号集嘛
调用该函数后,set指向的信号集中将包含linux支持的64种信号,相当于64为都置1;

int sigaddset(sigset_t *set, int signum);//添加信号进去
在set指向的信号集中加入signum信号,相当于将给定信号所对应的位置1;

int sigdelset(sigset_t *set, int signum);//删除信号出去
在set指向的信号集中删除signum信号,相当于将给定信号所对应的位置0;

int sigismember(const sigset_t *set, int signum);//看看是不是已经在里面了
判定信号signum是否在set指向的信号集中,相当于检查给定信号所对应的位是0还是1。

好,看完上面这些处理函数,其实这几个函数真的就是对信号集进行操作而已,而不会对具体信号有什么动作。
别急

7.4 阻塞信号集

阻塞信号集也叫做当前进程的信号屏蔽字。
这里的屏蔽应该理解为阻塞而非忽略

(1)sigprocmask
调用sigprocmask函数可以读取或更改进程的信号屏蔽字。

#include<signal.h>

int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);
//成功返回0,失败返回-1

参数释义:
set:用于更改的信号集
oldset:用于传出原信号集
how:怎么操作set

how的参数选择:
SIG_BLOCK:set包含了我们希望添加到当前信号屏蔽字的信号
SIG_UNBLOCK:set包含了我们希望从当前信号屏蔽字移除的信号
SIG_SETMASK:设置当前信号屏蔽字为set所指向的值。

这个要生效的话,至少需要有其中一个信号的驱动(就是哪个倒霉的过来阻塞一下)

(2)sigpending

#include<signal.h>

int sigpending(sigset_t *set);

//成功返回0,失败返回-1

功能:读取当前进程的未决信号集,通过set参数传出。

到这里差不多可以去弄信号集了

7.5 信号捕捉设定

防止信号意外死亡
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

参数释义

signum 捕捉的信号
act 传入的动作
oldact 原动作

信号

我去借鉴了一段代码:捕捉我们给自己定时发送的14号自杀信号:

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/time.h>
 
//定义捕捉函数
void catch_sig(int num){
	printf("catch %d sig\n", num);
}
 
int main(){
	//注册捕捉函数
	struct sigaction act;
	//说明为你使用的是sigaction结构体中的第一个捕捉函数
	act.sa_flags=0;
	//那个捕捉函数的函数指针指向我们上面自己写的捕捉函数catch_sig
	act.sa_handler=catch_sig;
	//清空信号集
	sigemptyset(&act.sa_mask);
	sigaction(SIGALRM, &act, NULL);
 
	//setitimer 5秒之后每隔3秒来一次信号
	struct itimerval myit={{3,0},{5,0}};  //这个后续章节会提到,我现在也不是很清楚
	setitimer(ITIMER_REAL, &myit, NULL);//还有这个
	while(1){
		printf("Who can kill me!\n");
		sleep(1);
	}	
	return 0;
}

7.6 C标准库信号处理函数

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

//signum 要捕捉的信号
//handler 要执行的捕捉函数指针,函数应该声明 void func(int);//函数名可变

7.7 SIGCHLD 信号处理

17号信号

产生条件:

子进程结束时
子进程收到SIGSTOP信号时
子进程处在睡眠状态,收到SIGCONT信号唤醒时

哎,这个有点不好搞,我先把其他方法搞清楚咯
代码也是抄来的,贴这儿了

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
 
void catch_sig(int num){
	pid_t pid=waitpid(-1, NULL, WNOHANG);
	if(pid>0){
		printf("wait child %d ok\n", pid);
	}
}
 
int main(){
	int i=0;
	pid_t pid;
	for(i=0; i<10; i++){
		pid=fork();
		if(pid==0){
			break;
		}
	}
	if(i==10){
		//father
		struct sigaction act;
		act.sa_flags=0;
		sigemptyset(&act.sa_mask);
		act.sa_handler=catch_sig;
		sigaction(SIGCHLD, &act, NULL);
		while(1){
			sleep(1);
		}
	}else if(i<10){
		printf("I am %d child, pid=%d\n", i, getpid());
		sleep(i);
	}
	return 0;
}

相关标签: Linux学习笔记