Linux-IPC之信号

本文详细介绍了Linux中的信号机制,包括信号概念、signal处理、sigaction注册、sigprocmask信号阻塞、kill信号发送函数以及计时器与信号的结合使用,如睡眠函数和时钟处理。通过实例解析了不同计时器类型及其参数设置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、信号概念

信号是进程在运行过程中,由自身产生或由进程外部发过来的消息(事件)。信号是硬件中断的软件模拟(软中断)。每个信号用一个整型常量宏表示,以SIG开头,比如SIGCHLD、SIGINT等,它们在系统头文件<signal.h>中定义,也可以通过在shell下键入kill –l查看信号列表,或者键入man 7 signal查看更详细的说明。
信号的生成来自内核,让内核生成信号的请求来自3个地方:
1.用户:用户能够通过输入CTRL+c、Ctrl+\,或者是终端驱动程序分配给信号控制字符的其他任何键来请求内核产生信号;
2.内核:当进程执行出错时,内核会给进程发送一个信号,例如非法段存取(内存访问违规)、浮点数溢出等;
3.进程:一个进程可以通过系统调用kill给另一个进程发送信号,一个进程可以通过信号和另外一个进程进行通信。
由进程的某个操作产生的信号称为同步信号(synchronous signals),例如除0;由像用户击键这样的进程外部事件产生的信号叫做异步信号(asynchronous signals)。
进程接收到信号以后,可以有如下3种选择进行处理:
1.接收默认处理:接收默认处理的进程通常会导致进程本身消亡。例如连接到终端的进程,用户按下CTRL+c,将导致内核向进程发送一个SIGINT的信号,进程如果不对该信号做特殊的处理,系统将采用默认的方式处理该信号,即终止进程的执行; signal(SIGINT,SIG_DFL);
2.忽略信号:进程可以通过代码,显示地忽略某个信号的处理,例如:signal(SIGINT,SIG_IGN);但是某些信号是不能被忽略的,例如9号信号;
3.捕捉信号并处理:进程可以事先注册信号处理函数,当接收到信号时,由信号处理函数自动捕捉并且处理信号。

有两个信号既不能被忽略也不能被捕捉,它们是SIGKILL和SIGSTOP。即进程接收到这两个信号后,只能接受系统的默认处理,即终止进程。SIGSTOP是暂停进程。

二、signal信号处理机制

可以用函数signal注册一个信号捕捉函数。原型为:
#include <signal.h>
typedef void (*sighandler_t)(int);  //函数指针
sighandler_t  signal(int signum, sighandler_t handler);

signal的第1个参数signum表示要捕捉的信号,第2个参数是个函数指针,表示要对该信号进行捕捉的函数,该参数也可以是SIG_DFL(表示交由系统缺省处理,相当于白注册了)或SIG_IGN(表示忽略掉该信号而不做任何处理)。signal如果调用成功,返回以前该信号的处理函数的地址,否则返回SIG_ERR。
sighandler_t是信号捕捉函数,由signal函数注册,注册以后,在整个进程运行过程中均有效,并且对不同的信号可以注册同一个信号捕捉函数。该函数只有一个整型参数,表示信号值。

示例:
//捕捉终端Ctrl+c产生的SIGINT信号
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

int main()
{
	typedef void (*sigp)(int);
	sigp p;
	p=signal(SIGINT,SIG_IGN);
	if(SIG_ERR==p)
	{
		perror("signal");
		return -1;
	}
	while(1);
	return 0;
}

运行结果:

捕捉Ctrl+c产生的信号,所以Ctrl+c无法终止进程,Ctrl+\结束进程

//捕捉终端Ctrl+\产生的SIGQUIT信号,signal第二个参数直接传函数名
#include <stdio.h>
#include <signal.h>

void sig(int signum)
{
	printf("signum is %d\n",signum);
}
int main()
{
	signal(SIGQUIT,sig);
	while(1);
	return 01;
}

运行结果:

捕捉ctrl+c产生的SIGQUIT信号,输出SIGQUIT的值,Ctrl+c结束进程

累计发送只会执行两次:


//多个信号
#include <stdio.h>
#include <signal.h>

void sig(int signum)
{
	printf("signum is %d\n",signum);
	sleep(5);
	printf("after sleep , the signum is %d\n",signum);
}
int main()
{
	signal(SIGQUIT,sig);//Ctrl+\产生的信号
	signal(SIGINT,sig);//Ctrl+c产生的信号
	while(1);
	return 01;
}

运行结果:


结论:
1、自己不能打断自己
2、可以互相打断,不能累计

在signal处理机制下,还有许多特殊情况需要考虑:
1、注册一个信号处理函数,并且处理完毕一个信号之后,是否需要重新注册,才能够捕捉下一个信号;(不需要)
2、如果信号处理函数正在处理信号,并且还没有处理完毕时,又发生了一个同类型的信号,这时该怎么处理;(挨着执行),后续相同信号忽略(会多执行一次)。
3、如果信号处理函数正在处理信号,并且还没有处理完毕时,又发生了一个不同类型的信号,这时该怎么处理;(跳转去执行另一个信号,之后再执行剩下的没有处理完的信号)
4、如果程序阻塞在一个系统调用(如read(...))时,发生了一个信号,这时是让系统调用返回错误再接着进入信号处理函数,还是先跳转到信号处理函数,等信号处理完毕后,系统调用再返回。


示例:
//signla_read.c
#include <stdio.h>
#include <signal.h>

void sig(int signum)
{
	printf("signum is %d\n",signum);
	sleep(5);
	printf("after sleep , the signum is %d\n",signum);
}
int main()
{
	signal(SIGINT,sig);
	int ret;
	char buf[10]={0};
	ret=read(0,buf,sizeof(buf));
	printf("buf is %s\n",buf);
	return 0;
}

运行结果:


结论:
如果不输入字符串,程序一直阻塞,但可以捕捉信号

三、sigaction信号处理注册

原型:
#include &
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值