自我名言:只有努力,才能追逐夢(mèng)想,只有努力,才不會(huì)欺騙自己。
喜歡的點(diǎn)贊,收藏,關(guān)注一下把!
首先說(shuō)明一點(diǎn)信號(hào)不是信號(hào)量。不能把這兩個(gè)東西放在一起。
那信號(hào)講什么呢?
1.預(yù)備知識(shí)
那信號(hào)是怎么回事,這里只能這樣說(shuō),信號(hào)是針對(duì)進(jìn)行發(fā)送某種信號(hào)到來(lái)的一種機(jī)制,讓信號(hào)能被進(jìn)程處理。,讓我們?cè)诤竺娴闹R(shí)中,更能理解這句話的含義。
先見(jiàn)識(shí)見(jiàn)識(shí)信號(hào)。
前面數(shù)字是信號(hào)的編號(hào),后面大寫(xiě)的是宏。
就比如殺死一個(gè)進(jìn)程
kill -9 進(jìn)程pid
這里可以使用編號(hào),也可以使用SIGKILL
再可以數(shù)一數(shù)信號(hào)有多少個(gè)。
其實(shí)并沒(méi)有64個(gè),0,32,33信號(hào)是沒(méi)有的。
【1,31】普通信號(hào)
【34,64】實(shí)時(shí)信號(hào) (我們不學(xué)這個(gè))
先說(shuō)信號(hào)的概念幫我們簡(jiǎn)單了解一下信號(hào),但再說(shuō)信號(hào)一些概念之前,我們先從生活角度中的信號(hào)來(lái)幫我們理解。
生活中的信號(hào):
1.紅綠燈
2.鬧鐘
3.信息通知
4.勞資蜀道三
5.女朋友把你拉黑
6.烽火臺(tái)狼煙
等等這些都是我們生活中的信號(hào)。
我們以紅綠燈為例。人是能夠識(shí)別紅綠燈的。
這里識(shí)別有兩層意思。
第一個(gè)問(wèn)題可能會(huì)覺(jué)得很奇怪,你為什么能夠識(shí)別紅綠燈?
第二個(gè)問(wèn)題,當(dāng)信號(hào)來(lái)的時(shí)候,你不一定會(huì)立即處理這個(gè)信號(hào)
信號(hào)的產(chǎn)生是異步的。
舉個(gè)栗子,你正在宿舍打著游戲,這時(shí)外賣(mài)小哥給你打電話讓你下樓取餐,但是你忙著打著游戲并沒(méi)有立刻下樓去取,而是讓他把外賣(mài)放在樓下。當(dāng)你打完游戲,記起還有外賣(mài)沒(méi)拿,所以去樓下拿外賣(mài)。當(dāng)然還有另一種情況,你打游戲上頭了。然后忘記有外賣(mài)在樓下這回事。
當(dāng)綠燈到達(dá)的時(shí)候,你有三種處理動(dòng)作
如何把上面的這些概念遷移到進(jìn)程中呢?
這里要有一個(gè)共識(shí):信號(hào)是給進(jìn)程發(fā)的
-
進(jìn)程是如何識(shí)別信號(hào)的?(認(rèn)識(shí)+動(dòng)作)
進(jìn)程本身就是程序員編寫(xiě)的屬性和邏輯的集合。所以這里先粗略的說(shuō)是由程序員編碼完成的。(后面學(xué)了信號(hào)更多知識(shí)就可以詳細(xì)說(shuō)明了)。 -
當(dāng)進(jìn)程收到信號(hào)的時(shí)候,進(jìn)程可能正在執(zhí)行更重要的代碼,所以信號(hào)不一定會(huì)被立即處理
-
進(jìn)程本身必須要有對(duì)于信號(hào)的保存能力
-
進(jìn)程在處理信號(hào)的時(shí)候,一般有三種動(dòng)作(默認(rèn),自定義,忽略)【信號(hào)被捕捉】
在我們現(xiàn)在還沒(méi)有學(xué)過(guò)信號(hào),上面1,2,4我們都不能具體解釋?zhuān)贿^(guò)3我們可以根據(jù)以往學(xué)過(guò)的知識(shí)來(lái)分析分析。
如果一個(gè)信號(hào)是發(fā)給進(jìn)程的,而進(jìn)程要保存,那么應(yīng)該保存在哪里?
task_struct(PCB)結(jié)構(gòu)體中。
如何保存呢? 更準(zhǔn)確來(lái)說(shuō)如何保存是否收到了指定信號(hào)【1,31】。
是否是一種兩態(tài),我們是不是可以在task_struct結(jié)構(gòu)體里,當(dāng)然task_struct結(jié)構(gòu)體中包含其他一大堆的屬性,可以存在一個(gè)unsigned int signal32位比特位。
所以在進(jìn)程中是不是只要存在對(duì)應(yīng)的位圖結(jié)構(gòu),然后當(dāng)我們收到信號(hào)時(shí),是不是只要將對(duì)應(yīng)信號(hào)的位置由0->1,就代表我們已經(jīng)完成了信號(hào)的發(fā)送,并且讓進(jìn)程暫時(shí)把這個(gè)信號(hào)保存起來(lái)了。
那如何理解信號(hào)的發(fā)送呢?
發(fā)送信號(hào)的本質(zhì):修改PCB的信號(hào)位圖!
PCB是內(nèi)核維護(hù)的數(shù)據(jù)結(jié)構(gòu)對(duì)象----->PCB的管理者是OS,誰(shuí)有權(quán)力修改PCB中的內(nèi)容呢?------>OS!!
所以無(wú)論未來(lái)我們學(xué)習(xí)多少種發(fā)送信號(hào)的方式,本質(zhì)都是通過(guò)OS向目標(biāo)進(jìn)程發(fā)送的信號(hào)!!
未來(lái)想讓用戶(hù)也能發(fā)送信號(hào)------->OS必須要提供發(fā)送信號(hào),處理信號(hào)的相關(guān)系統(tǒng)調(diào)用!
我們使用kill命令,底層一定調(diào)用了對(duì)應(yīng)的系統(tǒng)調(diào)用!
2.信號(hào)產(chǎn)生
2.1通過(guò)鍵盤(pán)發(fā)送信號(hào)
int main()
{
int cnt=0;
while(true)
{
printf("我是一個(gè)進(jìn)程,我正在運(yùn)行%d\n",cnt++);
sleep(1);
}
return 0;
}
ctrl+c熱鍵,終止前臺(tái)進(jìn)程。
本質(zhì)ctrl+c是一個(gè)組合鍵---->OS識(shí)別---->OS將ctrl+c解釋成為2號(hào)信號(hào),2)SIGINT----->處理(三種動(dòng)作)。但我們對(duì)2號(hào)信號(hào)沒(méi)做任何改變,所以是默認(rèn)處理。
man 7 signal //查看信號(hào)對(duì)應(yīng)的手冊(cè)
Action(行為):Term (Terminal終端)結(jié)束進(jìn)程
Comment(解釋):從鍵盤(pán)中斷
所以2號(hào)信息的默認(rèn)動(dòng)作,結(jié)束進(jìn)程。
接下來(lái)驗(yàn)證一下是不是發(fā)送了2號(hào)信號(hào)。
先介紹一個(gè)函數(shù)signal,對(duì)指定的信號(hào)設(shè)置一個(gè)自定義動(dòng)作。
signum:信號(hào)編號(hào)(捕捉那個(gè)信號(hào))
handler:函數(shù)指針(捕捉這個(gè)信號(hào)后,你想怎么做,這是一個(gè)回調(diào)函數(shù))
接下來(lái)驗(yàn)證
#include<iostream>
#include<unistd.h>
#include<cstdio>
#include<signal.h>
void handler(int signo)
{
cout<<"捕捉到信號(hào):"<<signo<<endl;
}
int main()
{
signal(2,handler);
int cnt=0;
while(true)
{
printf("我是一個(gè)進(jìn)程,我正在運(yùn)行%d\n",cnt++);
sleep(1);
}
return 0;
}
我不是對(duì)2號(hào)信號(hào)進(jìn)行捕捉嗎,并且代碼還做了修改,為什么運(yùn)行結(jié)果沒(méi)什么變化。
注意,這里是signal函數(shù)的調(diào)用,并不是handler的調(diào)用,并且僅僅是設(shè)置了對(duì)信號(hào)的捕捉方法,并不代表方法被調(diào)用了。所以一般這個(gè)方法不會(huì)執(zhí)行,除非收到對(duì)應(yīng)的信號(hào)。
這兩種方法都可以發(fā)送信號(hào)。然后signal函數(shù)對(duì)2號(hào)信息進(jìn)行捕捉。
現(xiàn)在發(fā)2號(hào)信號(hào),雖然能被捕捉,但是進(jìn)程怎么退不出來(lái)了。
這是因?yàn)槲覀儼?號(hào)信號(hào)默認(rèn)動(dòng)作,改成了自定義動(dòng)作。
如果想退出怎么辦?
kill -9 編號(hào) //殺死進(jìn)程
或者在自定義動(dòng)作種加一個(gè)exit。
void handler(int signo)
{
cout<<"進(jìn)程捕捉到了一個(gè)信號(hào),信號(hào)編號(hào)是:"<<signo<<endl;
exit(0);
}
其實(shí)還有一個(gè)組合建ctrl+\,發(fā)送的是3號(hào)信號(hào)。也能終止進(jìn)程。
這里留了一個(gè)問(wèn)題,Core和Trem都是終止進(jìn)程。為什么OS要設(shè)置兩種不同的行為有什么用?
2.2系統(tǒng)調(diào)用接口向進(jìn)程發(fā)送信號(hào)
kill可以給任意進(jìn)程發(fā)送任意信號(hào)。
pid:目標(biāo)進(jìn)程pid
sig:發(fā)送幾號(hào)信號(hào)
成功返回0,識(shí)別返回-1。
我們給進(jìn)程發(fā)信號(hào)底層用的就是這個(gè)。
前面說(shuō)過(guò),信號(hào)是由OS向進(jìn)程發(fā)送的。OS有這個(gè)能力,但不代表有權(quán)限使用這個(gè)能力。
信號(hào)的發(fā)送是由用戶(hù)發(fā)起而OS執(zhí)行的。
接下來(lái)我們寫(xiě)的代碼想呈現(xiàn)這樣的效果,一個(gè)進(jìn)程正在運(yùn)行,另一個(gè)進(jìn)程在命令行給這個(gè)進(jìn)程發(fā)送任意信號(hào)。
//mysignal.cc
#include<iostream>
#include<unistd.h>
#include<cstdio>
#include<signal.h>
#include<sys/types.h>
#include<string>
using namespace std;
void Usage(const string& proc)
{
cout<<"\nUsage "<<proc<<" pid signo\n"<<endl;
}
// ./mysignal pid signo------>命令行參數(shù)
int main(int argc,char* argv[])
{
if(argc != 3)
{
Usage(argv[0]);
exit(1);
}
pid_t id=stoi(argv[1]);
int signo=stoi(argv[2]);
int n=kill(id,signo);
if(n != 0)
{
perror("kill");
}
return 0;
}
//mytest.cc
#include<iostream>
#include<unistd.h>
int main()
{
int cnt=0;
while(true)
{
printf("我是一個(gè)進(jìn)程,pid:%d,我正在運(yùn)行%d\n",getpid(),cnt++);
sleep(1);
}
return 0;
}
這個(gè)不就是和我們?cè)诿钚袌?zhí)行kill命令一樣的原理嗎。
kill(),可以向任意進(jìn)程發(fā)送任意信號(hào)
raise,給自己發(fā)送任意信號(hào)。----->就相當(dāng)于 kill(getpid(),任意信號(hào))
int main()
{
int cnt=0;
while(true)
{
printf("cnt:%d,pid:%d\n",cnt++,getpid());
if(cnt == 10)
raise(9);
sleep(1);
}
return 0;
}
abort,給自己發(fā)送指定的信號(hào)(6號(hào)信號(hào))。------>相當(dāng)于kill(getpid(),SIGABRT)
int main()
{
int cnt=0;
while(true)
{
printf("cnt:%d,pid:%d\n",cnt++,getpid());
if(cnt == 10)
abort();
sleep(1);
}
return 0;
}
關(guān)于信號(hào)處理的行為的理解:有很多的情況,進(jìn)程收到大部分的信號(hào),默認(rèn)處理動(dòng)作都是終止進(jìn)程。
既然大部分信號(hào)默認(rèn)都是終止進(jìn)程,那有那么多類(lèi)的信號(hào)有什么用?
信號(hào)的意義:信號(hào)的不同,代表不同的事情,但是對(duì)事情發(fā)生之后的處理可以一樣。
也就是說(shuō)進(jìn)程意外終止了,我們可以根據(jù)信號(hào)不同來(lái)確定是什么原因?qū)е碌摹?/p>
2.3硬件異常產(chǎn)生信號(hào)
信號(hào)產(chǎn)生,不一定非得是用戶(hù)顯示發(fā)送的。
看下面一段代碼
int main()
{
int cnt=0;
while(true)
{
printf("cnt:%d,pid:%d\n",cnt++,getpid());
int a=10;
a/=0;
}
return 0;
}
為什么除0會(huì)終止進(jìn)程?
因?yàn)楫?dāng)前進(jìn)程會(huì)收到來(lái)自O(shè)S發(fā)送的信號(hào)。SIGPFE。
如何證明呢?
void handler(int signo)
{
cout<<"進(jìn)程捕捉到了一個(gè)信號(hào),信號(hào)編號(hào)是:"<<signo<<endl;
}
int main(int argc,char* argv[])
{
signal(SIGFPE,handler);
int cnt=0;
while(true)
{
printf("cnt:%d,pid:%d\n",cnt++,getpid());
int a=10;
a/=0;
}
return 0;
}
我確實(shí)捕捉到了8號(hào)信號(hào),但為什么OS一直發(fā)送信號(hào)呢?
難道是我這里一直在死循環(huán)的原因?
修改一下代碼
int main()
{
signal(SIGFPE,handler);
int cnt=0;
int a=10;
a/=0;
while(true)
{
printf("cnt:%d,pid:%d\n",cnt++,getpid());
}
return 0;
}
發(fā)現(xiàn)還是一直在發(fā)送8號(hào)信號(hào)。
這到底是為什么?
先來(lái)解答,OS如何得知應(yīng)該給當(dāng)前進(jìn)程發(fā)送8號(hào)信號(hào)呢?或OS怎么知道我除0了呢?
CPU運(yùn)算異常了,OS會(huì)不會(huì)知道?
OS肯定會(huì)知道CPU運(yùn)算出現(xiàn)了問(wèn)題,因?yàn)镺S是軟硬件資源的管理者。
OS查看到狀態(tài)寄存器溢出位由0->1,OS就識(shí)別到CPU內(nèi)部出錯(cuò)了。
誰(shuí)導(dǎo)致CPU出錯(cuò)了?
CPU當(dāng)前正在調(diào)度誰(shuí),就是那個(gè)進(jìn)程出現(xiàn)了問(wèn)題,OS向目標(biāo)進(jìn)程發(fā)送8號(hào)信息,目標(biāo)進(jìn)程收到8號(hào)信號(hào),后序處理就會(huì)終止自己了。
那為什么一直發(fā)信息呢?
收到信號(hào),不一定會(huì)引起進(jìn)程退出,沒(méi)有退出,進(jìn)程可能還會(huì)被CPU調(diào)度。
CPU內(nèi)部的寄存器只有一份,但是寄存器種中的內(nèi)容,屬于當(dāng)前進(jìn)程的上下文,CPU內(nèi)部狀態(tài)寄存器溢出標(biāo)記位由0->1,你是沒(méi)有能力或者動(dòng)作去修改這個(gè)問(wèn)題的。
當(dāng)進(jìn)程被切換的時(shí)候,就有無(wú)數(shù)次狀態(tài)寄存器被保存和恢復(fù)的過(guò)程,所以每一次恢復(fù)的時(shí)候,就讓OS識(shí)別到了CPU內(nèi)部的狀態(tài)寄存器中標(biāo)記位是1,每一次都會(huì)發(fā)8號(hào)信號(hào)。
再看一種由硬件異常產(chǎn)生的信號(hào)。
int main()
{
signal(SIGFPE,handler);
int cnt=0;
while(true)
{
printf("cnt:%d,pid:%d\n",cnt++,getpid());
int* ptr=NULL;
*ptr=10;
}
return 0;
}
為什么野指針就奔潰了?
因?yàn)镺S會(huì)給當(dāng)前進(jìn)程發(fā)送指定的11號(hào)信號(hào)。
證明一下。
void handler(int signo)
{
cout<<"進(jìn)程捕捉到了一個(gè)信號(hào),信號(hào)編號(hào)是:"<<signo<<endl;
}
int main()
{
signal(11,handler);
int cnt=0;
while(true)
{
printf("cnt:%d,pid:%d\n",cnt++,getpid());
int* ptr=NULL;
*ptr=10;
}
return 0;
}
OS怎么知道我野指針了呢?
根據(jù)我們以前學(xué)的知識(shí),虛擬地址—>物理地址的轉(zhuǎn)換,要經(jīng)過(guò)頁(yè)表。今天我要告訴你除了頁(yè)表還有一種硬件MMU。MMU是內(nèi)存管理單元。
MMU其實(shí)是通過(guò)讀取頁(yè)表中的內(nèi)容,在內(nèi)部形成對(duì)應(yīng)的物理地址,然后再去訪問(wèn)我們對(duì)應(yīng)的物理地址。
當(dāng)我們ptr解引用,訪問(wèn)的是0號(hào)地址。經(jīng)過(guò)頁(yè)表映射,發(fā)現(xiàn)在映射的時(shí)候,當(dāng)前進(jìn)程是不允許去訪問(wèn)對(duì)應(yīng)的0號(hào)地址的。不允許訪問(wèn)當(dāng)然可以攔截不讓你訪問(wèn)。但更重要的是,你為什么會(huì)訪問(wèn),所以O(shè)S覺(jué)得你犯錯(cuò)了就應(yīng)該付出相應(yīng)的代價(jià),所以MMU這個(gè)硬件因?yàn)閷?duì)應(yīng)的越界訪問(wèn)(野指針訪問(wèn))發(fā)送異常。OS知道當(dāng)前硬件發(fā)生異常,所以O(shè)S將異常轉(zhuǎn)換成11號(hào)信號(hào)發(fā)送給目標(biāo)進(jìn)程。
2.4軟件條件
在管道我們說(shuō)過(guò)匿名管道的一個(gè)場(chǎng)景,讀端關(guān)閉,寫(xiě)端一直寫(xiě)沒(méi)有任何意義,OS會(huì)給當(dāng)前寫(xiě)進(jìn)程發(fā)送SIGPIPE信號(hào),然后進(jìn)程終止了。
所謂的進(jìn)程,OS,管道,尤其是管道和這一整套OS發(fā)信號(hào)的原因和OS發(fā)信號(hào)的過(guò)程,和硬件都沒(méi)有關(guān)系。而是僅僅因?yàn)樽x端關(guān)閉了這一軟件條件所觸發(fā)的OS發(fā)送信號(hào)給目標(biāo)進(jìn)程,這種場(chǎng)景我們就稱(chēng)之為軟件條件會(huì)觸發(fā)信號(hào)。
下面我們要說(shuō)的是一種定時(shí)器軟件條件。給當(dāng)前進(jìn)程設(shè)定鬧鐘,alarm()。
設(shè)置一個(gè)時(shí)鐘時(shí)刻發(fā)送信號(hào)。
seconds:多少秒之后發(fā)送信號(hào)
返回值是0或者是以前設(shè)定的鬧鐘時(shí)間還余下的秒數(shù)
發(fā)送的是SIGALRM(14)信號(hào)。
int main()
{
//這個(gè)鬧鐘是給現(xiàn)在設(shè)的還是給未來(lái)設(shè)的?
//是不是我調(diào)用了alarm,我的進(jìn)程會(huì)立馬收到對(duì)應(yīng)的鬧鐘呢?
//答案:并不是。這是給未來(lái)設(shè)置的鬧鐘。是1秒之后向我這個(gè)進(jìn)程發(fā)信號(hào)。
alarm(1);
int cnt=0;
while(true)
{
printf("cnt: %d\n",cnt++);
}
return 0;
}
根據(jù)運(yùn)行結(jié)果,請(qǐng)問(wèn)我們這段代碼有什么用呢?
其實(shí)這是統(tǒng)計(jì)1S左右,我們計(jì)算機(jī)能夠?qū)?shù)據(jù)累計(jì)多次次。
修改一下代碼再看一下效果。
int cnt=0;
void catchSig(int signo)
{
cout<<"進(jìn)程捕捉到了一個(gè)信號(hào),信號(hào)編號(hào)是:"<<signo<<" cnt :"<<cnt<<endl;
}
int main()
{
signal(SIGALRM,catchSig);
alarm(1);
while(true)
{
cnt++;
}
return 0;
}
次數(shù)多了很多次,這是因?yàn)閜rintf會(huì)訪問(wèn)外設(shè),而訪問(wèn)外設(shè)比較慢。
還有就是這個(gè)鬧鐘是一次性鬧鐘,響了之后就不響了。
如果想響多次,要重新在設(shè)定鬧鐘。
void catchSig(int signo)
{
cout<<"進(jìn)程捕捉到了一個(gè)信號(hào),信號(hào)編號(hào)是:"<<signo<<" cnt :"<<cnt<<endl;
alarm(1);
}
alarm(0),取消鬧鐘,并且返回鬧鐘剩下多少時(shí)間。
int main(int argc,char* argv[])
{
signal(SIGALRM,catchSig);
alarm(5);
while(true)
{
cnt++;
if(cnt == 3)
{
int n=alarm(0);
cout<<n<<endl;
}
sleep(1);
}
return 0;
}
為什么設(shè)鬧鐘就是軟件條件了呢?
"鬧鐘"其實(shí)就是用軟件條件實(shí)現(xiàn)的。
2.5總結(jié)
1.上面所說(shuō)的所有信號(hào)產(chǎn)生,最終都要有OS來(lái)進(jìn)行執(zhí)行,為什么?
OS是進(jìn)程的管理者
- 信號(hào)的處理是否是立即處理的?
在合適的時(shí)候(什么合適的時(shí)候,下面說(shuō))。
3.信號(hào)如果不是被立即處理,那么信號(hào)是否需要暫時(shí)被進(jìn)程記錄下來(lái)?記錄在哪里最合適呢?
4.一個(gè)進(jìn)程在沒(méi)有收到信號(hào)的時(shí)候,能否能知道,自己應(yīng)該對(duì)合法信號(hào)作何處理呢?
5.如何理解OS向進(jìn)程發(fā)送信號(hào)?能否描述一下完整的發(fā)送處理過(guò)程?
上面的問(wèn)題,我們都可以從接下來(lái)信號(hào)的學(xué)習(xí)中得到答案。
信號(hào)產(chǎn)生這里還有最后一個(gè)問(wèn)題。
man 7 signal //信號(hào)手冊(cè)
Stop暫停進(jìn)程,Cont繼續(xù)進(jìn)程,Ign忽略進(jìn)程(這個(gè)信號(hào)說(shuō)完最后面解釋)這些都沒(méi)有問(wèn)題。
Term,Core都是終止進(jìn)程,有什么區(qū)別?
其實(shí)這有關(guān)于,進(jìn)程退出時(shí),核心轉(zhuǎn)儲(chǔ)問(wèn)題。
看下面一段代碼
int main()
{
//核心轉(zhuǎn)儲(chǔ)
while(true)
{
int a[10];
a[100]=10;
}
return 0;
}
int main()
{
//核心轉(zhuǎn)儲(chǔ)
while(true)
{
int a[10];
a[1000]=10;
}
return 0;
}
數(shù)組明明都越界了啊,怎么進(jìn)程沒(méi)有奔潰報(bào)錯(cuò)?
其實(shí)在C的時(shí)候就說(shuō)過(guò),數(shù)組越界不一定會(huì)報(bào)錯(cuò),因?yàn)閷?duì)數(shù)組的檢查是隨機(jī)的。這是我們?cè)谡Z(yǔ)言層面的理解。
int main()
{
//核心轉(zhuǎn)儲(chǔ)
while(true)
{
int a[10];
a[10000]=10;
}
return 0;
}
那這次怎么就檢測(cè)出來(lái)了。按照語(yǔ)言層面解釋可能是因?yàn)檫@次越界被檢測(cè)到了。
接下來(lái)我們從底層理解:
編譯器上編譯你的代碼時(shí),在棧上給你開(kāi)辟多大空間和編譯器是強(qiáng)相關(guān)的,你要申請(qǐng)10個(gè)int大小元素的數(shù)組,它確實(shí)給你的就是10個(gè)元素,指的是數(shù)組的元素,但是并不代表給你的代碼塊或者函數(shù)分配棧幀結(jié)構(gòu)是10個(gè)元素的大小,可能給你的會(huì)很大,所以呢,即便你越界了,但是你還是在有效棧區(qū)里,所以沒(méi)有報(bào)錯(cuò),除非你訪問(wèn)了一個(gè)完全不是你的空間。比如你現(xiàn)在訪問(wèn)的時(shí)候,訪問(wèn)的是系統(tǒng)的地址空間中或者訪問(wèn)到一個(gè)不讓你訪問(wèn)的區(qū)域,那么此時(shí)OS系統(tǒng)就能識(shí)別出來(lái)。所以O(shè)S在識(shí)別越界的問(wèn)題上有可能也死別不出來(lái),從而出現(xiàn)把數(shù)據(jù)改變了,但用戶(hù)不知情的情況。
這個(gè)信號(hào)是11號(hào)信號(hào),段錯(cuò)誤,它的終止方式是Core。
像Trem這種結(jié)束,是正常結(jié)束,OS不會(huì)做額外操作的。而以Core這種結(jié)束,OS除了終止進(jìn)程,它還要做其他工作。
但是以Core為終止,我也沒(méi)見(jiàn)OS做什么額外工作啊, 除了給我打印出一個(gè)錯(cuò)誤描述,像Trem終止進(jìn)程不也是給我打印出一個(gè)錯(cuò)誤描述嗎。
在云服務(wù)器上,默認(rèn)如果進(jìn)程是Core退出的,我們暫時(shí)看不到明顯現(xiàn)象,如果想看到需要打開(kāi)一個(gè)選項(xiàng)。
ulimit -a //可以看到系統(tǒng)給我們當(dāng)前資源設(shè)置的上限
core file size 大小為0,這是云服務(wù)器默認(rèn)關(guān)閉了core file選項(xiàng)。
想要打開(kāi),ulimit就帶上你想要設(shè)置誰(shuí),-c(選項(xiàng)),大小為多少。
ulimit -c 1024 //打開(kāi)云服務(wù)器core file選項(xiàng),默認(rèn)可以向OS中形成最大為1024個(gè)block的數(shù)據(jù)塊
然后運(yùn)行同樣的代碼
相比較我們之前運(yùn)行的時(shí)候,除了段錯(cuò)誤后面還跟了一個(gè)(core dumped)
發(fā)現(xiàn)我們當(dāng)前目錄下多了一個(gè)以core命名的文件。
所謂的核心轉(zhuǎn)儲(chǔ):當(dāng)進(jìn)程出現(xiàn)異常的時(shí)候,我們將進(jìn)程在對(duì)應(yīng)的時(shí)刻,在內(nèi)存中的有效數(shù)據(jù)(二進(jìn)制數(shù)據(jù))轉(zhuǎn)儲(chǔ)到磁盤(pán)中。
該文件我們用vim打開(kāi)是一堆亂碼。我們是無(wú)法識(shí)別的。
那形成核心轉(zhuǎn)儲(chǔ)有什么意義呢?或者說(shuō)為什么要有核心轉(zhuǎn)儲(chǔ)?
一般進(jìn)程在運(yùn)行的時(shí)候出現(xiàn)崩潰,其實(shí)我們更想知道的是,為什么會(huì)崩潰,在哪里崩潰。所以O(shè)S為了便于我們后期做調(diào)試,會(huì)將進(jìn)程在運(yùn)行期間出現(xiàn)崩潰的代碼的相關(guān)上下文數(shù)據(jù)全部dump到磁盤(pán)中,用來(lái)進(jìn)行支持調(diào)試。
如何支持呢?
linux下默認(rèn)編譯都是release不能調(diào)試,debug才能調(diào)試,因此我們編譯時(shí)帶上-g選項(xiàng)。
當(dāng)前自動(dòng)幫我們?cè)u(píng)判,進(jìn)程收到11號(hào)信號(hào)引起的段錯(cuò)誤,報(bào)錯(cuò)是在mysignal.cc的第37行,代碼是a[10000]=10引起的錯(cuò)誤。直接就幫我們找到了錯(cuò)誤。
這種直接快速定位到出問(wèn)題的方式,我們稱(chēng)之為事后調(diào)試。
像這種以2號(hào)信號(hào),Trem終止進(jìn)程,并不會(huì)在當(dāng)前目錄下形成core文件。
Trem,Core都是進(jìn)程終止,它們的區(qū)別是,以Core退出的可以被核心轉(zhuǎn)儲(chǔ)的以便于后序快遞定位問(wèn)題,以Trem退出就是正常終止進(jìn)程。
以后進(jìn)程出現(xiàn)異常退出,你可以查看是什么信號(hào)的什么行為導(dǎo)致的,如果是Core,把Core打開(kāi),再執(zhí)行一下,gdb快速定位問(wèn)題。
信號(hào)產(chǎn)生到目前為止差不多講完了,但這里可能有人會(huì)有這樣的疑問(wèn)。如果我們把所有信號(hào)都捕捉,換成自定義動(dòng)作,不讓進(jìn)程退出,那進(jìn)程是不是無(wú)法被殺死了。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-751876.html
void catchSig(int signo)
{
cout<<"進(jìn)程捕捉到了一個(gè)信號(hào),信號(hào)編號(hào)是:"<<signo<<endl;
}
int main()
{
for(int signo=1;signo<=31;++signo)
{
signal(signo,catchSig);
}
while(true) sleep(1);
return 0;
}
難道真的無(wú)法殺死了?
kill -9還是可以殺死進(jìn)程,無(wú)論你怎么修改,無(wú)法對(duì)9號(hào)信號(hào)設(shè)定捕捉,即使你做了,OS也不會(huì)給你設(shè)置。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-751876.html
到了這里,關(guān)于【linux】信號(hào)——信號(hào)產(chǎn)生的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!