用flash做的網(wǎng)站展示品牌策劃方案模板
1. 信號什么時候被處理
? ? ? ? 當(dāng)進(jìn)程從內(nèi)核態(tài)返回到用戶態(tài)的時候,進(jìn)行信號的檢測和處理
什么內(nèi)核態(tài),什么又是用戶態(tài)呢?
? ? ? ? 當(dāng)進(jìn)程在CPU上運(yùn)行時,內(nèi)核態(tài):允許進(jìn)程訪問操作系統(tǒng)的代碼和數(shù)據(jù),用戶態(tài):進(jìn)程只能訪問用戶自己的代碼和數(shù)據(jù)
為什么要有,用戶態(tài)和內(nèi)核態(tài)呢?
? ? ? ? 因?yàn)檫M(jìn)程需要訪問系統(tǒng)內(nèi)的資源,以及調(diào)用系統(tǒng)函數(shù)接口,例如IO就是系統(tǒng)調(diào)用,一個進(jìn)程只有在內(nèi)核態(tài)通過系統(tǒng)調(diào)用才能訪問操作系統(tǒng)的資源,這樣可以保證系統(tǒng)的安全性
從用戶態(tài)切換到內(nèi)核態(tài)的方式:
- 系統(tǒng)調(diào)用(int 80)
- 異常
- 外圍設(shè)備中斷
????????CPU內(nèi)部有一個ecs寄存器,它的后兩位標(biāo)識進(jìn)程屬于內(nèi)核態(tài)還是用戶態(tài),00標(biāo)識內(nèi)核態(tài),11標(biāo)識用戶態(tài)。int 80 匯編語句可以讓ecs從11變成00,從用戶態(tài)切換到內(nèi)核態(tài)
內(nèi)核態(tài)和用戶態(tài)分離是如何實(shí)現(xiàn)的?
? ? ? ? ?操作系統(tǒng)的代碼和數(shù)據(jù)在物理內(nèi)存中只有一份,在計(jì)算機(jī)剛剛啟動的時候,就是在加載OS,內(nèi)核級頁表在操作系統(tǒng)中也是只有一份,它映射整個操作系統(tǒng)的代碼和數(shù)據(jù),而且每個進(jìn)程的虛擬內(nèi)存的內(nèi)核空間的內(nèi)容都是一樣的,都是通過內(nèi)核級頁表映射來的。每個進(jìn)程共享內(nèi)核空間,而用戶空間是進(jìn)程獨(dú)有的,所以用戶級頁表有很多個,每個進(jìn)程獨(dú)有一份。
? ? ? ? 每個進(jìn)程需要訪問系統(tǒng)資源,調(diào)用系統(tǒng)接口,就會從用戶態(tài)切換到內(nèi)核態(tài),代碼從用戶區(qū)的代碼跳轉(zhuǎn)到內(nèi)核區(qū)執(zhí)行,當(dāng)內(nèi)核區(qū)的代碼執(zhí)行完,就會返回用戶區(qū),也就會從內(nèi)核態(tài)切回用戶態(tài),也就是這個時候,檢測進(jìn)程的pending信號集。
2. 信號如何被處理
處理流程:
????????如果信號的處理動作不是自定義的,那么就會在第三步處理信號,處理完畢后如果進(jìn)程還活著,就返回用戶模式,并繼續(xù)執(zhí)行
? ? ? ? sighandler和main是兩個獨(dú)立的控制流,使用的是不同的??臻g
在上一篇博客留下了一個問題:
如果調(diào)用sigprocmask解除了對當(dāng)前若干個未決信號的阻塞,則在函數(shù)返回前,至少將其中一個信號遞達(dá)(在下一篇博客中解答為什么)
? ? ? ? 因?yàn)楫?dāng)調(diào)用sigprocmask,也就是系統(tǒng)調(diào)用,就會從用戶態(tài)切換到內(nèi)核態(tài),在函數(shù)返回前,也就是從內(nèi)核態(tài)切回到用戶態(tài)前,會進(jìn)行信號檢測,也就會信號遞達(dá),至少一個是為啥呢?
? ? ? ? 這是因?yàn)榭赡茉谧远x信號處理的過程中發(fā)生狀態(tài)切換,可能是系統(tǒng)調(diào)用,也可能是中斷,都有可能。自定義信號處理是上面圖的第四步,處于用戶態(tài),如果切換成內(nèi)核態(tài)再切回的過程中,就會再一次處理信號。甚至可以一直這樣。
? ? ? ? 但是這種情況,有時候并不是我們想要的,例如一個進(jìn)程一直再給另一個進(jìn)程發(fā)送某個信號,這就可能會出現(xiàn),當(dāng)前自定義的信號還沒處理完,接著又去處理下一個信號。特別是同一個信號,最容易出現(xiàn)這種情況,會死循環(huán)。
? ? ? ? 但是其實(shí)操作系統(tǒng)提供了對應(yīng)的接口設(shè)置,操作系統(tǒng)的設(shè)計(jì)者已經(jīng)想到了
?函數(shù)功能:
? ? ? ? 自定義信號的處理方法,設(shè)置信號屏蔽字,當(dāng)處理該信號時,內(nèi)核會提前把sa_mask加入到block位圖中,默認(rèn)會把當(dāng)前信號加入,確保在自定義函數(shù)處理過程中,不會再處理不想處理的信號,信號處理函數(shù)返回時自動恢復(fù)原來的信號屏蔽字
參數(shù):
? ? ? ? signum:信號編碼
? ? ? ? act:設(shè)置信號處理方法,輸入型參數(shù)
? ? ? ? oldact:舊的信號處理方法會通過oldact傳出,輸出型參數(shù)
返回值:
? ? ? ? 0標(biāo)識成功,-1標(biāo)識失敗
? ? ? ? 在Linux下,當(dāng)子進(jìn)程退出或者暫停時,會向父進(jìn)程發(fā)送SIGCHLD信號,父進(jìn)程對于SIGCHLD信號的默認(rèn)處理動作是忽略,但是我們知道,如果父進(jìn)程不等待回收子進(jìn)程,子進(jìn)程會一直保持僵尸狀態(tài),進(jìn)程PCB會保持下來。
? ? ? ? 但是如果我們想要子進(jìn)程自己處理完,就退出并且釋放自己的資源,父進(jìn)程不關(guān)心子進(jìn)程的退出結(jié)果,要怎么辦呢?
? ? ? ? 可以通過sigaction或者signal修改信號的默認(rèn)處理動作為SIG_IGN,這樣就可以,可見Linux下系統(tǒng)的默認(rèn)處理動作為忽略和自定義處理動作忽略還是有區(qū)別的。
? ? ? ? 當(dāng)然這里我們也可以當(dāng)父進(jìn)程修改信號自定義處理動作為非阻塞等待,來獲取子進(jìn)程退出的結(jié)果,代碼如下:
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <wait.h>using namespace std;void handler(int sig)
{cout << "捕捉到:" << sig << endl;int status = 0;pid_t pid = 0;while((pid = waitpid(-1, &status, WNOHANG)) > 0){cout << "child pid" << pid << endl;cout << "!!!!!!!!!!!!" << endl;if(WIFEXITED(status))cout << "退出碼為:" << WEXITSTATUS(status) << endl;else if(WIFSIGNALED(status))cout << "終止信號:" << WTERMSIG(status) << endl;}
}// 回收子進(jìn)程通過信號
int main()
{struct sigaction act;act.sa_handler = handler;sigaction(SIGCHLD, &act, nullptr);// 4. 自定義SIG_IGNsignal(SIGCHLD, SIG_IGN);if(fork() == 0){cout << "child pid" << getpid() << endl;sleep(5);exit(0);}while(true) sleep(1);return 0;
}
? ? ? ? 完。