系列文章目錄
點(diǎn)擊進(jìn)入系列文章總目錄
C++技能系列
Linux通信架構(gòu)系列
C++高性能優(yōu)化編程系列
深入理解軟件架構(gòu)設(shè)計(jì)系列
高級C++并發(fā)線程編程
期待你的關(guān)注哦?。?!
現(xiàn)在的一切都是為將來的夢想編織翅膀,讓夢想在現(xiàn)實(shí)中展翅高飛。
Now everything is for the future of dream weaving wings, let the dream fly in reality.
一、信號的基本概念
信號,在很多大型應(yīng)用程序中都經(jīng)常出現(xiàn)。信號就是一個(gè)通知(事件通知),用來通知某一個(gè)進(jìn)程發(fā)生的發(fā)生了某一件事。當(dāng)然,這些事情或者說這些信號一般都是突然事件,或者說都是突然到來的,進(jìn)程本身并不知道這些信號在什么時(shí)候發(fā)生。換句話說,信號是異步發(fā)生的,又稱軟件中斷。
在Nginx中,可以把信號想象成是一個(gè)mastrt進(jìn)程和work進(jìn)程之間的一個(gè)很有效的通信手段,所以把信號看成一種非常簡單的短消息。
1、信號一般是怎么產(chǎn)生的?
1.1、某個(gè)進(jìn)程發(fā)送給另一個(gè)進(jìn)程或者發(fā)送給自己
注意:進(jìn)程能把信號發(fā)送給自己。某個(gè)進(jìn)程把自己執(zhí)行起來后,再執(zhí)行一個(gè)自己并向原來的自己發(fā)送信號是可以的。
如Nginx在運(yùn)行過程中(1個(gè)master進(jìn)程,多個(gè)worker進(jìn)程),要做熱升級。熱升級是要啟動新的master進(jìn)程的,那么這個(gè)新啟動的master進(jìn)程啟動時(shí),通過增加一些命令行參數(shù)的手段就可以向舊的master進(jìn)程發(fā)送一些信號,從而控制舊的master進(jìn)程做一些動作。
1.2、由內(nèi)核發(fā)送給某個(gè)進(jìn)程
(1)通過在鍵盤上輸入一些命令動作,如按Ctrl + C組合鍵(中斷信號)、使用kill命令等。
(2)內(nèi)存訪問異常。除數(shù)為0等,硬件會檢測到并通知內(nèi)核等。
2、信號在系統(tǒng)中的定義
信號是有名字的,這些名字都是以SIG開頭,不同的系統(tǒng)支持的信號數(shù)量各不相同,少則支持十幾個(gè),多則支持50~60個(gè)。
信號雖然有名字,其實(shí)也都是一些數(shù)字。準(zhǔn)確的說,信號是一些正整數(shù),定義在系統(tǒng)的頭文件里,一般在編程的時(shí)候包含signal.h(usr/include/)頭文件即可。
輸入命令可以查詢: sudo find / -name "signal.h" | xargs grep - in "SIGHUP"
這個(gè)命令的含義是從根目錄開始,尋找一個(gè)名字叫做signal.h的文件, -name就是根據(jù)名字來查找。在某些文件中查找某行是否包含某個(gè)字符串時(shí),可以用上面的命令。(在一批文件里搜索一個(gè)字符串)。
grep是文本搜索工具,-i參數(shù)表示查找忽略大小寫,-n參數(shù)表示顯示查找到的行號,要查找文本字符串是”SIGHUP“。
xargs參數(shù)表示向其他命令傳遞參數(shù),用了xargs之后,find命令找到的文件內(nèi)容就能傳遞到grep中,所以grep實(shí)際是在找到的文件內(nèi)容中進(jìn)行搜索。
隨便找一個(gè)signal.h文件,利用vim編輯器查看內(nèi)容:
在vim編輯器查看signal.h的結(jié)果, 看起來大概有32個(gè)信號
,這些SIG開頭的信號其實(shí)就是一些宏定義。
二、通過kill命令認(rèn)識一些信號
相信很多人對kill的用法最熟悉的莫過于輸入”kill 進(jìn)程ID“來殺死一個(gè)進(jìn)程。其實(shí)kill命令所做的工作也是想進(jìn)程發(fā)送一個(gè)信號,如果沒有特別的要求,操作系統(tǒng)就會根據(jù)該信號,對該進(jìn)程執(zhí)行默認(rèn)的動作,也就是終止該進(jìn)程。
以 kill -數(shù)字 進(jìn)程ID
方式發(fā)送多種信號,不帶 -數(shù)字
,那個(gè)數(shù)字 默認(rèn)是15
,既 -15
。
kill的參數(shù) | 該參數(shù)發(fā)出的信號 | 操作系統(tǒng)默認(rèn)的動作 |
---|---|---|
-1 | SIGHUP(鏈接斷開) | 終止掉進(jìn)程(進(jìn)程沒了) |
-2 | SIGINT(終端中斷符,比如Ctrl + C) | 終止掉進(jìn)程(進(jìn)程沒了) |
-3 | SIGQUIT(終端退出符,比如Ctrl + \) | 終止掉進(jìn)程(進(jìn)程沒了) |
-9 | SIGKILL(終止) | 終止掉進(jìn)程(進(jìn)程沒了) |
-18 | SIGCONT(使暫停的進(jìn)程繼續(xù)) | 忽略(進(jìn)程依舊還在運(yùn)行不受影響) |
-19 | SIGSTOP(停止),可用SIGCONT繼續(xù),但任務(wù)被放到了后臺 | 停止進(jìn)程(不是終止,進(jìn)程還在) |
-20 | SIGTSTP (終端停止符,同Ctrl + Z),但任務(wù)被放到了后臺,可用SIGCONT繼續(xù) | 停止進(jìn)程(不是終止,進(jìn)程還在) |
值得一提的是-19對應(yīng)的SIGSTOP信號,該信號用于停止進(jìn)程,但進(jìn)程還在,和以往的信號(比如SIGKILL)不一樣(SKILL是終止進(jìn)程,進(jìn)程不在了)。
接下來,我們測試一下,寫一個(gè)無限循環(huán)、不停輸出信息的代碼:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *const *argv)
{
printf("hello nginx\n");
for(;;)
{
sleep(1);
printf("進(jìn)程休息1s\n");
}
printf("程序退出!再見!\n");
return 0;
}
編譯、鏈接并運(yùn)行,然后我們kill 5382
殺死進(jìn)程,如圖:
如果輸入kill 進(jìn)程ID就是向該進(jìn)程發(fā)送SIGTERM終止信號,同時(shí)進(jìn)程被終止了,顯示顯示killed by SIGTERM,說明被SIGTERM信號殺死了。
后續(xù)幾個(gè)信號就不列舉了,可以自己去嘗試一下。
三、進(jìn)程的狀態(tài)
如何查看進(jìn)程狀態(tài)呢?
查看進(jìn)程狀態(tài),使用ps命令,在顯示的列中增加一個(gè)stat 列即可。
ps -eo pid,ppid,sid,tty,pgrp,comm,stat
| grep -E ‘bash|PID|nginx’
還有一種方法,ps命令配合aux也可以顯示進(jìn)程狀態(tài)(aux代表一種BSD風(fēng)格的顯示格式)
ps -aux
| grep -E ‘bash|PID|nginx’
如圖:
STAT列顯示進(jìn)程狀態(tài)字母是什么意思呢?可以看下表:
狀態(tài) | 含義 |
---|---|
D | 不可中斷的休眠狀態(tài)(通常是I/O的進(jìn)程),可以處理信號,有延遲 |
R | 可執(zhí)行狀態(tài) & 運(yùn)行狀態(tài)(在運(yùn)行隊(duì)列里的狀態(tài)) |
S | 可中斷的休眠狀態(tài)之中(等待某事件完成),可以處理信號 |
T | 停止或被追蹤(被作業(yè)控制信號所停止) |
Z | 僵尸進(jìn)程 |
X | 死掉的進(jìn)程 |
< | 高優(yōu)先級的進(jìn)程 |
N | 低優(yōu)先級的進(jìn)程 |
L | 有些頁被鎖緊內(nèi)存 |
s | session leader(會話首進(jìn)程),其下有子進(jìn)程 |
t | 追蹤期間被調(diào)試器停止 |
+ | 位于前臺的進(jìn)程組 |
可以看到nginx進(jìn)程的狀態(tài)是S+狀態(tài)。S意為sleepling(睡眠),+意為在前臺運(yùn)行。
如果現(xiàn)在用kill -19命令發(fā)送SIGSTOP信號來停止nginx進(jìn)程(不是終止)則進(jìn)程還在。
看下圖的實(shí)例演示:
四、常用的Signal信號列表
常用的Signal信號如表所示:
信號名稱 | 信號含義 |
---|---|
SIGHUP(1)(鏈接斷開) | 終端斷開信號。如果終端接口檢測到一個(gè)鏈接斷開,發(fā)送信號到該終端所在的會話首進(jìn)程,默認(rèn)動作會導(dǎo)致所有相關(guān)的進(jìn)程退出(Xshell斷開就會發(fā)送此信號)。kill -1 進(jìn)程號 也能發(fā)送此信號給進(jìn)程。 |
SIGALM(定時(shí)器超時(shí)) | 一般調(diào)用系統(tǒng)函數(shù)alarm創(chuàng)建定時(shí)器后,定時(shí)器超時(shí)就會產(chǎn)生這個(gè)信號 |
SIGINT(2)(中斷) | 輸入Ctrl + C(如進(jìn)程正在循環(huán)中做一件事),按Ctrl + C 組合鍵(中斷鍵)就能打斷進(jìn)程正在做的事,按Ctrl + C組合鍵(中斷鍵)就能打開進(jìn)程正在做的事,終止進(jìn)程。但shell會將后臺進(jìn)程對該信號的處理設(shè)置忽略(也就是說該進(jìn)程若在后臺運(yùn)行則不會收到該信號) |
SIGSEGV(無效內(nèi)存) | 內(nèi)存訪問異常,除數(shù)為0等,硬件會檢測到并通知內(nèi)核。其實(shí)這個(gè)SEGV代表斷違例(segmentation violation),有時(shí)候運(yùn)行一個(gè)自己寫的C程序,如果程序內(nèi)存訪有問題,執(zhí)行時(shí)也會出現(xiàn)這個(gè)信號 |
SIGIO(異步I/O) | 通知異步I/O信號,如果通信接口套接字接口上有數(shù)據(jù)到達(dá),或發(fā)生一些異步錯(cuò)誤,內(nèi)核就會向進(jìn)程通知該信號。 |
SIGCHLD(子進(jìn)程改變) | 一個(gè)進(jìn)程終止或停止時(shí),這個(gè)信號會被發(fā)送給父進(jìn)程(想象一下官方的Nginx: worker進(jìn)程終止時(shí),master進(jìn)程應(yīng)該會收到內(nèi)核發(fā)出的針對該信號的通知。) |
SIGUSR1、SIGUSR2(都是用戶定義的信號) | 用戶定義的信號,可用于應(yīng)用程序。 |
SIGTERM(15)(終止) | 一般 通過在命令行輸入”kill 進(jìn)程ID“ 命令殺死一個(gè)進(jìn)程時(shí)就會觸發(fā)這個(gè)信號,程序收到這個(gè)信號后,可以編寫代碼做退出前的處理工作,實(shí)現(xiàn)”優(yōu)雅退出“ |
SIGKILL(-9)(終止) | 該信號不能被忽略,不能被進(jìn)程本身捕捉,是殺死任意進(jìn)程的可靠方法 |
SIGSTOP(19)(停止) | 該信號不能被忽略,不能被進(jìn)程本身捕捉,使進(jìn)程停止執(zhí)行??梢园l(fā)送SIGCONT信號讓該進(jìn)程繼續(xù)執(zhí)行,但繼續(xù)執(zhí)行后該進(jìn)程會被放入后臺 |
SIGQUIT(3)(終端退出符) | 按Ctrl + \ 組合鍵會觸發(fā)該信號。但shell會將后臺進(jìn)程對該信號的處理設(shè)置為忽略(也就是說在后臺運(yùn)行則不會收到該信號) |
SIGCONT(18)(使暫停的進(jìn)程繼續(xù)) | 使 暫停的進(jìn)程繼續(xù)運(yùn)行
|
SIGTSTP(20)(終端停止符) | 按Ctrl + Z 組合鍵觸發(fā)該信號。 進(jìn)程被停止,并被放入后臺,可以用SIGCONT繼續(xù)運(yùn)行
|
五、信號處理的相關(guān)動作
回顧剛才講的kill命令時(shí),用不同數(shù)字向nginx進(jìn)程發(fā)送信號,結(jié)果是不同的,如果用-1、-2、-3、-9等發(fā)送信號時(shí),結(jié)果是不同的,發(fā)現(xiàn)nginx進(jìn)程被殺掉了(用ps看不到了);但如果用-18發(fā)信號時(shí)(讓進(jìn)程繼續(xù)運(yùn)行的信號)時(shí),發(fā)現(xiàn)nginx進(jìn)程沒什么反應(yīng);用-19、-20發(fā)信號時(shí),發(fā)現(xiàn)nginx進(jìn)程停止了,但沒有終止(用ps還能看到nginx進(jìn)程的存在),可以用-18發(fā)送信號讓停止的nginx進(jìn)程繼續(xù)運(yùn)行。
當(dāng)某個(gè)信號出現(xiàn)時(shí),可以按以下三種方式之一進(jìn)行處理。這些方法稱為信號的處理或與信號相關(guān)的動作。(1)執(zhí)行系統(tǒng)的默認(rèn)動作
如果在代碼中沒有特別針對信號的處理代碼,當(dāng)應(yīng)該進(jìn)程收到信號時(shí),操作系統(tǒng)(內(nèi)核)會對該進(jìn)程執(zhí)行一個(gè)默認(rèn)動作。不同的信號對應(yīng)不同的默認(rèn)動作,但是對于絕大多數(shù)信號,內(nèi)核對進(jìn)程執(zhí)行的默認(rèn)動作是把該進(jìn)程殺死(終止該進(jìn)程)。
(2)忽略此信號
如果不希望當(dāng)收到某個(gè)信號的時(shí)候,內(nèi)核執(zhí)行該信號所對應(yīng)的默認(rèn)動作(如把進(jìn)程殺死),可以通過寫一些代碼使操作系統(tǒng)忽略此信號,操作系統(tǒng)發(fā)現(xiàn)代碼中實(shí)現(xiàn)了對該信號的忽略,就不會針對該信號對進(jìn)程采取任何動作。
可能會有一個(gè)疑問:如果某個(gè)進(jìn)程忽略了所有的信號,那內(nèi)核豈不是完全殺不死該進(jìn)程了?
當(dāng)然不是這樣的,因?yàn)橹挥袃蓚€(gè)信號不能被忽略(特權(quán)信號),就是SIGKILL和SIGSTOP。如果遇到常規(guī)殺不死的進(jìn)程,可以使用kill -9的命令,正常情況下都能把進(jìn)程殺死。
(3)捕捉該信號
所謂捕捉該信號,就是在代碼中寫一個(gè)信號處理函數(shù),當(dāng)系統(tǒng)收到該信號時(shí),會自動調(diào)用該處理函數(shù)來處理。
當(dāng)然,就算寫了自己的信號處理函數(shù),SIGKILL和SIGSTOP這兩個(gè)信號仍然是特權(quán)信號,因此不要寫代碼去捕捉這兩個(gè)信號,因?yàn)檫@樣的代碼是無效的。
六、小結(jié)
希望對信號有一個(gè)較好的理解,因?yàn)樵诤芏啻笮蛙浖_中,信號的使用都會體現(xiàn)在很多方面。文章來源:http://www.zghlxwxcb.cn/news/detail-511298.html
點(diǎn)擊進(jìn)入系列文章目錄文章來源地址http://www.zghlxwxcb.cn/news/detail-511298.html
到了這里,關(guān)于Linux信號概念、認(rèn)識、處理動作 ( 2 ) -【Linux通信架構(gòu)系列 】的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!