平臺(tái)網(wǎng)站建設(shè)協(xié)議中國做網(wǎng)站的公司排名
🐱作者:一只大喵咪1201
🐱專欄:《Linux學(xué)習(xí)》
🔥格言:你只管努力,剩下的交給時(shí)間!
信號(hào)
- 🔔信號(hào)
- 🎵預(yù)備知識(shí)
- 🎵信號(hào)處理方法的注冊(cè)
- 🔔信號(hào)的產(chǎn)生
- 🎵通過終端按鍵產(chǎn)生信號(hào)
- 🎵調(diào)用系統(tǒng)調(diào)用向進(jìn)程發(fā)信號(hào)
- 🎵硬件異常產(chǎn)生信號(hào)
- 🎵由軟件條件產(chǎn)生信號(hào)
- 🔔核心轉(zhuǎn)儲(chǔ)
🔔信號(hào)
從生活中入手,例如發(fā)令槍,鬧鐘,紅綠燈等等,這些都是信號(hào)。信號(hào)必須都是動(dòng)態(tài)的,像路標(biāo)就不能稱之為信號(hào)。
以紅綠燈為例,一看到紅綠燈我們就知道紅燈行,綠燈停,我們不僅能認(rèn)識(shí)它是一個(gè)紅綠燈,而且還知道應(yīng)該產(chǎn)生什么樣的行為,這樣才算是能夠識(shí)別紅綠燈。
- 識(shí)別 = 認(rèn)識(shí) + 行為產(chǎn)生
對(duì)于紅綠等這個(gè)信號(hào),我們需要有如下幾個(gè)共識(shí):
- 我們之所以能識(shí)別紅綠燈,是因?yàn)槲覀兪艿竭^教育(手段),讓我們?cè)诖竽X中記住了不同顏色對(duì)應(yīng)的行為(屬性)。
- 當(dāng)綠燈亮了以后,不一定要立刻過馬路,比如有其他的車闖紅燈,需要進(jìn)行避讓,所以說我們不一定要立刻產(chǎn)生相應(yīng)的行為。
- 紅燈亮了以后,正好來了一個(gè)電話,在接電話這個(gè)期間我們會(huì)記住此時(shí)是紅燈,不會(huì)將這個(gè)狀態(tài)忘記。
- 紅綠燈默認(rèn)的行為是紅燈行,綠燈停,但是也可以產(chǎn)生其他行為,還可以忽略。
現(xiàn)在將生活中紅綠燈的例子遷移到進(jìn)程中:
- 共識(shí):信號(hào)是發(fā)給進(jìn)程的。
- 進(jìn)程之所以能夠識(shí)別信號(hào),是因?yàn)槌绦騿T將對(duì)應(yīng)的信號(hào)種類和邏輯已經(jīng)寫好了的。
- 當(dāng)信號(hào)發(fā)給進(jìn)程后,進(jìn)程不一定要立刻去處理,可能有更加緊急的任務(wù),會(huì)在合適的時(shí)候去處理。
- 進(jìn)程收到信號(hào)到處理信號(hào)之前會(huì)有一個(gè)窗口期,這個(gè)期間要將收到的信號(hào)進(jìn)行保存。
- 處理信號(hào)的方式有三種:默認(rèn)動(dòng)作,自定義動(dòng)作,忽略。
我們學(xué)習(xí)信號(hào)是學(xué)習(xí)它的整個(gè)生命周期,按照時(shí)間軸,分為信號(hào)產(chǎn)生,信號(hào)保存,信號(hào)處理。但是在這之前先需要學(xué)習(xí)一些預(yù)備知識(shí)。
🎵預(yù)備知識(shí)
進(jìn)程能夠識(shí)別的信號(hào)是已經(jīng)寫好的,它有62個(gè):
- 紅色框中的是普通信號(hào),編號(hào)從1-31。
- 綠色框中的是實(shí)時(shí)信號(hào),編號(hào)從34-64。
這其中沒有32號(hào)和33號(hào)信號(hào),所以一共有62個(gè)信號(hào)。而且這里我們只學(xué)習(xí)普通信號(hào),對(duì)實(shí)時(shí)信號(hào)暫不做研究。
- 在使用這些信號(hào)時(shí),可以用信號(hào)名,也可以用信號(hào)編號(hào),它是一樣的,都是宏定義后的結(jié)果。
根據(jù)我們對(duì)Linux的了解,信號(hào)存放在哪里呢?既然信號(hào)是給進(jìn)程的,而進(jìn)程是通過內(nèi)核數(shù)據(jù)結(jié)構(gòu)來管理的,所以我們可以推斷出,信號(hào)放在進(jìn)程的task_struct結(jié)構(gòu)體中。
既然它是在PCB中,而且數(shù)量是31個(gè),task_struct中必定不會(huì)設(shè)置31個(gè)變量來存放信號(hào),數(shù)組還有可能,但是信號(hào)的狀態(tài)只分為有和沒有兩種,所以再次推斷,31個(gè)信號(hào)放在一個(gè)32位的整形變量中,每個(gè)比特位代表一個(gè)信號(hào)。
本喵寫一段偽代碼來示意一下:
struct task_struct
{//進(jìn)程屬性unsigned int signal;//.......
}
就像在學(xué)習(xí)基礎(chǔ)IO和進(jìn)程間通信的時(shí)候,那些flags標(biāo)志中的不同的比特位代表著不同的意義,這31個(gè)信號(hào)量也是這種方式:
具體的保存細(xì)節(jié)后面本喵再詳細(xì)講解。
問題來了,內(nèi)核數(shù)據(jù)結(jié)構(gòu)的修改,這個(gè)工作是由誰來完成的?毫無疑問是操作系統(tǒng),因?yàn)閠ask_struct就是它維護(hù)的,而且是存在于內(nèi)存中的,只有操作系統(tǒng)才有權(quán)力去修改它,用戶是無法直接操作的,因?yàn)椴僮飨到y(tǒng)不相信任何人。
所以說,無論哪個(gè)信號(hào),最后的本質(zhì)都是由操作系統(tǒng)發(fā)生給進(jìn)程的,這里的發(fā)送本質(zhì)就是在修改task_struct中存放信號(hào)那個(gè)變量的比特位。
- 信號(hào)發(fā)送的本質(zhì)就是在修改PCB中的信號(hào)位圖。
無論未來我們學(xué)習(xí)了多少中發(fā)送信號(hào)的方式,本質(zhì)都是通過操作系統(tǒng)向目標(biāo)進(jìn)程發(fā)送信號(hào)。
所以操作系統(tǒng)一定會(huì)提供相關(guān)的系統(tǒng)調(diào)用,比如我們之前使用過的各自信號(hào):
kill -9 pid值 //停止某個(gè)進(jìn)程
kill -19 pid值 //暫停某個(gè)進(jìn)程
kill -18 pid值 //繼續(xù)某個(gè)進(jìn)程
它們的底層一定是在調(diào)用相關(guān)的系統(tǒng)調(diào)用,來讓操作系統(tǒng)修改PCB中的信號(hào)位圖。
🎵信號(hào)處理方法的注冊(cè)
- 所謂的注冊(cè),就是告訴操作系統(tǒng),當(dāng)某個(gè)進(jìn)程接收到某個(gè)信號(hào)后的處理方式。
既然是告訴操作系統(tǒng),那么肯定會(huì)用到系統(tǒng)調(diào)用,該系統(tǒng)調(diào)用的名字是signal():
- int signal:要注冊(cè)的信號(hào)編號(hào)
- sighandler_t handler:自定義的函數(shù)指針
可以將信號(hào)的處理方式寫成一個(gè)函數(shù),然后將函數(shù)名傳遞個(gè)signal,此時(shí)當(dāng)進(jìn)程接收到signum指定的信號(hào)編號(hào)時(shí),就會(huì)執(zhí)行我們定義的函數(shù)。
void handler(int signo)
{cout<<"進(jìn)程接收到的一個(gè)信號(hào),編號(hào):"<<signo<<endl;
}
函數(shù)的返回類型必須是void類型,形參必須是signo類型,名字可以隨意。
補(bǔ)充:
我們之前,在shell的命令行中,會(huì)按crtl + c 來結(jié)束正在運(yùn)行的進(jìn)程。這種鍵我們稱為熱鍵。
ctrl + c本質(zhì)是上一個(gè)組合鍵,操作系統(tǒng)識(shí)別到這種組合鍵后,會(huì)將它解釋為2號(hào)信號(hào)SIGINT。
現(xiàn)在將我們自己寫的函數(shù)注冊(cè)為2號(hào)信號(hào)的處理方式。
運(yùn)行起來后發(fā)現(xiàn),按上ctrl+c后,進(jìn)程不會(huì)結(jié)束了。
- 2號(hào)信號(hào)SIGINT的默認(rèn)處理方式就是結(jié)束進(jìn)程。
- 我們自定義的處理方式中并沒有結(jié)束進(jìn)程,所以進(jìn)程在收到2號(hào)進(jìn)程后打印了一句話。
- 所有信號(hào)的默認(rèn)處理方式都是結(jié)束進(jìn)程,只是不同的信號(hào)代表的意義不一樣。
小實(shí)驗(yàn):
我們將31個(gè)信號(hào)都自定義處理方式,并且不退出進(jìn)程,那么這個(gè)進(jìn)程是不是就無法退出了?
此時(shí)31個(gè)信號(hào)的自定義處理方式中都沒有進(jìn)程退出。
可以看到,給這個(gè)進(jìn)程這么多信號(hào),都沒有結(jié)束,而且自定義處理方式也執(zhí)行了,打印出了接收到的信號(hào)編號(hào),但是進(jìn)程還是在執(zhí)行。
那這樣是不是意味著這個(gè)進(jìn)程無法結(jié)束了?不是的,操作系統(tǒng)就防著你呢,不會(huì)讓這種惡意的東西存在的。
- 9號(hào)信號(hào)SIGKILL是不能夠自定義處理方式的,所以該信號(hào)能夠?qū)⑦@個(gè)進(jìn)程結(jié)束掉。
kill -9 pid值//殺死指定進(jìn)程
該指令可以結(jié)束任何進(jìn)程,這就是操作系統(tǒng)留的后手。
🔔信號(hào)的產(chǎn)生
有了上面的預(yù)備知識(shí)以后,就可以正式來研究信號(hào)了。
🎵通過終端按鍵產(chǎn)生信號(hào)
也就是在鍵盤上按一些熱鍵,來給進(jìn)程發(fā)送相應(yīng)的信號(hào),ctrl+c在上面本喵就講解過了,它產(chǎn)生的是2號(hào)信號(hào)SIGINT。
還有常用按鍵ctrl+\,它產(chǎn)生的是3號(hào)信號(hào)SIGQUIT。
可以看到通過鍵盤產(chǎn)生了2號(hào)和3號(hào)信號(hào)。
🎵調(diào)用系統(tǒng)調(diào)用向進(jìn)程發(fā)信號(hào)
系統(tǒng)調(diào)用kill():
- pid_t pid:要給發(fā)信號(hào)的pid
- int sig:要發(fā)送的信號(hào)編號(hào)
- 返回值:發(fā)送成功返回0,失敗返回-1
該系統(tǒng)調(diào)用是一個(gè)進(jìn)程給另一個(gè)進(jìn)程發(fā)送指定信號(hào),可以向任意進(jìn)程發(fā)送任意信號(hào)。
信號(hào)接收端:
這是一個(gè)一直在運(yùn)行的程序,用來接收從其他進(jìn)程發(fā)送過來的信號(hào)。
信號(hào)發(fā)送端:
這是一個(gè)帶有命令行參數(shù)的程序,輸入的選項(xiàng)中的pid值和信號(hào)編號(hào),在程序中在調(diào)用kill系統(tǒng)調(diào)用向指定pid的進(jìn)程發(fā)送指定信號(hào)。
效果:
左邊在執(zhí)行mysginal的時(shí)候,輸入對(duì)應(yīng)的信號(hào)編號(hào)和pid,右邊正在運(yùn)行的進(jìn)程就會(huì)接收到指定的信號(hào)而停止運(yùn)行。
系統(tǒng)調(diào)用raise():
- int sig:要發(fā)送的信號(hào)
- 返回值:發(fā)送成功返回0,失敗返回-1
該系統(tǒng)調(diào)用是由進(jìn)程自己調(diào)用,也就是進(jìn)程自己可以給自己發(fā)送任意信號(hào)。
通過命令行參數(shù)指定信號(hào),在程序執(zhí)行5秒鐘后給自己發(fā)送該信號(hào)。
無論命令行輸入哪個(gè)信號(hào)的編號(hào),在5秒鐘后,該進(jìn)程都會(huì)給自己發(fā)送輸入的信號(hào),讓進(jìn)程結(jié)束。
系統(tǒng)調(diào)用abort():
- 沒有參數(shù),沒有返回值
該系統(tǒng)調(diào)用只能給自己發(fā)送指定的信號(hào),該信號(hào)是SIGABRT,信號(hào)編號(hào)是6。
5秒鐘后給自己發(fā)送SIGABRT信號(hào)。
在運(yùn)行5秒鐘后,該進(jìn)程接收到了6號(hào)信號(hào)SIGABRT。
雖然有3個(gè)系統(tǒng)調(diào)用來產(chǎn)生信號(hào),但是歸根到底都是在使用kill系統(tǒng)調(diào)用。
- kill()可以給任意進(jìn)程發(fā)送任意信號(hào)。
- raise()可以給自己發(fā)送任意信號(hào)。
//raise本質(zhì)
kill(getpid(),signo);
- abort()可以給自己發(fā)送SIGABRT信號(hào)。
//abort本質(zhì)
kill(getpid(),SIGABRT);
🎵硬件異常產(chǎn)生信號(hào)
除0操作導(dǎo)致的硬件異常:
在這段代碼中,有除0操作,我們知道,除0得到的是無窮大的數(shù),所以在編程的時(shí)候是不允許出現(xiàn)的。
- 在運(yùn)行的時(shí)候,直接出錯(cuò),沒有再執(zhí)行下去,是因?yàn)榻邮盏搅诵盘?hào)。
- 接收到的信號(hào)是SIGFPE信號(hào),編號(hào)為8號(hào)。
這其實(shí)就是一種硬件異常產(chǎn)生的信號(hào)。
- CPU中有很多的寄存器,例如eax,ebx,eip等等。
CPU會(huì)從內(nèi)存中將代碼中的變量拿到寄存器中進(jìn)行運(yùn)算,如果有必要,還會(huì)將運(yùn)算的結(jié)果放回到內(nèi)存中。
- 還有一個(gè)狀態(tài)寄存器,如果CPU在運(yùn)算的時(shí)候發(fā)現(xiàn)了除0操作,就會(huì)將狀態(tài)寄存器的溢出標(biāo)志位置一。
此時(shí)就意味著硬件產(chǎn)生了異常。而操作系統(tǒng)是一個(gè)進(jìn)行軟硬件資源管理的軟件,CPU的中狀態(tài)寄存器的溢出標(biāo)志位置一后,操作系統(tǒng)可以第一時(shí)間拿到。
- 除0導(dǎo)致硬件異常以后,操作系統(tǒng)會(huì)給對(duì)應(yīng)的進(jìn)程發(fā)送SIGFPE信號(hào)。
當(dāng)進(jìn)程接收到SIGFPE信號(hào)以后,默認(rèn)的處理方式就是結(jié)束進(jìn)程。
現(xiàn)在我們對(duì)這個(gè)SIGFPE信號(hào)注冊(cè)一個(gè)自定義處理方式:
只打印接收到的信號(hào)編號(hào),進(jìn)程不退出。
在進(jìn)程運(yùn)行起來后,怎么就開始鞭尸了呢?也就是這個(gè)信號(hào)被操作系統(tǒng)不停的發(fā)送給這個(gè)進(jìn)程。
- 進(jìn)程收到信號(hào)后進(jìn)程不退出,隨著CPU時(shí)間片的輪轉(zhuǎn)就會(huì)再次被調(diào)到。
- CPU中只有一份寄存器,但是寄存器中的內(nèi)容屬于當(dāng)前進(jìn)程的上下文。
- 當(dāng)進(jìn)程被切換的時(shí)候,就有無數(shù)次的狀態(tài)寄存器被保存和恢復(fù)的過程。
- 而除0操作導(dǎo)致的溢出標(biāo)志位置一的數(shù)據(jù)還會(huì)被恢復(fù)到CPU中。
- 所以每一次恢復(fù)的時(shí)候,操作系統(tǒng)就會(huì)識(shí)別到,并且給對(duì)應(yīng)進(jìn)程發(fā)送SIGFPE信號(hào)。
所以就會(huì)導(dǎo)致上面不停調(diào)用自定義處理函數(shù),不停打印接收到的信號(hào)編號(hào)。
解引用空指針導(dǎo)致的硬件異常:
上面代碼中存在對(duì)空指針的解引用操作,空指針的本質(zhì)是(void*)0,而0地址處是不允許我們用戶進(jìn)行訪問的,這部分屬于內(nèi)核空間。
- 運(yùn)行的時(shí)候直接出錯(cuò),沒有再運(yùn)行下去,也是因?yàn)榻邮盏搅诵盘?hào)。
- 接收到的信號(hào)是SIGSEGV,編號(hào)是11。
這同樣是一種硬件異常產(chǎn)生的信號(hào)。
- 我們之前一直談?wù)摰捻摫頃r(shí)間上是頁表+MMU,而MMU是在CPU中的,未來簡便,我們就只說頁表。
- 進(jìn)程地址空間和物理內(nèi)存之間的映射關(guān)系實(shí)際上是有MMU去完成映射的。
- 當(dāng)對(duì)空指針解引用的時(shí)候,MMU會(huì)拒絕這種操作,從而產(chǎn)生異常標(biāo)志。
- 操作系統(tǒng)拿到MMU產(chǎn)生的異常以后就會(huì)給對(duì)應(yīng)的進(jìn)程發(fā)送SIGSEGV信號(hào)。
當(dāng)進(jìn)程接收到編號(hào)為11的SIGSEGV信號(hào)以后,默認(rèn)的處理動(dòng)作就是結(jié)束進(jìn)程。
將這個(gè)信號(hào)注冊(cè)自定義處理方式,同樣打印接收到的信號(hào)編號(hào),但是不結(jié)束進(jìn)程,可以看到,和除0操作一樣,也是在鞭尸,不停的打印。
- 硬件異常所產(chǎn)生的信號(hào),如果不結(jié)束這個(gè)進(jìn)程,我們是沒有能力去處理這個(gè)進(jìn)程的。
- 隨著時(shí)間片的輪轉(zhuǎn),這個(gè)導(dǎo)致硬件異常的進(jìn)程還會(huì)不停的調(diào)到,所以操作系統(tǒng)會(huì)不停的向進(jìn)程發(fā)送信號(hào)。
- 硬件異常產(chǎn)生的信號(hào)并不會(huì)顯示發(fā)送,而是由操作系統(tǒng)自動(dòng)發(fā)送的。
🎵由軟件條件產(chǎn)生信號(hào)
讀端關(guān)閉觸發(fā)的信號(hào):
比如在學(xué)習(xí)匿名管道的時(shí)候,當(dāng)讀端關(guān)閉的時(shí)候,寫端所在進(jìn)程就會(huì)收到編號(hào)為13的SIGPIPE信號(hào)結(jié)束進(jìn)程。
讀端5秒鐘之后關(guān)閉,寫端進(jìn)程注冊(cè)自定義處理方式,打印寫端接收到的信號(hào)編號(hào),但是不結(jié)束進(jìn)程。
在讀端關(guān)閉以后,寫端的自定義處理方式中就接收到了系統(tǒng)發(fā)給的SIGPIPE信號(hào),編號(hào)為13。
- 讀端是否關(guān)閉是軟件中的條件。
- 當(dāng)條件達(dá)成以后,產(chǎn)生信號(hào)。
鬧鐘觸發(fā)的信號(hào):
鬧鐘就是系統(tǒng)中的定時(shí)器,使用的時(shí)候同樣需要通過系統(tǒng)調(diào)用實(shí)現(xiàn):
- 參數(shù):要定的時(shí)長。
- 返回值:距離定的時(shí)間還差多少。
定時(shí)1秒鐘,在循環(huán)中進(jìn)行瘋狂加1,設(shè)置自定義處理方式,打印定時(shí)到后收到的信號(hào)編號(hào),并且統(tǒng)計(jì)這一秒中內(nèi)進(jìn)行了多少次加1操作。
當(dāng)定時(shí)1秒鐘時(shí)間到了以后,自定義處理方式中打印出接收到的信號(hào)編號(hào)是14號(hào)的SIGALRM信號(hào),并且統(tǒng)計(jì)出了1秒鐘進(jìn)行加1操作的次數(shù)。
- 自定義處理方式中沒有退出進(jìn)程,所以在執(zhí)行完處理方式以后,進(jìn)程繼續(xù)運(yùn)行。
- 也就是繼續(xù)進(jìn)行加1操作,但是不會(huì)再收到信號(hào)了,因?yàn)槎〞r(shí)到的條件只達(dá)成一次,所以信號(hào)也只產(chǎn)生一次。
如果想每隔一秒條件達(dá)成一次,產(chǎn)生一次SIGALRM信號(hào),可以在這樣處理:
- 在自定義處理方式中定時(shí)1s。
- 當(dāng)1秒定時(shí)條件達(dá)成以后,產(chǎn)生信號(hào),執(zhí)行處理方法后會(huì)開始新一輪的定時(shí)。
- 軟件中某個(gè)條件達(dá)成以后,操作系統(tǒng)就會(huì)產(chǎn)生相應(yīng)的信號(hào),比如上面的SIGPIPE信號(hào)和SIGALRM信號(hào)。
鬧鐘的管理:
操作系統(tǒng)中會(huì)有很多個(gè)進(jìn)程,我們可以創(chuàng)建一個(gè)鬧鐘,那么其他進(jìn)程也可以創(chuàng)建鬧鐘,這樣就會(huì)存在很多個(gè)鬧鐘,那么這些鬧鐘是怎么管理的呢?先描述再組織。
首先需要?jiǎng)?chuàng)建一個(gè)鬧鐘的結(jié)構(gòu)體,偽代碼:
struct alarm
{unit64_t when;//定時(shí)時(shí)長int type;//鬧鐘類型,一次性還是周期性task_struct* p;//所屬進(jìn)程的地址struct_alarm* next;//下一個(gè)鬧鐘的地址//其他屬性
}
大概就是這樣的一個(gè)結(jié)構(gòu)體來描述鬧鐘,必須由的肯定是定時(shí)時(shí)長,所屬進(jìn)程。
接下來就是組織了,用某一種數(shù)據(jù)結(jié)構(gòu)來管理這些鬧鐘對(duì)象,為了方便管理,可以選擇優(yōu)先級(jí)隊(duì)列prority_queuq來管理。
- 將定時(shí)時(shí)間最小的鬧鐘放在前面,時(shí)間長的放在后面。
- 操作系統(tǒng)每次只需要檢測隊(duì)首的定時(shí)時(shí)間是否達(dá)到就可以。
- 達(dá)到了就向?qū)?yīng)進(jìn)程發(fā)送SIGALRM信號(hào),并且從隊(duì)列中取出,以待再次檢測。
操作系統(tǒng)會(huì)周期性的檢測鏈表中的這些鬧鐘,偽代碼:
curr_timestamp > alarm.when;//超時(shí)了
//OS發(fā)送SIGALRM信號(hào)到alarm.p;
具體的實(shí)現(xiàn)細(xì)節(jié)有興趣的小伙伴可以看看源碼是怎么管理的,這里本喵只是介紹一種思想。
🔔核心轉(zhuǎn)儲(chǔ)
是否有一個(gè)疑問,31個(gè)信號(hào)的默認(rèn)處理方式都是結(jié)束進(jìn)程,并且還可以自定義處理方式,那么為什么要這么多信號(hào)呢?一個(gè)信號(hào)不就行了嗎?
- 重要的不是產(chǎn)生信號(hào)的結(jié)果,而是產(chǎn)生信號(hào)的原因。
- 所有出現(xiàn)異常的進(jìn)程,必然是收到了某一個(gè)信號(hào)。
在man的7號(hào)手冊(cè)中介紹了信號(hào)的名稱,對(duì)應(yīng)的編號(hào),默認(rèn)處理方式,以及產(chǎn)生該信號(hào)的原因。
- 我們可以根據(jù)這個(gè)表找到不同信號(hào)產(chǎn)生所對(duì)應(yīng)的不同原因。
以信號(hào)2和3為例,他兩的默認(rèn)處理方式一個(gè)是Term,一個(gè)是Core。
- Term和Core的結(jié)果都是結(jié)束進(jìn)程。
那么這兩個(gè)方式的區(qū)別在哪里呢?
- Term方式僅僅是結(jié)束進(jìn)程,結(jié)束了以后就什么都不干了。
- 但是Core不僅結(jié)束進(jìn)程,而且還會(huì)保存一些信息。
在數(shù)據(jù)越界非常嚴(yán)重的時(shí)候,該進(jìn)程會(huì)接收到SIGSEGV信號(hào),來結(jié)束進(jìn)程。
- 11號(hào)信號(hào)的默認(rèn)處理方式是Core。
在云服務(wù)器上,默認(rèn)情況下是看不到Core退出的現(xiàn)象的,這是因?yàn)樵品?wù)器關(guān)閉了core file選項(xiàng):
- core file size(紅色框)的大小是0,意味著這個(gè)選項(xiàng)是關(guān)閉的。
- 從這里還可以看到別的關(guān)于這個(gè)云服務(wù)器的信息,比如能夠打開的最多文件個(gè)數(shù),管道個(gè)數(shù),以及棧的大小等等信息。
為了能夠看到Core方式的明顯現(xiàn)象,我們需要將core file選項(xiàng)打開:
此時(shí)該選項(xiàng)就打開了,表示的意思就是核心轉(zhuǎn)儲(chǔ)文件的大小是1024個(gè)數(shù)據(jù)塊。
- 再運(yùn)行數(shù)據(jù)越界的程序時(shí),同樣會(huì)收到SIGSEGV信號(hào)停止。
- 但是在當(dāng)前目錄下會(huì)多出一個(gè)文件,如上圖中的綠色框。
- core.1739:被叫做核心轉(zhuǎn)儲(chǔ)文件,其中后綴1739是接收到該信號(hào)進(jìn)程的pid值。
對(duì)于一個(gè)奔潰的程序,我們最關(guān)心的是它為什么崩潰,在哪里崩潰?
- 當(dāng)進(jìn)程出現(xiàn)異常的時(shí)候,將進(jìn)程在對(duì)應(yīng)的時(shí)刻,在內(nèi)存中的有效數(shù)據(jù)轉(zhuǎn)儲(chǔ)到磁盤中-------核心轉(zhuǎn)儲(chǔ)。
- 核心轉(zhuǎn)儲(chǔ)的文件我們可以拿著它進(jìn)行調(diào)試,快速定位到出現(xiàn)異常而崩潰的位置。
- 使用gdb調(diào)試我們的可執(zhí)行程序。
- 調(diào)試開始后,輸入core-file core.pid值,表明調(diào)試核心轉(zhuǎn)儲(chǔ)文件。
- 此時(shí)gdb就會(huì)直接定位到產(chǎn)生異常的位置。
這就是核心轉(zhuǎn)儲(chǔ)的重要意義,它相比Term方式,能夠讓我們快速定位出現(xiàn)異常的位置。
篇幅有限,下篇文章再接著介紹。