Linux系统编程-信号

    0x00

    信号和中断类似,中断是硬件发出,而信号由软件发出。

    信号常用于进程间通信,一个信号常见的处理如下:

    1、设置对应信号的信号处理函数。

    2、当信号来临时,打断正常执行的程序(本质上是在系统调用前检查是否有信号的到来),去执行信号处理函数。

    3、信号处理函数执行完,继续执行原程序。


    0x01

    我们先来看一下linux中一共支持多少种信号。使用命令kill -l,可以列出linux支持的所有信号。

    

   

    每个信号都有对应的默认信号处理函数,使用命令man 7 signal,可以查看到。

    


    如果信号没有定义对应的信号处理函数,并且没有忽略信号,那么则采用默认的信号处理函数。

    这里要注意,信号SIGKILL和SIGSTOP既不能被捕捉,也不能被忽略。


    0x02

    我们看一个自定义信号处理函数的例子,来学习如何自定义信号处理,并且学习如何发送信号。

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>


#define ERR_EXIT(m) \
	do \
	{ \
		perror(m); \
		exit(EXIT_FAILURE); \
	} while(0)

void handler(int sig);
int main(int argc, char *argv[])
{
	if (signal(SIGINT, handler) == SIG_ERR)
		ERR_EXIT("signal error");
	for (;;) 
		pause();
	return 0;
}

void handler(int sig)
{
	printf("recv a sig=%d\n", sig);
}
    这个程序中我们自定义了信号处理函数,对应的信号是SIGINT,那么什么时候会发送这个这个信号呢?答案是当用户按下Ctrl + C时,我们看控制台输出的结果。

    
    对照信号表,SIGINT是2号信号。在程序中pause,可以用来等待下一次信号的来临。

    在这个例子中发送信号是通过键盘按Ctrl + C。

    这里顺便说一下三号信号SIGQUIT,发送该信号,按键盘Ctrl + \。

  

    0x03

    我们接着讲另一种发送信号的方式,通过kill,列出代码:

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>


#define ERR_EXIT(m) \
	do \
	{ \
		perror(m); \
		exit(EXIT_FAILURE); \
	} while(0)

void handler(int sig);
int main(int argc, char *argv[])
{
	if (signal(SIGUSR1, handler) == SIG_ERR)
		ERR_EXIT("signal error");
	pid_t pid = fork();
	if (pid == -1)
		ERR_EXIT("fork error");

	if (pid == 0) //子进程
	{
		kill(getppid(), SIGUSR1);
		exit(EXIT_SUCCESS);
	}

	int n = 5; //父进程
	do
	{
		n = sleep(n);
	} while (n > 0);
	return 0;
}

void handler(int sig)
{
	printf("recv a sig=%d\n", sig);
}
    这个程序中使用了SIGUSR1这个用户自定义信号。使用kill向对应的父进程发送信号,父进程sleep一旦接收到信号,会立刻退出,所以就不会睡眠5秒钟,那么如何让父进程睡眠5秒钟呢?我们采用了循环的方式,即使被子进程发来的信号打断,也可以继续睡眠剩余的时间。

    这个例子中发送信号,我们采用kill的方式。

    0x04

    下面再看一个通过alarm发送信号的例子:

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>


#define ERR_EXIT(m) \
	do \
	{ \
		perror(m); \
		exit(EXIT_FAILURE); \
	} while(0)

void handler(int sig);
int main(int argc, char *argv[])
{
	if (signal(SIGALRM, handler) == SIG_ERR)
		ERR_EXIT("signal error");

	alarm(1);
	for (;;)
		pause();
	return 0;
}

void handler(int sig)
{
	printf("recv a sig=%d\n", sig);
	alarm(1);
}
    通过alarm函数向自身进程发送SIGALRM的信号,在信号处理函数中再一次发送ALRM信号,起到了一个定时器的作用。


    0x05

    接下来的例子,我们讲解下结束进程的几种方式,结合waitpid函数,进一步理解waitpid函数。

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>


#define ERR_EXIT(m) \
	do \
	{ \
		perror(m); \
		exit(EXIT_FAILURE); \
	} while(0)

int main(int argc, char *argv[])
{
	pid_t pid;
	pid = fork();
	if (pid == -1)
		ERR_EXIT("fork error");

	if (pid == 0) //子进程
	{
		sleep(3);
		printf("this is child\n");
		exit(100);
		//return 0;
		//abort();
		//raise(SIGKILL);
	}

	int ret;
	printf("this is parent\n");
	int status;
	//ret = wait(&status);
	//ret = waitpid(-1, &status, 0);
	ret = waitpid(pid, &status, 0); //父进程
	printf("ret = %d pid = %d\n", ret, pid);
	if (WIFEXITED(status))
		printf("child exited normal exit status=%d\n", WEXITSTATUS(status));
	//else
	//	printf("child exited abnormal\n");

	else if (WIFSIGNALED(status))
		printf("child exited abnormal signal number=%d\n", WTERMSIG(status));
	else if (WIFSTOPPED(status))
		printf("child stoped signal number=%d\n", WSTOPSIG(status));
	return 0;
}
    这个例子说明了结束一个进程的几种方式:

    1、exit(100) ---> 控制台输出:child exited normal exit status=100 

    2、return 0 ---> 控制台输出:child exited normal exit status=0

    3、abort(),向当前进程发送SIGABRT,SIGABRT信号的默认处理函数为终止进程 ---> 控制台输出:child exited abnormal signal number=6 (SIGABRT是6号信号)

    4、raise(SIGKILL),向当前进程发送SIGKILL信号,SIGKILL信号的默认处理函数是终止进程 --> 控制台输出:child exited abnormal signal number=9 (SIGKILL是9号信号)


    0x06

    总结:

    信号既可以被捕获(自定义信号处理函数),也可以忽略,也可以走默认信号处理函数。

    发送信号有以下几种方式:

    1、键盘操作,Ctrl + C,Ctrl + \

    2、kill

    3、alarm

    4、abort

    5、raise

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值