Linux(進程間通訊-信號)

信號通訊是在軟件層面上對中斷機制的一種模擬。信號是進程間通訊機制中惟一的異步通訊機制。異步

信號能夠直接進行用戶空間進程和內核進程之間的交互,內核進程也能夠利用它來通知用戶空間進程發生了哪些系統事件。它能夠在任什麼時候候發給某一進程,而無需知道該進程的狀態。若是該進程當前並未處於執行態,則該信號就由內核保存起來,直到該進程恢復執行再傳遞給它爲止。函數

信號事件的產生有硬件來源(如按下鍵盤或者其餘硬件故障)和軟件來源,軟件來源除了軟件調用,還包括一些非法運算等操做。指針

進程能夠經過3種方式來響應一個信號:rest

一、忽略信號:即對信號不作任何處理,其中,有兩個信號不能忽略,SIGKILL & SIGSTOP。code

二、捕捉信號:定義信號處理函數,當信號發生時,執行相應的操做。進程

三、執行默認操做:Linux對每種信號都規定了默認操做,下表包含了部分的定義:事件

信號的相關函數包括信號的發送和設置,具體以下:字符串

發送信號的函數:kill()    raise()    sigqueue()get

設置信號的函數:signal()    sigaction()    setitimer()原型

其餘函數:alarm()    pause()

信號發送:kill()    raise()

kill() 函數同kill 系統命令同樣,能夠發送信號給進程或是進程組(實際上,kill 系統命令就是調用 kill() 函數。須要注意的是,它不只能夠終止進程(發送 SIGTERM信號),也能夠向進程發送其餘信號。

注意:raise() 函數只能向進程自身發送信號

函數原型:

int kill(pid_t pid,int sig);

int raise(int sig);

說明:pid 爲進程號;sig 爲信號類型號

raise() 例程:進程在執行while(1)循環輸出字符串的時候調用raise(SIGSTOP)使進程中止 \

/* raise.c */
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
	printf("PID: %d\n",getpid());
	while(1){
		printf("run\n");
		raise(SIGSTOP);
	}
}

程序輸出:

PID: 55859
run

[1]+  Stopped                 ./a.out

能夠看到,進程在輸出了第句run以後調用raise(SIGSTOP)使進程中止了,使用命令 "ps -ef | grep a.out" 能夠看到進程並無消失,確認進程只是暫停。使用 "kill -9 55859" 殺死進程。

kill() 例程:子進程間隔10us打印一串字符,父進程100us後 kill 子進程。

/* kill.c */
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	pid_t pid;
	pid = fork();
	if(pid == -1){
		perror("fork failed:");
		exit(-1);
	}
	if(pid == 0){
		while(1){
			printf("child\n");
			sleep(1);
		}
	}else{
		sleep(3);
		kill(pid,SIGINT);
		sleep(3);
		exit(0);
	}
}

alarm() 能夠在進程中設置一個定時器,當定時器指定的時間到時,它就向進程發送SIGALARM信號。一個進程只能有一個鬧鐘,重複設置,前面的鬧鐘會被覆蓋。

示例:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
	alarm(3);
	while(1){
		printf("run\n");
		sleep(1);
		pause();
	}
	printf("waken up\n");
}

程序輸出:

run
Alarm clock

註釋掉 pause(); 的程序輸出:

run
run
run
Alarm clock

信號的設置:signal()    sigaction()

signal() 用於設置信號的處理函數。主要用於前32種非實時信號的處理,不支持信號傳遞信息。

注:signal() 是UNIX系統的遺留版本,不一樣的系統表現不一且在有的系統上會出現信號丟失的問題。且在man中亦提到,’應避免使用signal(),使用sigaction()代替‘。因此這裏咱們就不討論signal(),直接使用sigaction()。

函數原型:

int sigaction(int signum,const struct sigaction *act,struct sigacton *oldact);

說明:signum - 信號類型,除SIGKILL & SIGSTOP 外的任何一個信號;act - 指向sigaction結構體的指針,包含對特殊信號的處理;oldact - 保留信號原先的處理方式。

struct sigaction {
	void     (*sa_handler)(int);
	void     (*sa_sigaction)(int, siginfo_t *, void *);
	sigset_t   sa_mask;
	int        sa_flags;
	void     (*sa_restorer)(void);
};

說明:

sa_handler 和 sa_sigaction 二者只能定義一個,且sa_sigaction 是 sa_handler的升級版本,因爲sa_sigaction又太複雜,在這裏咱們就不討論sa_sigaction。

sa_handler 是一個函數指針,指向信號處理函數。它既能夠是用戶自定義的處理函數,也能夠爲SIG_DFL(採用默認的處理方式)或SIG_IGN(忽略信號)。信號處理函數只有一個參數,即信號類型。

sa_mask是一個信號集合,用來指定在信號處理函數執行過程當中哪些信號被屏蔽。

sa_flags 中包含了許多標誌位,都是和信號處理相關的選項。常見可選值包括(SA_NODEFER/SA_NOMASK/SA_NOCLDSTOP/SA_RESTART/SA_ONESHOT/SA_RESETHAND)

sigaction() 示例:

/* signal.c */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.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()
{
	struct sigaction action;
	//初始化結構體
	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 & SIGQUIT\n:");
	
	pause();
	exit(0);
}
相關文章
相關標籤/搜索