Linux进程间通讯|信号通讯

信号通讯

1. 信号概述

信号是在软件层次上对中断机制的一种模拟。在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的:一个进程不必通过任何操作来等待信号的到达。信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。它可以在任何时候发送给某一进程,而无需知道该进程的状态。如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它为止;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
信号是进程间通讯机制中唯一的异步通讯机制。可以把这种机制看作异步通知,通知接受信号的进程有哪些事情发生了。信号事件的产生有硬件来源(比如按键或硬件故障)和软件来源(kill()、raise()、alarm()、setitimer()、sigqueue()等,还包括一些非法运算操作)。
进程可以通过3种方式来响应一个信号:

  • 忽略信号:对信号不做任何处理。SIGKILL及SIGSTOP信号不能被忽略
  • 捕捉信号:定义信号处理函数,当信号发生时执行相应的处理函数
  • 缺省方式:Linux对每种信号都规定了默认操作,如下表所示
信号名 含义 默认操作
SIGHUP 用户终端连接结束时发出,通知同一会话内的各个进程与控制端不再关联 终止进程
SIGINT 输入INTR字符时(Ctrl+C)时发出,并送到前台进程的每个进程中 终止进程
SIGQUIT 和SIGINT类似,但有QUIT字符(Ctrl+ \ )来控制 终止进程
SIGILL 在一个进程企图执行一条非法指令时发出 终止进程
SIGFPE 在发生致命的算术运算时发出 终止进程
SIGKILL 用来立即结束程序的运行,且不能被阻塞、处理和忽略 终止进程
SIGALARM 当一个定时器到时的时候发出 终止进程
SIGSTOP 用于暂停一个进程,且不能被阻塞、处理和忽略 暂停进程
SIGTSTP 用于交互停止进程,用户在输入SUSP字符时(Ctrl+Z)发出 终止进程
SIGCHLG 子进程改变状态时,父进程会收到这个信号 忽略

2. 信号相关函数

2.1 信号发送:kill() 和 raise()

kill() 函数与 kill 系统命令一样,可以发送信号给进程或进程组(kill系统命令就是用kill()函数实现的)。该函数不仅可以终止进程,也可以向进程发送其他信号。
raise() 函数只允许进程向自身发送信号。

/*****kill()函数*****/
函数原型:int kill(pid_t pid, int sig)
传 入 值:pid -->正数:发送信号给进程号为pid的进程
			 --> 0:信号被发送到所有和当前进程在同一个进程组的进程
			 -->-1:信号发给所有的进程表中的进程(除了进程号最大的进程外)
			 -->小于-1:信号发送给进程组号为-pid的每一个进程
		  sig 信号类型
返 回 值:成功返回0;失败返回-1

/*****raise()函数*****/
函数原型:int raise(int sig)
传 入 值:sig 信号类型
返 回 值:成功返回0;失败返回-1

实例程序

/*****kill_raise.c*****/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(){
    
    
	pid_t pid;
	int ret;
	
	if((pid == fork()) < 0){
    
    	//创建子进程
		printf("Fork error\n");
		exit(-1);
	}

	if(pid == 0){
    
    
		/*在子进程中使用raise()函数发出SIGSTOP信号,使子进程暂停*/
		printf("child(pid: %d) is waiting for any signal!\n",getpid());
		raise(SIGSTOP);
		exit(0);
	}else{
    
    
		/*在父进程中收集子进程的状态,并调用kill()函数发送信号*/
		if((waitpid(pid,NULL,WNOHANG)) == 0){
    
    
			kill(pid,SIGKILL);
			printf("parent kill child process %d\n",pid);
		}
		waitpid(pid,NULL,0);
		exit(0);
	}
}

程序运行结果如下

linux@linux-virtual-machine:~/andy/proc$ ./kill_raise
child(pid:4877) is waiting for any signal!
parent kill child process 4877
2.2 定时器信号:alarm() 和 pause()

alarm() 也称为闹钟函数,它可以在进程中设置一个定时器。当定时器指定的时间到了时,它就向进程发送SIGALARM信号。要注意的是,一个进程只能有一个闹钟时间,如果在调用 alarm()之前已经设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。
pause() 用于将调用进程挂起直至接收到信号为止。

/*****alarm()函数*****/
函数原型:unsigned int alarm(unsigned int seconds)
传 入 值:seconds 指定秒数,系统经过seconds秒后向该进程发送SIGALARM信号
返 回 值:成功:如果调用此函数前,进程中已经设置了闹钟时间,则返回上一个闹钟剩余的时间,否则返回0
		 失败:返回-1

/*****pause()函数*****/
函数原型:int pause(void)
返 回 值:-1 并且把errno值设为EINTR

实例程序

/*****alarm_pause.c*****/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){
    
    
	alarm(5);
	pause();
	printf("I have been waken up\n");	//此语句不会被执行
}

程序运行结果如下

linux@linux-virtual-machine:~/andy/proc$ ./alarm_pause
Alarm clock		//SIGALARM信号默认处理函数打印
2.3 信号设置:signal() 和 sigaction()

信号处理主要有两种方法:一种是使用简单的signal() 函数,另一种是使用 sigaction()。
signal() 函数只需要指定信号类型和信号处理函数即可。主要用于前32种非实时信号的处理,不支持信号传递信息。
sigaction() 函数相对 signal() 函数更加健壮、功能更全。

/*****signal()函数*****/
函数原型:typedef void (*sighandler_t)(int);
		 sighandler_t signal(int signum,sighandler_t handler);
传 入 值:signum 指定信号代码
		 handler -->SIG_IGN 忽略该信号
		         -->SIG_DFL 采用系统默认方式处理信号
		         -->自定义的信号处理函数
返 回 值:成功:返回以前的信号处理函数
		 失败:返回-1

/*****sigaction()函数*****/
函数原型:int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
传 入 值:signum 信号类型,除SIGKILL和SIGSTOP之外的任何一个信号
		 act 指向sigaction结构体的指针,包含对特点信号的处理
		 oldact 保存信号原先的处理方式
返 回 值:成功返回0;失败返回-1

//sigaction结构体的定义
struct sigaction{
    
    
	void (*sa_handler)(int signo); //函数指针,指向信号处理函数
	sigset_t sa_mask;  //信号集合,用来指定在信号处理函数执行过程中哪些信号被屏蔽
	int sa_flags;  //信号处理相关的各种标志位
	void (*sa_restore)(void);
}

signal() 实例程序

/*****signal.c*****/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void my_func(int sign_no){
    
    	//自定义信号处理函数
	if(sign_no == SIGINT)
		printf("I have got SIGINT\n");
	else if(sign_no == SIGQUIT)
		printf("I have got SIGQUIT\n");
}

int main(){
    
    
	printf("Waiting for signal SIGINT or SIGQUIT...\n");
	signal(SIGINT,my_func);
	signal(SIGQUIT,my_func);
	pause();
	exit(0);	
}

程序运行结果如下

linux@linux-virtual-machine:~/andy/proc$ ./signal
Waiting for signal SIGINT or SIGQUIT...
I have got SIGINT (按 ctrl + c 组合键)
linux@linux-virtual-machine:~/andy/proc$ ./signal
Waiting for signal SIGINT or SIGQUIT...
I have got SIGQUIT (按 ctrl + \ 组合键)

sigaction() 实例程序,实现同样的功能

/*****sigaction.c*****/
//头文件省略
//自定义信号处理函数同以上实例
int main(){
    
    
	struct sigaction action;
	/*sigaction结构初始化*/
	sigaction(SIGINT,0,&action);
	action.sa_handler = my_func;
	sigaction(SIGINT,&action,0);

	sigaction(SIGQUIT,0,&action);
	action.sa_handler = my_func;
	sigaction(SIGQUIT,&action,0);
	printf("Waiting for signal SIGINT or SIGQUIT...\n");

	pause();
	exit(0);	
}

猜你喜欢

转载自blog.csdn.net/Chuangke_Andy/article/details/108305697