亚洲熟女综合色一区二区三区,亚洲精品中文字幕无码蜜桃,亚洲va欧美va日韩va成人网,亚洲av无码国产一区二区三区,亚洲精品无码久久久久久久

一篇文章徹底搞定Linux信號!

1.信號是什么?

信號其實就是一個軟件中斷。

例:

  1. 輸入命令,在 Shell 下啟動一個前臺進程。
  2. 用戶按下 Ctrl-C,鍵盤輸入產(chǎn)生一個硬件中斷。
  3. 如果 CPU 當(dāng)前正在執(zhí)行這個進程的代碼,則該進程的用戶空間代碼暫停執(zhí)行, CPU 從用戶態(tài)切換到內(nèi)核態(tài)處理硬件中斷。
  4. 終端驅(qū)動程序?qū)?Ctrl-C 解釋成一個 SIGINT 信號,記在該進程的 PCB 中(也可以說發(fā)送了一個 SIGINT 信號給該進程)。
  5. 當(dāng)某個時刻要從內(nèi)核返回到該進程的用戶空間代碼繼續(xù)執(zhí)行之前,首先處理 PCB 中記錄的信號,發(fā)現(xiàn)有一個 SIGINT 信號待處理,而這個信號的默認處理動作是終止進程,所以直接終止進程而不再返回它的用戶空間代碼執(zhí)行。

?在這個例子中,由 ctrl+c 產(chǎn)生的硬件中斷就是一個信號。Ctrl+C 產(chǎn)生的信號只能發(fā)送給前臺進程,命令后加 & 就可放到后臺運行。Shell 可同時運行一個前臺進程和任意多個后臺進程,只有前臺進程才能接受到像 CTRL+C 這種控制鍵產(chǎn)生的信號。

?

2.信號的種類

使用命令查看:

kill?-l

非可靠信號:1~31 號信號,信號可能會丟失 可靠信號:34~64 號信號,信號不可能丟失

一篇文章徹底搞定Linux信號!

SIGHUP:1 號信號,Hangup detected on controlling terminal or death of controlling process(在控制終端上掛起信號,或讓進程結(jié)束),ation:term

SIGINT:2 號信號,Interrupt from keyboard(鍵盤輸入中斷,「ctrl + c」?),action:term

SIGQUIT:3 號信號,Quit from keyboard(鍵盤輸入退出「ctrl+ |」?),action:core,產(chǎn)生 core dump 文件

SIGABRT:6 號信號,Abort signal from abort(3)(非正常終止,「double free」),action:core

SIGKILL:9 號信號,Kill signal(殺死進程信號),action:term,該信號不能被阻塞、忽略、自定義處理

SIGSEGV:11 號信號,Invalid memory reference(無效的內(nèi)存引用,解引用空指針、內(nèi)存越界訪問),action:core

SIGPIPE:13 號信號,Broken pipe: write to pipe with no readers(管道中止: 寫入無人讀取的管道,會導(dǎo)致管道破裂),action:term

SIGCHLD:17 號信號,Child stopped or terminated(子進程發(fā)送給父進程的信號,但該信號為忽略處理的)

SIGSTOP:19 號信號,Stop process(停止進程),action:stop

SIGTSTP:20 號信號,Stop typed at terminal(終端上發(fā)出的停止信號,「ctrl + z」),action:stop

具體的信號采取的動作和詳細信息可查看:「man 7 signal」

3.信號的產(chǎn)生

3.1 硬件產(chǎn)生

硬件產(chǎn)生即通過終端按鍵產(chǎn)生的信號:

  1. ctrl + c:SIGINT(2),發(fā)送給前臺進程,& 進程放到后臺運行,fg 把剛剛放到后臺的進程,再放到前臺來運行
  2. ctrl + z:SIGTSTP(20),一般不用,除非有特定場景
  3. ctrl + | :SIGQUIT(3),產(chǎn)生 core dump 文件

產(chǎn)生 core dump 文件的條件:

當(dāng)前OS一定不要限制core?dump文件的大小,ulimit?-a
磁盤空間要足夠
如何產(chǎn)生:
3.1?解引用空指針,收到11號信號,產(chǎn)生core?dump文件
3.2?內(nèi)存訪問越界,程序一旦崩潰,就會收到11號信號,也就會產(chǎn)生core?dump文件
3.3 double free,收到6號信號,并產(chǎn)生core dump。
3.4?free(NULL),不會崩潰

3.2 軟件產(chǎn)生

軟件產(chǎn)生即調(diào)用系統(tǒng)函數(shù)向進程發(fā)信號

  1. kill 函數(shù)
#include?<sys/types.h>
#include?<signal.h>
int?kill(pid_t?pid,?int?sig);
參數(shù)解釋:
pid:進程號
sig:要發(fā)送的信號值
返回值:成功返回0,失敗返回-1,并設(shè)置錯誤
  1. kill 命令:kill -[信號] pid,
  2. abort:void abort(void);,收到 6 號信號,誰調(diào)用該函數(shù),誰就收到信號
  3. alarm:unsigned int alarm(unsigned int seconds);,收到 14 號信號,告訴內(nèi)核在 seconds 秒后給進程發(fā)送 SIGALRM 信號,該信號默認處理動作為終止當(dāng)前進程。

4.信號的注冊

信號注冊又分為可靠信號的注冊和非可靠信號的注冊。信號注冊實際上是一個位圖和一個 sigqueue 隊列。

圖片

4.1 非可靠信號的注冊

當(dāng)進程收到非可靠信號時:

  1. 將非可靠信號對應(yīng)的比特位置為 1
  2. 添加 sigqueue 節(jié)點到 sigqueue 隊列當(dāng)中,但是,在添加 sigqueue 節(jié)點的時候,隊列當(dāng)中已然有了該信號的 sigqueue 節(jié)點,則不添加

4.2 可靠信號的注冊

當(dāng)進程所受到可靠信號時:

?在 sig 位圖中更改信號對應(yīng)的比特位為 1 不論之前 sigqueue 隊列中是否存在該信號的 sigqueue 節(jié)點,都再次添加 sigqueue 節(jié)點到 sigqueue 隊列當(dāng)中去

?

5.信號的注銷

5.1 非可靠信號的注銷

信號對應(yīng)的比特位從 1 置為 0 將該信號的 sigqueue 節(jié)點從 sigqueue 隊列當(dāng)中進行出隊操作

5.2 可靠信號的注銷

?將該信號的 sigqueue 節(jié)點從 sigqueue 隊列當(dāng)中進行出隊操作 需要判斷 sigqueue 隊列當(dāng)中是否還有相同的 sigqueue 節(jié)點:①沒有了:信號比特位從 1 置為 0 ②還有:不會更改 sig 位圖中的比特位

?

6.信號阻塞

6.1 信號是怎樣阻塞的?

一篇文章徹底搞定Linux信號!

?信號的阻塞,并不會干擾信號的注冊。信號能注冊,但不能被立即處理, 將 block 位圖中對應(yīng)的信號比特位置為 1,表示阻塞該信號 進程收到該信號,還是一如既往的注冊 當(dāng)進程進入到內(nèi)核空間,準備返回用戶空間的時候,調(diào)用 do_signal 函數(shù),就不會立即去處理該信號了 當(dāng)該信號不被阻塞后,就可以進行處理了

?

6.2sigprocmask

函數(shù)原型:int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 參數(shù)解釋:

how,該做什么樣的操作
SIG_BLOCK:設(shè)置信號為阻塞
SIG_UNBLOCK:解除信號阻塞
SIG_SETMASK:替換阻塞位圖
set:用來設(shè)置阻塞位圖
SIG_BLOCK:設(shè)置某個信號為阻塞,block(new)?= block(old)?|?set
SIG_UNBLOCK:解除某個信號阻塞,block(new)= block(old)?&?(~set)
SIG_SETMASK:替換阻塞位圖,block(new)=?set
oldset:原來的阻塞位圖

例:下述例子,信號全部被阻塞,采用 kill -9,將該進程結(jié)束掉

#include?<stdio.h>
#include?<signal.h>
#include?<unistd.h>


void?signcallback(int?signumber)
{
??printf("change?the?signal?%dn",signumber);
}

int?main()
{
??sigset_t?set;
??sigset_t?oldset;
??sigfillset(&set);//所有比特位全置為1,則信號全部會被阻塞
??sigprocmask(SIG_BLOCK,&set,&oldset);
??while(1)
??{
????sleep(1);
??}

??return?0;
}

結(jié)果:此時發(fā)送信號是不會有作用的,采用 kill -9 強殺掉

一篇文章徹底搞定Linux信號!

7.信號未決

7.1 未決概念

實際執(zhí)行信號的處理動作稱為信號遞達(Delivery),信號從產(chǎn)生到遞達之間的狀態(tài),稱為信號未決(Pending)。進程可以選擇阻塞(Block)某個信號。被阻塞的信號產(chǎn)生時將保持在未決狀態(tài),直到進程解除對此信號的阻塞,才執(zhí)行遞達的動作。注意,阻塞和忽略是不同的,只要信號被阻塞就不會遞達,而忽略是、在遞達之后可選的一種處理動作。

7.2 sigpending

函數(shù)原型:int sigpending(sigset_t *set); 讀取當(dāng)前進程的未決信號集,通過 set 參數(shù)傳出。調(diào)用成功返回 0,出錯返回 - 1.

例:

#include?<stdio.h>
#include?<unistd.h>
#include?<signal.h>

void?signalcallback(int?signumber)
{
??printf("chang?signumber?%dn",signumber);
}
void?printsigset(sigset_t?*set)
{
??int?i?=?0;
??for(;i?<?32;i++)
??{
????if(sigismember(set,i))
????{
??????putchar('1');
????}
????else{
??????putchar('0');
????}
??}
}

int?main()
{
??signal(2,signalcallback);
??signal(10,signalcallback);
??sigset_t?set;
??sigset_t?oldset;
??sigset_t?pending;
??sigfillset(&set);//所有比特位全部置為1,則信號會全部被阻塞
??sigprocmask(SIG_BLOCK,&set,&oldset);
??while(1)
??{
????sigpending(&pending);
????printsigset(&pending);
????sleep(1);
??}

??return?0;
}

結(jié)果:

一篇文章徹底搞定Linux信號!

8.信號的處理方式

一篇文章徹底搞定Linux信號!
每個信號都有兩個標志位分別表示阻塞和未決,還有一個函數(shù)指針表示處理動作。

在上述例子中:

  1. SIGHUP 信號未阻塞也未產(chǎn)生過,當(dāng)它遞達時執(zhí)行默認處理動作。
  2. SIGINT 信號產(chǎn)生過,但正在被阻塞,所以暫時不能遞達。雖然它的處理動作是忽略,但在沒有解除阻塞之前不能忽略這個信號,因為進程仍有機會改變處理動作之后再解除阻塞。
  3. SIGQUIT 信號未產(chǎn)生過,一旦產(chǎn)生 SIGQUIT 信號將被阻塞,它的處理動作是用戶自定義函數(shù) sighandler。

8.1signal 函數(shù)

該函數(shù)可以更改信號的處理動作。

typedef?void?(*sighandler_t)(int);
sighandler_t?signal(int?signum,?sighandler_t?handler);
參數(shù)解釋:

signum:更改的信號值
handler:函數(shù)指針,要更改的動作是什么

實際上,該函數(shù)內(nèi)部也調(diào)用了 sigaction 函數(shù)。

8.2sigaction 函數(shù)

讀取和修改與指定信號相關(guān)聯(lián)的處理動作。

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

參數(shù)解釋:

signum:待更改的信號值

struct sigaction 結(jié)構(gòu)體:

void?????(*sa_handler)(int);//函數(shù)指針,保存了內(nèi)核對信號的處理方式
void?????(*sa_sigaction)(int,?siginfo_t?*,?void?*);//
sigset_t???sa_mask;//保存的是當(dāng)進程在處理信號的時候,收到的信號
int????????sa_flags;//SA_SIGINFO,OS在處理信號的時候,調(diào)用的就是sa_sigaction函數(shù)指針當(dāng)中
//保存的值0,在處理信號的時候,調(diào)用sa_handler保存的函數(shù)
void?????(*sa_restorer)(void);

例:

#include?<stdio.h>
#include?<unistd.h>
#include?<signal.h>

void?signcallback(int?signumber)
{
??printf("change?signumber?%dn",signumber);
}


int?main()
{
??struct?sigaction?act;//act為入?yún)?
??sigemptyset(&act.sa_mask);
??act.sa_flags?=?0;
??act.sa_handler?=?signcallback;

??struct?sigaction?oldact;//oldact為出參
??sigaction(3,&act,&oldact);
??while(1)
??{
????sleep(1);
??}
??return?0;
}

結(jié)果:

一篇文章徹底搞定Linux信號!

8.3 自定義信號處理的流程

一篇文章徹底搞定Linux信號!
  1. 「task_struct」?結(jié)構(gòu)體中有一個「struct sighand_struct」?結(jié)構(gòu)體。
  2. 「struct sighand_struct」?結(jié)構(gòu)體有一個?「struct k_sigaction action[_NSIG]」?結(jié)構(gòu)體數(shù)組。
  3. 該數(shù)組中,其中的?「_sighandler_t sa_handler」?保存的是信號的處理方式,通過改變其指向,可以實現(xiàn)我們對自定義信號的處理。

9.信號的捕捉

9.1 信號捕捉的條件

?如果信號的處理動作是用戶自定義函數(shù),在信號遞達時就調(diào)用這個函數(shù),這就稱為信號捕捉。

?

9.2 信號捕捉流程

一篇文章徹底搞定Linux信號!

內(nèi)核態(tài)返回用戶態(tài)會調(diào)用 do_signal 函數(shù),兩種情況:

  1. 無信號:sys_return 函數(shù),返回用戶態(tài)
  2. 有信號:先處理信號,信號返回,再調(diào)用 do_signal 函數(shù) 例:
  3. 程序注冊了 SIGQUIT 信號的處理函數(shù) sighandler。
  4. 當(dāng)前正在執(zhí)行 main 函數(shù),這時發(fā)生中斷或異常切換到內(nèi)核態(tài)。
  5. 在中斷處理完畢后要返回用戶態(tài)的 main 函數(shù)之前檢查到有信號 SIGQUIT 遞達。
  6. 內(nèi)核決定返回用戶態(tài)后不是恢復(fù) main 函數(shù)的上下文繼續(xù)執(zhí)行,而是執(zhí)行 sighandler 函數(shù), sighandler 和 main 函數(shù)使用不同的堆??臻g,它們之間不存在調(diào)用和被調(diào)用的關(guān)系,是兩個獨立的控制流程。
  7. sighandler 函數(shù)返回后自動執(zhí)行特殊的系統(tǒng)調(diào)用 sigreturn 再次進入內(nèi)核態(tài)。
  8. 如果沒有新的信號要遞達,這次再返回用戶態(tài)就是恢復(fù) main 函數(shù)的上下文繼續(xù)執(zhí)行了。

10.常用信號集操作函數(shù)

int?sigemptyset(sigset_t?*set);://將比特位圖全置為0

int?sigfillset(sigset_t?*set);//將比特位圖全置為1

int?sigaddset(sigset_t?*set,?int?signum);//將該set位圖,多少號信號置為1

int?sigdelset(sigset_t?*set,?int?signum);//將該set位圖,多少號信號置為0

int?sigismember(const?sigset_t?*set,?int?signum);//信號signum是否是set位圖中的信號

11.SIGCHLD 信號


該信號是子進程在結(jié)束是發(fā)送給父進程的信號,但是該信號的處理方式是默認處理的。父進程對子進程發(fā)送過來的 SIGCHLD 信號進行了忽略處理,就會導(dǎo)致子進程成為僵尸進程。

可以自定義該信號的處理方式:

#include?<stdio.h>
#include?<unistd.h>
#include?<signal.h>
#include?<string.h>
#include?<sys/wait.h>
#include?<stdlib.h>

void?signcallback(int?signumber)
{
??printf("change?signal?%dn",signumber);
??wait(NULL);
}

int?main()
{
??signal(17,signcallback);
??pid_t?pid?=?fork();
??if(pid?<?0)
??{
????perror("fork");
????return?-1;
??}
??else?if(pid?==?0)
??{
????printf("I?am?childn");
????sleep(1);
????exit(12);
??}
??else{
????while(1)
????{
??????sleep(1);
????}
??}
??return?0;
}

指令查看后臺:「ps aux | grep ./fork」

一篇文章徹底搞定Linux信號!

鏈接:https://blog.csdn.net/w903414/article/details/109802539

(版權(quán)歸原作者所有,侵刪)

相關(guān)新聞

歷經(jīng)多年發(fā)展,已成為國內(nèi)好評如潮的Linux云計算運維、SRE、Devops、網(wǎng)絡(luò)安全、云原生、Go、Python開發(fā)專業(yè)人才培訓(xùn)機構(gòu)!