Linux中的信號(注冊,注銷,處理,阻塞)
未決:pending(漢語翻譯:待定,即將發(fā)生)
- 信號產(chǎn)生但沒有被處理,pending表0變1,表示進程收到對應(yīng)信號(準確的來說叫信號寫入)
- 阻塞,為了攔截對應(yīng)信號做出對應(yīng)的處理動作。不想處理某些信號,攔不住別人給進程發(fā)
- 信號待定是可以隨時遞達(被處理),如果沒有被阻塞,信號會在合適的時候遞達(被處理)
- 什么叫合適的時候:從內(nèi)核態(tài)切換到用戶態(tài)時進行信號的檢測和處理
- 如果信號阻塞,該信號永遠處于未決狀態(tài)
- 總之pending表就是隨時能處理信號,一旦設(shè)置了阻塞,只有解除阻塞才能處理信號
信號注冊
信號注冊:在進程中做標記,讓進程能夠知道自己收到某個信號,官方說法操作系統(tǒng)內(nèi)核給進程發(fā)送信號的過程,這個過程稱為注冊
- PCB中有一個pending未決(沒有被處理)信號的集合(是一個位圖結(jié)構(gòu)),標記進程收到了某個信號
- PCB中有一個sigqueue鏈表(實質(zhì)是一個雙向鏈表),進程每收到一個信號,就是給該鏈表中添加一個對應(yīng)信號的節(jié)點,并且修改位圖,將信號值對應(yīng)位置置1
非可靠信號注冊:
- 如果沒有收到信號則注冊(添加節(jié)點,位圖置1)
- 如果之前收到了信號還未處理,則什么都不做(丟棄)
可靠信號注冊:
- 不管位圖是否置1(是否已經(jīng)注冊,還沒處理),都會添加一個信號節(jié)點
可以通過sigqueue函數(shù)在發(fā)送信號的同時攜帶數(shù)據(jù)
信號注銷
什么是注銷:
- 在信號被處理之前,消除信號存在的痕跡
- 主要防止信號被重復(fù)處理
非可靠信號的注銷:
刪除信號的信息節(jié)點,位圖置0
可靠信號的注銷:
刪除信號的一個信息節(jié)點,當前沒有相同節(jié)點則位圖置0
信號處理
處理就是調(diào)用信號的處理函數(shù)
有三種處理方式
默認處理:系統(tǒng)中已經(jīng)定義好了處理方式
忽略處理:空的處理方式
SIGCHLD:子進程再退出時給父進程發(fā)送一個SIGCHLD信號,父進程對于該信號的處理方式是忽略處理 該信號對于所有進程而言都是忽略處理
自定義處理:自己定義一個處理函數(shù),替換掉處理函數(shù)
自定義處理函數(shù)signal
用handler函數(shù),替換signum信號當前的處理函數(shù)
自定義信號的捕捉流程(捕捉流程:從用戶態(tài)進入內(nèi)核態(tài),找到信號并處理的過程)
進入內(nèi)核的原因:
- 系統(tǒng)調(diào)用函數(shù)
- 中斷
- 進程異常
當前執(zhí)行流在用戶態(tài)收到信號,不能立即處理信號,而是從用戶態(tài)切換到內(nèi)核態(tài)時處理
進入內(nèi)核后,執(zhí)行流并不知道要處理信號,而是執(zhí)行一些功能當要從內(nèi)核態(tài)返回到用戶態(tài),一定會調(diào)用do_signal函數(shù)判斷當前進程有無收到信號,收到則立即處理,沒有直接返回到用戶態(tài)
如果有信號就執(zhí)行信號對應(yīng)動作
如果是自定義處理方式,會從內(nèi)核態(tài)切換到用戶態(tài)執(zhí)行自定義函數(shù),調(diào)用sigreturn函數(shù)再次進入到內(nèi)核(不能以內(nèi)核身份執(zhí)行用戶寫的自定義代碼)
再去調(diào)用dosignal判斷是否有其它信號需要處理
信號阻塞
阻塞(阻塞不是在處理信號)
信號被阻塞后依然會注冊,但是暫時不處理(直到被解除阻塞)
PCB中有個block阻塞信號集合,哪個信號被添加到block阻塞集合中,表示收到信號但暫時不處理
更改或獲取進程的信號block表(將信號集合設(shè)置進內(nèi)核)
int sigprocmask(int how, const sigset_t* set, sigset_t* oset)
how:對PCB中信號阻塞集合進行的操作
SIG_BLOCK:block|=set
把set集合中的信號添加到block集合中,阻塞set集合中的信號
SIG_UNBLOCK:block&=~set
解除
SIG_SETMASK:block=set
重新設(shè)置block表
oldset將修改前block集合中的信息添加到old中,便于還原
是輸出型參數(shù),將原block返回,不想返回設(shè)置為NULL
要屏蔽2號信號,將2號信號添加到set中
set是輸入型參數(shù)
- 函數(shù)在阻塞指定信號時,就要傳進去一個集合, 意味著要將所有的信號添加到一個集合中
- 所以要先定義一個信號集合的變量sigset_t
- 通過sigsetempty(sigset* set)先將集合中的數(shù)據(jù)清空
- 通過sigaddset(sigset_t* set, int sugnum)----將signum添加到set集合中(單個添加)
- 通過sigfillset(sigset_t *set)------------------------將所有信號添加到set中
- 通過sigdelset(sigset_t *set, int signum)----移除指定信號
- 通過sigismember(sigset_t *set, int signum)–按段信號signum是(返回1)否(-1)在set信號集合中
9號和19號不能被阻塞,忽略,自定義
僵尸進程是已經(jīng)死了的
SIGPIPE
- 管道讀端關(guān)閉,繼續(xù)寫,會在write會觸發(fā)異常
SIGCHLD
- 子進程退出時給父進程發(fā)送的信號
- 這個信號的默認處理方式是忽略,因此未對子進程退出做出處理
修改SIGCHLD的默認處理方法(讓回調(diào)函數(shù)內(nèi)部wait子進程)其實并不合理
因為該信號是非可靠信號,大量子進程同時退出,發(fā)出的多個非可靠信號可能只注冊了一次
可直接顯示忽略,明確告訴OS,子進程退出我不關(guān)心,直接把資源回收
signal(SIGCHLD, SIG_IGN)
或者在回調(diào)函數(shù)內(nèi)部,使用waitpid循環(huán)等待每一個子進程退出的信號
int sigpending(sigset_t *set)
獲取當前進程的pending信號集(輸出型參數(shù)),放入set集合中
PCB如何獲取signal函數(shù)使用自定義信號
在PCB有一個結(jié)構(gòu)體指針struct sighand_struct* sighand,該指針指向結(jié)構(gòu)體sighand_struct;
該結(jié)構(gòu)體中有一個struct k_sigaction action[_NSIG]數(shù)組,數(shù)組中的每個元素是一個多重嵌套結(jié)構(gòu)體,如圖
- action數(shù)組中一個元素就保存了一個信號對應(yīng)的處理方式,
- 例如,2號信號對應(yīng)一個action數(shù)組中的元素,2號信號的處理方式就保存在_sighander_t類型的變量中
- 該類型的變量保存了三種數(shù)據(jù),就是上述的三種信號處理方式,默認處理、忽略處理、自定義處理
PCB與signal函數(shù)的關(guān)系
- 在調(diào)用signal函數(shù),參數(shù)sighandler保存了新函數(shù)地址,該新地址是傳送給PCB中action數(shù)組的元素,該元素內(nèi)容被修改
- 就通過signal函數(shù)改變了2號信號的處理方式,
- 當進程在處理2號時通過PCB一步步找到action數(shù)組找到2號信號的處理方式,發(fā)現(xiàn)其處理方式是自定義的函數(shù)
- 從而調(diào)用自定義的函數(shù)
sigaction函數(shù)
修改指定信號的處理動作
改變action數(shù)組元素中第二個結(jié)構(gòu)體,而signal函數(shù)只是改變該結(jié)構(gòu)體下的一個成員變量
需要配合其他函數(shù)使用
sa_handler是信號的處理方式
sa_mask:信號正在被處理時,內(nèi)存會將當前信號加入到信號屏蔽字,信號返回時回復(fù)成原來的信號屏蔽字
這樣保證了在處理某個信號時,此信號再次產(chǎn)生就會被阻塞直到當前處理結(jié)束
需要用到的清空函數(shù)(上圖黑色部分指錯了):
可重入函數(shù)
現(xiàn)有一個鏈表需要進行頭插,在執(zhí)行頭插函數(shù)時,代碼進入內(nèi)核在退出時發(fā)現(xiàn)一個信號,該信號也是在執(zhí)行頭插代碼
這樣會發(fā)生內(nèi)存泄漏,信號所調(diào)用的頭插函數(shù)插入的節(jié)點找不到了,這種函數(shù)就稱為不可重入函數(shù)
如果只是訪問變量或者參數(shù),則稱為可重入函數(shù)
volatile關(guān)鍵字
在編譯的時候,全局變量flags只進行邏輯運算,編譯器會自動將其優(yōu)化,將變量優(yōu)化到寄存器中
不從內(nèi)存中讀取,而信號修改flag的值只是對內(nèi)存值修改
防止編譯時因優(yōu)化,使CPU不從內(nèi)存中取值,而從寄存器中取值
sigchld
子進程退出時不是默默的退出
子進程退出時會給父進程發(fā)送SIGCHLD信號,該信號的默認處理方式是IGN(忽略)
無論是阻塞還是非阻塞,都需要父進程主動去詢問
則現(xiàn)在提前告訴子進程,子進程死亡時發(fā)消息,父進程收到后回收資源即可
所以通過signal函數(shù)修改SIGCHLD信號的處理方式,改為wait等待子進程即可
前七個子進程結(jié)束,通過waitpid等待成功,在等待第八個子進程退出時,父進程會阻塞在waitpid函數(shù)處
通過sigaction將SIGCHLD的處理動作設(shè)置為SIG_IGN,這樣fork出來的子進程在終止時會自動清理掉,不會產(chǎn)生僵尸進程
這兩個忽略其實沒有任何區(qū)別
意義:手動設(shè)置了SIG_IGN,除了修改父進程,子進程也會受影響,讓子進程退出不給對應(yīng)父進程發(fā)送信號
在OS層面上,手動設(shè)置信號,會在設(shè)置子進程時不給父進程發(fā)消息,并自動釋放
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Ubuntu安裝MySQL5.7并配置數(shù)據(jù)存儲路徑的方法步驟
這篇文章主要介紹了Ubuntu安裝MySQL5.7并配置數(shù)據(jù)存儲路徑的方法步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-06-06CentOS8.0 網(wǎng)絡(luò)配置的實現(xiàn)
這篇文章主要介紹了CentOS8.0 網(wǎng)絡(luò)配置的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10Linux系統(tǒng)管理常用命令及參數(shù)說明中文版
這篇文章主要介紹了Linux系統(tǒng)管理常用命令及其參數(shù)的中文版說明,方便了英文不好的同學學習Linux,需要的朋友可以參考下2014-03-03Ubuntu基礎(chǔ)教程之a(chǎn)pt-get命令
這篇文章主要給大家介紹了關(guān)于Ubuntu基礎(chǔ)教程之a(chǎn)pt-get命令的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Ubuntu系統(tǒng)具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2019-08-08Linux關(guān)機時執(zhí)行指定腳本功能實現(xiàn)
本篇文章給大家分享了Linux關(guān)機時執(zhí)行指定腳本功能的實現(xiàn)詳解,對此有需要的朋友跟著小編一起學習下。2018-03-03linux系統(tǒng)中設(shè)置定時任務(wù)的實現(xiàn)方式
在SpringBoot中設(shè)置定時任務(wù),需要使用@EnableScheduling注解和@Scheduled注解,配合cron表達式,在Linux系統(tǒng)中,使用crontab工具可設(shè)置系統(tǒng)級的定時任務(wù),首先需要創(chuàng)建執(zhí)行腳本,并賦予執(zhí)行權(quán)限,然后通過crontab?-e進入編輯界面2024-10-10