Linux 信號機制

概述

Linux 在進程間通信時,有時候需要用到異步通訊方式,而信號機制是Linux系統(tǒng)本身提供的一種異步通訊.

Linux中信號的類別

Linux信號在系統(tǒng)中總數(shù)是有限的,信號種類如下所示:


Linux支持的信號.png

這些信號在Linux系統(tǒng)中各自有不同的用途.同時在Unix和Linux的不斷發(fā)展中出現(xiàn)了兩種信號,或者說由于歷史遺留問題出現(xiàn)了兩種信號:可靠信號和不可靠信號.不可靠信號是從早期的Unix繼承而來,而可靠信號是后來定義的信號.

信號的處理

一個進程對信號的響應可以分為三種情況,分別為:

  • 忽略信號
  • 捕捉信號
  • 執(zhí)行系統(tǒng)默認操作

忽略信號

忽略信號是指在代碼中進行設置后,進程不會對信號進行響應,值得注意的是有兩個信號是不能忽略的這兩個信號為SIGKILL和SIGSTOP.

捕捉信號

捕捉信號是指在代碼中設置函數(shù),當指定的信號發(fā)生時,調用已經設置好的處理函數(shù),使得進程可以按照自己的意愿對信號發(fā)生時所代表的時間進行處理.

執(zhí)行系統(tǒng)默認操作

Linux操作系統(tǒng)規(guī)定了很多對于信號的默認操作,這些可以通過查詢獲取到,但是對于實時信號來說,器系統(tǒng)的默認操作都是進程終止.

信號的使用

在使用信號時首先需要確認使用何種信號.然后需要進程去產生信號.

信號的產生

信號可以通過六個函數(shù)產生:

  • kill函數(shù)
  • raise函數(shù)
  • sigqueue函數(shù)
  • alarm函數(shù)
  • setitimer函數(shù)
  • abort函數(shù)

kill函數(shù)

kill函數(shù)原型如下:

int kill(pid_t pid,int signo)

kill函數(shù)中的pid參數(shù)可以設置為如下方式:

pid > 0: 將信號發(fā)送給指定進程ID為pid的進程
pid == 0: 將信號發(fā)送給與發(fā)送進程在同一進程組的所有進程
pid < 0: 將信號發(fā)送給進程組ID等于pid絕對值的進程,如果pid==-1,那么就將信 號發(fā)送給有權限發(fā)送信號的系統(tǒng)上的所有進程.

kill函數(shù)中的signo參數(shù)也可以設置為如下方式:

signo == 0:發(fā)送一個空信號,實際上不發(fā)送任何值給目標進程,但是可以檢測目標進程是否存在,同時是否有權限向目標進程發(fā)送該信號.
signo != 0:向目標進程發(fā)送指定的信號

raise函數(shù)

raise函數(shù)原型如下:

int raise(int sig);

raise函數(shù)在實質上等價于kill(get_pid(),signo);因此raise函數(shù)只可以向自身進程發(fā)送信號其中signo參數(shù)的設置和kill函數(shù)相同.

sigqueue函數(shù)

sigqueue函數(shù)原型如下:

int sigqueue(pid_t pid, int sig, const union sigval value);

sigqueue函數(shù)的pid參數(shù)和sig參數(shù)和kill函數(shù)相同其功能也和kill函數(shù)類似.但是和kill函數(shù)不同sigqueue函數(shù)是較新的發(fā)送信號的函數(shù),支持后面出現(xiàn)的實時信號,在發(fā)送信號的時候,也支持參數(shù)的傳遞,比kill函數(shù)多了一些信號的附加信息.

sigqueue函數(shù)比kill函數(shù)更加優(yōu)越的地方主要在于第三個參數(shù)的使用上.第三個參數(shù)定義如下:

typedef union sigval {
int sival_int;
void *sival_ptr;
}sigval_t;

可以注意到這是一個聯(lián)合體,在聯(lián)合體中可以指定信號傳輸?shù)膮?shù),要么是一個四字節(jié)值,要么是一個指針.使用這個聯(lián)合體時,信號的目標進程也需要使用新的信號捕捉函數(shù)sigaction,否則該參數(shù)無效,具體的內容參照下面關于sigaction函數(shù)的敘述.

alarm函數(shù)

alarm函數(shù)的原型如下所示:

unsigned int alarm(unsigned int seconds);

信號中有一個專門用來定時的信號SIGALRM信號,alarm函數(shù)就是為使用這個信號專門設計的一個函數(shù),在alarm函數(shù)中的senconds參數(shù)中傳入具體的秒數(shù),在相應的時間到達時,就會向函數(shù)所在進程發(fā)送一個SIGALRM信號.
需要注意的一點是,每個進程只能擁有一個鬧鐘時間,如果一個進程已經設置過鬧鐘時間,且時間還未達到時,再次調用alarm函數(shù)設置鬧鐘時間,那么之前的值將會被新值替代,同時將鬧鐘時間的余留值返回.所以當新設置的鬧鐘時間為0時,就會取消原有的鬧鐘時間.

setitimer函數(shù)

setitimer函數(shù)原型如下:

int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);

setitimer函數(shù)是對alarm函數(shù)功能的擴充,同時setitimer函數(shù)還有一個配套的查詢函數(shù):

int getitimer(int which, struct itimerval *curr_value);

setitimer函數(shù)支持三種定時器,這個選擇由which參數(shù)指定,這三個定時器分別為:

  • ITIMER_REAL:設定絕對時間,當設定的時間到達時內核將發(fā)送SIGALRM信號給本進程
  • ITIMER_VIRTUAL :設定程序的執(zhí)行時間(指程序在用戶層運行的時間),當程序的執(zhí)行時間到達時,內核將發(fā)送SIGALRM信號給本進程
  • ITIMER_PROF :設定進程執(zhí)行以及內核因本進程而消耗的時間總和,內核將發(fā)送ITIMER_VIRTUAL信號給本進程

setitimer的第二個參數(shù)是指定運行的時間,這個參數(shù)的結構體原型如下所示:

struct itimerval
{
    struct timeval it_interval; /* Interval for periodic timer */
    struct timeval it_value;    /* Time until next expiration */
};
struct timeval 
{
    time_t      tv_sec;         /* seconds */
    suseconds_t tv_usec;        /* microseconds */
};

其中itimerval結構體是傳入的參數(shù),這個結構體包含了兩個timerval結構體變量,這兩個變量用來設定時間.
其中it_interval指定的是發(fā)送信號的周期時間,it_value中保存的是到下一次發(fā)送信號的時間.
在setitimer的第三個參數(shù)時返回之前設定的時間周期值.

abort函數(shù)

abort函數(shù)原型如下

void abort(void);

該函數(shù)向進程發(fā)送SIGABORT信號,默認情況下進程會異常退出饱溢,即使SIGABORT被進程設置為阻塞信號帅刊,調用abort()后窟她,SIGABORT仍然能被進程接收陈症。該函數(shù)無返回值。

信號的捕捉和處理函數(shù)

目前在Linux中信號的捕捉處理函數(shù)有兩個:

  • signal函數(shù)
  • sigaction函數(shù)

signal函數(shù)

signal函數(shù)的原型如下所示i:

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

第一個參數(shù)signum負責設定相應要捕捉的信號
第二個參數(shù)是一個函數(shù)指針,這個參數(shù)可以被指定為3個值:

SIG_IGN:忽略該信號
SIG_DFL:系統(tǒng)默認方式處理信號
函數(shù)指針:負責設定捕捉到信號時應采取的操作,函數(shù)原型為typedef指定的格式.

sigaction函數(shù)

sigaction函數(shù)在功能上已經徹底取代了signal函數(shù),該函數(shù)的原型為:

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

第一個參數(shù)signum負責指定要捕捉的信號.
第二個參數(shù)和第三個參數(shù)都是一個sigaction結構體,其中第二個為設定新值,第一個為返回原有的設定值.該結構體定義如下:

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_handler是函數(shù)指針:(可以參照signal函數(shù)中的函數(shù)指針)
    負責指定關于信號的信號處理函數(shù),但是該函數(shù)只能傳入一個對應信號的信號值
  • 第二個參數(shù)sa_sigaction也是一個函數(shù)指針:(可以參照signal函數(shù)中的函數(shù)指針)
    負責指定相應信號的處理函數(shù),但是該函數(shù)可以傳入三個參數(shù),第一個為信號值,第二個參數(shù)為一個siginfo_t結構體用以說明本次信號處理的各種詳細信息,第三個參數(shù)現(xiàn)行標準中現(xiàn)在還沒有做相關規(guī)定
    siginfo_t結構體定義如下:
siginfo_t
 {
               int      si_signo;     /* Signal number */
               int      si_errno;     /* An errno value */
               int      si_code;      /* Signal code */
               int      si_trapno;    /* Trap number that caused
                                         hardware-generated signal
                                         (unused on most architectures) */
               pid_t    si_pid;       /* Sending process ID */
               uid_t    si_uid;       /* Real user ID of sending process */
               int      si_status;    /* Exit value or signal */
               clock_t  si_utime;     /* User time consumed */
               clock_t  si_stime;     /* System time consumed */
               sigval_t si_value;     /* Signal value */
               int      si_int;       /* POSIX.1b signal */
               void    *si_ptr;       /* POSIX.1b signal */
               int      si_overrun;   /* Timer overrun count;
                                         POSIX.1b timers */
               int      si_timerid;   /* Timer ID; POSIX.1b timers */
               void    *si_addr;      /* Memory location which caused fault */
               long     si_band;      /* Band event (was int in
                                         glibc 2.3.2 and earlier) */
               int      si_fd;        /* File descriptor */
               short    si_addr_lsb;  /* Least significant bit of address
                                         (since Linux 2.6.32) */
               void    *si_call_addr; /* Address of system call instruction
                                         (since Linux 3.5) */
               int      si_syscall;   /* Number of attempted system call
                                         (since Linux 3.5) */
               unsigned int si_arch;  /* Architecture of attempted system call
                                         (since Linux 3.5) */
           }
  • 第三個元素是一個信號集:設定了在信號處理期間應該屏蔽的信號.

  • 第四個元素:設定了修改信號行為的標識.例如當自身子進程狀態(tài)改變時,不接受子進程狀態(tài)改變信號等.這個flag的設定值可以通過查詢獲取到.

  • 第五個參數(shù)也是一個函數(shù)指針:POSIX標準不再使用.

信號處理中的一些問題

  • 1 信號SIGKILL和SIGSTOP不能被忽略,如果采取忽略這兩個信號,那么程序會報錯
  • 2 對某個進程發(fā)送信號時,如果該進程是多線程的,就會出現(xiàn)問題.由于信號在設計時沒有多線程概念,所以在設計之初就沒有考慮信號和多線程一起使用的情況,雖然在后面對標準進行了重新修訂,單一些較老的信號依然存在一些問題.
    在信號和多線程一起使用時,信號只會被進程中的一個線程處理,其他線程是無法收到信號的.而且處理信號的線程是未知的(在ubuntu下,測試發(fā)現(xiàn)信號首先被主線程處理),而如果指定的信號沒有被處理信號的線程重新定義操作函數(shù),那么會采用系統(tǒng)默認操作,導致程序運行出現(xiàn)問題.最好的解決辦法就是,其他線程都屏蔽要捕捉的線程,只有信號處理線程不進行屏蔽,那么該信號就會達到目標線程進行處理.
    對信號阻塞進行處理的函數(shù)如下所示:
    int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
    其中how共有三個參數(shù):
    how的取值為:
  • SIG_BLOCK 把參數(shù)set中的信號添加到信號屏蔽字中
  • SIG_SETMASK 把信號屏蔽字設置為參數(shù)set中的信號
  • SIG_UNBLOCK 從信號屏蔽字中刪除參數(shù)set中的信號
    而在使用sigset之前要確保先調用int sigemptyset(sigset_t *set)或者int sigfillset(sigset_t *set); 對信號集進行初始化,否則該參數(shù)初值是未知的.

sigemptyset(sigset_t *set)初始化由set指定的信號集震糖,信號集里面的所有信號被清空;

int sigfillset(sigset_t *set);
調用該函數(shù)后录肯,set指向的信號集中將包含linux支持的64種信號;

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吊说,隨后出現(xiàn)的幾起案子论咏,更是在濱河造成了極大的恐慌,老刑警劉巖颁井,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厅贪,死亡現(xiàn)場離奇詭異,居然都是意外死亡雅宾,警方通過查閱死者的電腦和手機养涮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來眉抬,“玉大人贯吓,你說我怎么就攤上這事⊥抡蓿” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵蘸劈,是天一觀的道長昏苏。 經常有香客問我,道長威沫,這世上最難降的妖魔是什么贤惯? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮棒掠,結果婚禮上孵构,老公的妹妹穿的比我還像新娘。我一直安慰自己烟很,他們只是感情好颈墅,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著雾袱,像睡著了一般恤筛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上芹橡,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天毒坛,我揣著相機與錄音,去河邊找鬼。 笑死煎殷,一個胖子當著我的面吹牛屯伞,可吹牛的內容都是我干的。 我是一名探鬼主播豪直,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼劣摇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了顶伞?” 一聲冷哼從身側響起饵撑,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唆貌,沒想到半個月后滑潘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡锨咙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年语卤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酪刀。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖骂倘,靈堂內的尸體忽然破棺而出眼滤,到底是詐尸還是另有隱情,我是刑警寧澤诅需,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站荧库,受9級特大地震影響堰塌,放射性物質發(fā)生泄漏。R本人自食惡果不足惜分衫,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一场刑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蚪战,春花似錦牵现、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至概漱,卻和暖如春丑慎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工竿裂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留玉吁,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓腻异,卻偏偏與公主長得像进副,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子悔常,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內容

  • 信號的基本概念 信號被認為是一種軟件中斷(區(qū)別于硬件中斷),信號機制提供了一種在單進程/線程下處理異步事件的方法影斑。...
    小葉大孟閱讀 1,898評論 0 1
  • 進程之間可以通過信號傳遞信息,信號是一種軟中斷機制机打,通過信號用來通知進程發(fā)生了異步事件矫户。進程之間可以互相通過系統(tǒng)調...
    lintong閱讀 407評論 0 2
  • 理論部分 簡單來說 信號是用來通知進程發(fā)生了異步事件 比如,在終端運行程序残邀,按下ctrl+c就產生一個中端信號皆辽,大...
    捉蟲大師閱讀 1,733評論 0 2
  • Linux進程間通信的概念 linux下進程間通信的幾種主要手段簡介: 管道(Pipe)及有名管道(named p...
    wewarriors閱讀 928評論 0 6
  • 又近一年暑假驱闷,我早早地就安排好了孩子暑期的各個課程,又急吼吼地等待著學校的特訓名單空免。下午拿到成績時空另,看到...
    逸媽閱讀 217評論 4 1