处理信号

处理信号

信号是在程序运行过程中系统突然发生一些特殊事件时,用来通知正在运行的程序的手段。例如当shell中有一个程序正在运行时,用户突然按下了ctrl + C,系统就会想正在运行的程序发送SIGINT信号,该信号表示一个中断。系统用这种方式告诉正在运行的程序,当前正发生的一些事件,便于程序对这些突发时间进行处理。

信号的定义在头文件signal.h中,其中常用的有:

信号含义
SIGABORT进程异常终止
SIGALRM超时警告
SIGFPE浮点运算异常
SIGHUP连接挂断
SIGILL非法指令
SIGINT终端中断
SIGKILL终止进程(此信号不能被捕获或忽略)
SIGSTOP停止执行(此信号不能被捕获或忽略)
SIGPIPE向无读进程的管道写数据
SIGQUIT终端退出
SIGSEGV无效内存段访问
SIGTERM终止
SIGUSR1用户定义信号1
SIGUSR2用户定义信号2

如果程序接收到上述信号的任何一个,并且实现没有捕获它,就会终止运行。进程捕获一个信号的意思是,在程序中定义一个信号处理函数用来处理这个信号。如何捕获一个信号会在后面介绍。

下面的信号是对程序的一个提醒,就算程序不捕获也不会导致程序退出。

信号含义
SIGCHLD子进程已经停止或退出
SIGCONT继续执行暂停进程
SIGTSTP终端挂起
SIGTTIN后台进程尝试读操作
SIGTTOU后台进程尝试写操作

发送信号

在shell中可以使用kill命令来向一个进程发送信号,之前还以为这个命令只能杀死进程。具体可以查看kill的手册。在C语言中,可以使用同名的系统调用来实现相同的操作。但是在向一个进程发送信号的时候,要注意当前程序是否有向目标进程发送信号的权限。通常来说一个用户只能向自己的程序发送信号,也就是说目标进程的用户ID必须跟发送程序的一样,并且超级用户可以向所有的进程发送信号。

这个系统调用的定义如下:

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

int kill(pid_t pid, int sig);

kill()函数将参数sig给定的信号发送给pid指定的进程,如果成功返回0,失败时返回-1并设置errno变量,该变量在头文件errno.h中定义。

上面的系统调用会在调用时立即向指定进程发送信号,下面有一个函数alarm(),可以在给定时间之后向本进程发送一个SIGALRM信号,注意这里是向自己发送一个SIGALRM信号

就算多次调用alarm()函数也不会让程序在规定时间后发出多个SIGALRM信号,而是重置倒计时的时间,如果想让程序发出多个SIGALRM信号,需要等到原来的信号发出之后再调用alarm()函数重新倒计时。

该系统调用的定义为:

#include <unistd.h>

unsigned int alarm(unsigned int seconds)

捕捉信号

捕捉信号指的是当程序接受到一个信号时使用指定的信号处理函数来对信号进行处理。注意要在接到信号之前就对信号进行捕捉,也就是定义处理该信号的函数。捕获信号使用sigaction()函数来实现,它的定义如下:

#include <signal.h>

int sigaction(int sig, const struct sigaction * act, struct sigaction * oact);
  • 这个函数在捕获成功时返回0,失败时返回-1,并设置errno变量。
  • sig参数表示要捕获的信号,它的值可以是上面介绍的信号。
  • act参数是要对该信号进行的操作,sigaction结构体中有信号处理函数,以及另外的一些用于控制处理行为的变量。
  • sigaction()函数将当前信号的处理方式定义为参数act的时候,将该信号原来的处理方式存放在oact中。

sigaction()参数表中sigaction结构体在头文件signal.h中定义,下面是它至少应该包含的成员。

#include <signal.h>

struct sigaction{
	void (*) (int) sa_handler;
	sigset_t sa_mask;
	int sa_flags;
}
  • sa_handler是用来处理信号的函数,它除了可以是一个函数之外,还可以是SIG_IGNSIG_DFL分别表示忽略信号和恢复默认行为。
  • sigset_t表示的是一个信号集,是多个信号的集合,sa_mask成员表示在处理信号的过程中,屏蔽信号集sa_mask中的信号。
  • sa_flags用于改变信号的行为,它的取值和含义如下:
    信号含义
    SA_NOCLDSTOP子进程停止时不产生SIGCHLD信号(指暂停,返回时仍然会产生信号)
    SA_RESETHAND将对此信号的处理方式在信号处理函数的入口处重置为SIG_DFL
    SA_RESTART重启可中断的函数而不是给出EINTR错误
    SA_NODEFER捕获到信号时不将它添加到屏蔽字中

信号集

信号集是一组信号的集合,这个集合被存储在一个sigset_t类型的变量中,这个类型在头文件signal.h中定义。此外,signal.h中还定义了一组用来操作这个信号集的函数:

#include <signal.h>

int sigaddset(sigset_t * set, int signo);			//添加一个信号到信号集
int sigemptyset(sigset_t * set);				//将信号集初始化为空
int sigfillset(sigset_t * set);					//将所有信号添加到信号集中
int sigdelset(sigset_t * set, int signo);			//从信号集中删除一个信号
int sigismember(sigset_t * set, int signo);			//判断信号是否存在于信号集中
int sigprocmask(int how, const sigset_t * set, sigset_t * oset);//将set中的信号屏蔽,并将原来的屏蔽信号集保存到oset中

上面的函数在执行成功时都会返回0,失败时返回-1并设置errno

sigprocmask()set中的信号对当前程序的屏蔽信号集进行操作,并且把原来的屏蔽信号集写入到oset中。如果新信号集set是一个空指针,那么该函数的意义便成为了获取当前程序的屏蔽信号集。

how参数表示set信号集的用途,它可以是下面的值:

含义
SIG_BLOCK把参数set中的信号添加到信号屏蔽字中
SIG_SETMASK把信号屏蔽字设置为参数set中的信号
SIG_UNBLOCK从信号屏蔽字中删除参数set中的信号

下面是一个例子:

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

void ding(int sig){
	printf("Ding!\n");
}

int main(void){
	pid_t pid;

	printf("Alarm starting.\n");

	struct sigaction sa;
	sa.sa_handler = ding;
	sa.sa_flags = 0;
	sigemptyset(&sa.sa_mask);
	sigaction(SIGALRM, &sa, 0);

	pid = fork();
	switch (pid){
		case -1:
			printf("Forking process failed.\n");
			return 1;
		case 0:
			sleep(5);
			kill(getppid(), SIGALRM);
			return 0;
	}

	pause();
	printf("Done.\n");

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值