[Linux] 进程中的信号:产生、处理、信号集操作(上)

信号的相关概念

  • 是什么? 信号是进程间事件异步通知的一种方式,属于软中断。
  • 查看信号列表: kill -l
  • 给指定进程发信号: kill -signum +进程id

信号的产生方式

1)通过终端按键产生信号

SIGINT默认动作是终止进程,SIGQUIT默认动作是终止进程且core dump。

2)调用系统函数向进程发信号

  • int kill(pid_t pid, int signo):给指定进程发送信号
  • int raise(int signo):给自己发送signo信号
  • void abort(void):是当前进程接收到信号而异常终止,abort函数总会成功
  • main函数中使用命令行参数:kill(atoi(argv[1]), atoi(argv[2])):
int main(int argc, char* argv[]){  //argv[0]表示可执行程序
	printf("My pid id %d", getpid());
	signal(2, handler);
	if(argc==3){
		kill(atoi(argv[1]), atoi(argv[2]));
	}
}

3)由软件条件产生信号

调用alarm(unsigned int seconds)函数设定一个闹钟,告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。

4)硬件异常产生信号

内核检测到硬件异常会向进程发送适当的信号,比如:
除0操作----CPU运算单元异常
访问非法地址、野指针----MMU异常
注:野指针不存在虚拟地址,不会分配物理内存,在页表+MMU转换时就会发现地址转换错误。

信号的处理方式

  1. 忽略此信号
  2. 执行该信号默认的处理动作
  3. 捕捉一个信号:提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行该函数。
void handler(int signo){  //自定义一个信号处理函数
	printf("catch a signal: %d\n", signo);
}
int main(int argc, char* argv[]){
	printf("My pid id %d", getpid());
	signal(2, handler);  //当向进程发送2号信号时,就会调用handler函数去处理该信号
}

信号的产生流程

信号的产生流程图

信号在内核中的示意图

信号在内核中的示意图

1)信号的发送及记录

  • 信号的记录是在进程的PCB,用sigbitmap位图来保存信号数据,记录信号是否产生:比特位的位置表示信号的编号,比特位的内容表示是否收到信号;
  • 所以说,信号的发送本质是OS直接去修改目标进程PCB中的信号位图,只有OS有资格进行信号的发送。

2)信号相关操作

  • sigset_t表示信号集,sigset mask表示阻塞信号集,也叫信号屏蔽字;
  • int sigpending(sigset_t *set)----获取当前进程未决的信号集,set为输出型参数;
  • int sigprocmask(int how, const sigset_t *set, sigset_t *oset)----读取或者更改信号屏蔽字,若set、oset非空,在设置信号屏蔽字之前,先把原来的信号屏蔽字备份到oset中,再根据set和how的参数更改信号屏蔽字。

3)信号集操作函数

  • int sigemptyset(sigset_t *set)----清空信号集
  • int sigismember(const sigset_t *set, int signo)----判断signo信号是否在信号集中
  • int sigaddset(sigset_t *set, int signo)----添加signo信号到信号集中

4)测试用例

用例说明:屏蔽2号信号,获取其未决状态的信息(pending表中2号信号的比特位内容为1);一段时间之后取消对2号信号的阻塞,获取其递达状态的信息(pending表中2号信号的比特位内容为0

//普通信号:1-31号
void show_pending(sigset_t *pending){  //方便我们观察pending表中各信号的状态
	int sig=1;
	for(; sig<=31; sig++){
		if(sigismember(pending, sig)){
			 printf("1");
		}
		else{
			printf("0");
		}
	}
	printf("\n");
}

void handler(int signo){  //自定义信号处理函数
	printf("catch a signo: %d\n");
}

int main(){
//ctrl+C之后,2号信号产生
//此时可以使用信号捕捉,继续获取其后的pending表的状态信息
signal(2, handler);

//一旦接收到2号信号(未决状态),对应Pending表中的内容就会由0变为1
sigset_t block;
sigset_t oblock;
sigemptyset(&block);
sigemptyset(&oblock);
sigaddset(&block, 2);  //屏蔽2号信号集:将其block表的比特位内容由0置为1

//在设置信号屏蔽字之前,先把原来的信号屏蔽字备份到oblock中
sigprocmask(SIG_SETMASK, &block, &oblock);

//展示pending表
sigset_t pending;
int count = 0;
while(1){
	sigemptyset(&pending);
	sigpending(&pending);  //获取当前进程的pending信号集
	show_pending(&pending);
	sleep(1);
	count++;
	if(count == 10){  //一段时间之后,解除对2号信号的阻塞
		printf("recover sig mask!\n");
		sigprocmask(SIG_SETMASK, &oblock, NULL);
	}	
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值