1. 程式人生 > 其它 >Liunx系統程式設計篇—程序通訊(五)訊號

Liunx系統程式設計篇—程序通訊(五)訊號

技術標籤:筆記linux

Liunx系統程式設計篇—程序通訊(五)訊號

原理

對於Linux,訊號是軟中斷,例如下課鈴響了,老師要停止講課。許多重要的程式都需要處理訊號。
訊號,為 Linux 提供了一種處理非同步事件的方法。比如,終端使用者輸入了ctrl+c來中斷程式,會通過訊號機制停止一個程式。

概述

每個訊號都有一個名字和編號,這些名字都以“SIG”開頭,例如“SIGIO ”、“SIGCHLD”等等。
訊號定義在**signal.h標頭檔案中,訊號名都定義為正整數。
具體的訊號名稱可以 使用
kill -l**來檢視訊號 的名字以及序號,
訊號是 從1開始編號的,不存在0號訊號。kill對於訊號0又特殊的應用。

[email protected]-virtual-machine:~/桌面/linkr/程序間通訊$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21)
SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+
11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX [email protected]-virtual-machine:~/桌面/linkr/程序間通訊$

訊號的處理

有三種方法,忽略,捕捉,和預設動作

1.忽略訊號

大多數訊號可以使用這個方式來處理,但是有兩種訊號不能被忽略(分別是 SIGKILL和SIGSTOP)。因為他們向核心和超級使用者提供了程序終止和停止的可靠方法

2.捕捉訊號

需要告訴核心,使用者希望如何處理某一種訊號,說白了就是寫一個訊號處理函式,然後將這個函式告訴核心。當該訊號產生時,由核心來呼叫使用者自定義的函式,以此來實現某種訊號的處理。

3.系統預設動作

對於每個訊號來說,系統都對應由預設的處理動作,當發生了該訊號,系統會自動執行。不過,對系統來說,大部分的處理方式都比較粗暴,就是直接殺死該程序。
具體的訊號預設動作可以使用man 7 signal來檢視系統的具體定義。在此,我就不詳細展開了,需要檢視的,可以自行檢視。也可以參考 《UNIX 環境高階程式設計(第三部)》的 P251——P256中間對於每個訊號有詳細的說明。
瞭解了訊號的概述,那麼,訊號是如何來使用呢?

其實對於常用的 kill 命令就是一個傳送訊號的工具,kill 9 PID來殺死程序。比如,我在後臺運行了一個 top 工具,通過 ps 命令可以檢視他的 PID,通過 kill 9 來發送了一個終止程序的訊號來結束了 top 程序。如果檢視訊號編號和名稱,可以發現9對應的是 9) SIGKILL,正是殺死該程序的訊號。而以下的執行過程實際也就是執行了9號訊號的預設動作——殺死程序。

kill -9 程序PID
kill -SIGKILL 程序PID
ps -aux|grep a.out    

這兩句實現功能一樣。

建立

常用API

訊號處理函式的註冊

入門版:函式signal
高階版:函式sigaction

訊號處理髮送函式

1.入門版:kill
2.高階版:sigqueue

在正式開始瞭解這兩個函式之前,可以先來思考一下,處理中斷都需要處理什麼問題。按照我們之前思路來看,可以傳送的訊號型別是多種多樣的,每種訊號的處理可能不一定相同,那麼,我們肯定需要知道到底發生了什麼訊號。另外,雖然我們知道了系統發出來的是哪種訊號,但是還有一點也很重要,就是系統產生了一個訊號,是由誰來響應?如果系統通過 ctrl+c 產生了一個 SIGINT(中斷訊號),顯然不是所有程式同時結束,那麼,訊號一定需要有一個接收者。對於處理訊號的程式來說,接收者就是自己。

signal函式
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

函式原型由兩部分組成,一個是真實處理訊號的函式,另一個是註冊函數了。
註冊函式:
sighandler_t signal(int signum, sighandler_t handler);函式來說,
signum :訊號的編號。
handler :中斷函式的指標。

handler函式

真實處理訊號的函式:
typedef void (*sighandler_t)(int);中斷函式的原型中,有一個引數是 int 型別,顯然也是訊號產生的型別,方便使用一個函式來處理多個訊號。

例子

1.訊號的處理,捕捉動作

#include <signal.h>
#include <stdio.h>
//typedef void (*sighandler_t)(int);
//sighandler_t signal(int signum, sighandler_t handler);
void handler(int signum)
{
        printf("get signum=%d\n",signum);
        switch(signum){
                case 2:
                        printf("SIGINT\n");
                        break;
                case 10:
                        printf("SIGUSR1\n");
                        break;
        }
}
int main()
{
    signal(SIGINT,handler);//訊號處理函式的註冊 2
    signal(SIGUSR1,handler); // 9
    while(1);//不讓程式退出
    return 0;
}
//結果



[email protected]-virtual-machine:~/桌面/linkr/程序間通訊$ ./a.out 

//按cltr+c
[email protected]-virtual-machine:~/桌面/linkr/程序間通訊$ ./a.out 
^Cget signum=2
SIGINT
//
[email protected]-virtual-machine:~/桌面/linkr/程序間通訊$ ps -aux |grep a.out 
xin       19558  100  0.0   4516   852 pts/1    R+   11:23   1:04 ./a.out
xin       19567  0.0  0.0  16188   940 pts/0    S+   11:25   0:00 grep --color=auto a.out
 //
[email protected]-virtual-machine:~/桌面/linkr/程序間通訊$ kill -10 19558
[email protected]-virtual-machine:~/桌面/linkr/程序間通訊$ ./a.out 
^Cget signum=2
SIGINT
get signum=10
SIGUSR1
//殺死 a.out
kill -9 19558
[email protected]-virtual-machine:~/桌面/linkr/程序間通訊$ ./a.out 
^Cget signum=2
SIGINT
get signum=10
SIGUSR1
已殺死

2.將kill訊號處理髮送函式置於程式中,同時也可以使用system來實現kill。
訊號處理髮送函式

#include<stdio.h>
#include <signal.h>
#include <sys/types.h>
int main(int argc,char **argv)
{
        int signum=0;
        int pid=0;
        char cmd[128]={0};//設定system函式處理命令的大小
        signum= atoi(argv[1]);//轉為整型
        pid= atoi(argv[2]);//轉為整型
        printf("num=%d\n",signum);
        printf("pid=%d\n",pid);
       	
		kill(pid,signum);
		// sprintf(cmd,"kill -%d %d",signum,pid);
        // system(cmd);
        printf("send signal ok\n");
        return 0;
}
/*
注:
1、atoi (表示 ascii to integer)是把字串轉換成整型數的一個函式。
2、sprintf指的是字串格式化命令,
函式宣告為 int sprintf(char *string, char *format [,argument,…]);,
主要功能是把格式化的資料寫入某個字串中,即傳送格式化輸出到 string 所指向的字串。
3、
*/

訊號的處理:忽略

#include<stdio.h>
#include <signal.h>
void handler(int signum)
{
        printf("get signum=%d\n",signum);
        switch(signum){
                case 2:
                        printf("SIGINT\n");
                        break;
                case 10:
                        printf("SIGUSR1\n");
                        break;
        }

}
int main()
{
        signal(SIGINT,SIG_IGN);//將SIGINT訊號(ctrl+C、2)忽略
        signal(SIGUSR1,SIG_IGN);//將SIGUSR1訊號(10)忽略
        while(1);
        return 0;
}
實驗結果
//會發現ctrl+c與kill -10 pid已經對程序不起作用了,這兩個訊號被程序忽略。

參考原文