做賭博網(wǎng)站會(huì)被判多久上海搜索引擎優(yōu)化seo
初始信號(hào)
- 初始信號(hào)
- 什么是信號(hào)
- 站在應(yīng)用角度的信號(hào)
- 查看Linux系統(tǒng)定義的信號(hào)列表
- 信號(hào)的常見處理方式
- 信號(hào)的產(chǎn)生
- 通過終端按鍵產(chǎn)生信號(hào)
- 什么是core dump?
- 如何開啟core dump?
- core dump有什么用?
- 為什么默認(rèn)關(guān)閉core dump?
- 設(shè)置了core文件大小但是沒有產(chǎn)生core文件的可能原因
- 通過系統(tǒng)函數(shù)向進(jìn)程發(fā)信號(hào)
- 由軟件條件產(chǎn)生信號(hào)
- 硬件異常產(chǎn)生信號(hào)
- 一點(diǎn)簡(jiǎn)單的拓展,有關(guān)于鍵盤產(chǎn)生信號(hào)
- 關(guān)于異常產(chǎn)生信號(hào)
初始信號(hào)
什么是信號(hào)
Linux系統(tǒng)提供的讓用戶(進(jìn)程)給其他進(jìn)程發(fā)送異步信息的一種方式
- 在沒有發(fā)生的時(shí)候,我們已經(jīng)知道發(fā)生的時(shí)候,怎么處理了
- 我們可以識(shí)別各種類型的信號(hào),結(jié)合第一條和第二條,我們能識(shí)別一個(gè)信號(hào)并處理(打個(gè)比方說,當(dāng)我們過馬路時(shí)看到紅燈時(shí),我們眼睛看到了紅燈信號(hào),然后我們的處理方式通常就是站在馬路邊上等待)
- 信號(hào)到來的時(shí)候,我們正在處理更重要的事情,我們暫時(shí)不能處理到來的信號(hào),我們必須要將到來的信號(hào)進(jìn)行臨時(shí)保存
- 信號(hào)到了,可以不立即處理,自己在合適的時(shí)候處理
- 信號(hào)的產(chǎn)生是隨時(shí)產(chǎn)生的,我們無法準(zhǔn)確預(yù)料,所以信號(hào)是異步發(fā)送的(信號(hào)的產(chǎn)生,是由別人(用戶、進(jìn)程)產(chǎn)生的,我收到之前,我一直在忙我的事情,并發(fā)在運(yùn)行的)
將以上的“我”換成進(jìn)程,就是進(jìn)程看待信號(hào)的方式
站在應(yīng)用角度的信號(hào)
- 用戶輸入指令,在shell下啟動(dòng)一個(gè)前臺(tái)進(jìn)程
- 用戶按下ctrl+c,鍵盤輸入產(chǎn)生硬件中斷,被OS獲取,解釋成信號(hào),發(fā)送給目標(biāo)前臺(tái)進(jìn)程
- 前臺(tái)進(jìn)程因?yàn)槭盏叫盘?hào),進(jìn)而引起進(jìn)程退出
注意:
1、ctrl+c
產(chǎn)生的信號(hào)只能發(fā)送給前臺(tái)進(jìn)程,一個(gè)命令后面加上&可以將其放到后臺(tái)執(zhí)行,這樣shell就不必等待該進(jìn)程結(jié)束就可以接受新的命令,啟動(dòng)新的進(jìn)程
2.、shell可以同時(shí)接受運(yùn)行一個(gè)前臺(tái)進(jìn)程和任意多個(gè)后臺(tái)進(jìn)程,只有前臺(tái)進(jìn)程才能接到例如ctrl+c
這種控制鏈產(chǎn)生的信號(hào)
3、 前臺(tái)進(jìn)程在運(yùn)行過程中用戶隨時(shí)可能按下ctrl+c而產(chǎn)生一個(gè)信號(hào),也就是說該進(jìn)程的用戶代碼空間執(zhí)行到任何地方都可能因?yàn)槭盏叫盘?hào)而終止或者產(chǎn)生其他行為,所以信號(hào)相對(duì)于進(jìn)程的控制流程來說是異步的
查看Linux系統(tǒng)定義的信號(hào)列表
- 每個(gè)信號(hào)都有一個(gè)編號(hào)和一個(gè)宏定義名稱,比如2號(hào)信號(hào)就是
#define SIGINT 2
- 編號(hào)34以上的是實(shí)時(shí)信號(hào),這篇博客主要是討論編號(hào)34以下的信號(hào),不討論實(shí)時(shí)信號(hào),這些信號(hào)各自在什么條件下產(chǎn)生,默認(rèn)的處理動(dòng)作是什么,在signal(7)中都有詳細(xì)的說明:在命令行輸入
man 7 signal
即可查看
信號(hào)的常見處理方式
常見的信號(hào)處理方式有以下三種
- 忽略此信號(hào)
- 執(zhí)行該信號(hào)的默認(rèn)處理動(dòng)作
- 提供一個(gè)信號(hào)處理的函數(shù),要求在內(nèi)核處理信號(hào)的時(shí)候切換到用戶態(tài)執(zhí)行這個(gè)處理函數(shù),這種方式稱之為捕捉(catch)一個(gè)信號(hào)
??這里要先介紹一個(gè)linux的系統(tǒng)調(diào)用signal
1、當(dāng)我們要忽略一個(gè)信號(hào)時(shí)(這里用2號(hào)信號(hào)為例),就將第二個(gè)參數(shù)傳遞SIG_IGN即可
2、執(zhí)行該信號(hào)的默認(rèn)處理動(dòng)作,2號(hào)信號(hào)的默認(rèn)處理動(dòng)作就是終止當(dāng)前正在運(yùn)行的前臺(tái)進(jìn)程,這里就不再演示了
3、讓該信號(hào)執(zhí)行自定義的動(dòng)作
信號(hào)的產(chǎn)生
通過終端按鍵產(chǎn)生信號(hào)
??SIGINT的默認(rèn)處理動(dòng)作是終止當(dāng)前進(jìn)程,SIGQUIT的默認(rèn)處理動(dòng)作是終止進(jìn)程并Core Dump(之前演示時(shí)用到的ctrl+c就是向進(jìn)程發(fā)送2號(hào)的方式)
??進(jìn)程收到了的大部分信號(hào)的動(dòng)作都是進(jìn)程自己終止,終止有兩種方案,一種叫做core,一種叫做term,它們又什么區(qū)別?
什么是core dump?
??首先解釋什么是Core Dump
。當(dāng)一個(gè)進(jìn)程要異常終止時(shí),可以選擇把進(jìn)程的用戶空間內(nèi)存數(shù)據(jù)全部 保存到磁盤上,文件名通常是core
,這叫做Core Dump
。進(jìn)程異常終止通常是因?yàn)橛蠦ug,比如非法內(nèi)存訪問導(dǎo)致段錯(cuò)誤,事后可以用調(diào)試器檢查core
文件以查清錯(cuò)誤原因,這叫做Post-mortem Debug
(事后調(diào)試)。
??一個(gè)進(jìn)程允許產(chǎn)生多大的core
文件取決于進(jìn)程的Resource Limit
(這個(gè)信息保存 在PCB中)。默認(rèn)是不允許產(chǎn)生core
文件的,因?yàn)?code>core文件中可能包含用戶密碼等敏感信息,不安全。在開發(fā)調(diào)試階段可以用ulimit
命令改變這個(gè)限制,允許產(chǎn)生core文件。 首先用ulimit命
令改變Shell進(jìn)程的Resource Limit
,允許core文件最大為1024K
??而term就是不產(chǎn)生任何文件的普通終止行為
如何開啟core dump?
??之前已經(jīng)提到了core dump默認(rèn)是關(guān)閉的,使用命令ulimit -c可以查看相關(guān)的信息
使用ulimit -c可以通過設(shè)置文件大小打開core dump功能
core dump有什么用?
??當(dāng)一個(gè)進(jìn)程發(fā)生了除0異常,如果是term結(jié)束,在系統(tǒng)層面上直接不管了,直接把代碼和數(shù)據(jù)還有內(nèi)核數(shù)據(jù)結(jié)果直接釋放掉,這個(gè)進(jìn)程就沒了
??但是如果信號(hào)除0動(dòng)作是core,則會(huì)將進(jìn)程在內(nèi)存中的核心數(shù)據(jù)(主要是與調(diào)試有關(guān))轉(zhuǎn)儲(chǔ)到磁盤中形成core、core.pid的文件,這樣可以定位到這個(gè)進(jìn)程為什么會(huì)退出,以及是執(zhí)行到哪一行代碼退出了
??core其實(shí)是一種機(jī)制,如果進(jìn)程在執(zhí)行過程中出異常了,就將進(jìn)程在內(nèi)核中的核心數(shù)據(jù)轉(zhuǎn)儲(chǔ)到磁盤當(dāng)中以文件的方式保存,這種方式叫做core dump:核心轉(zhuǎn)儲(chǔ)
??所以命令ulimit -c其實(shí)是在設(shè)置core文件的上限大小(默認(rèn)情況下為0,代表即便是core退出,也不要形成核心轉(zhuǎn)儲(chǔ)文件)
??說了這么多,core文件中既然包含了相關(guān)的錯(cuò)誤信息,我們就可以根據(jù)文件中的錯(cuò)誤信息進(jìn)行調(diào)試
int main()
{int a=10;a/=0;return 0;
}
??比如這段代碼,在代碼中產(chǎn)生一個(gè)除0異常,然后運(yùn)行程序
發(fā)現(xiàn)多了一個(gè)文件core,這個(gè)文件存的就是程序的錯(cuò)誤原因
??大部分信號(hào)當(dāng)中,只要是退出信息(Action)是core,都是可以通過打開core dump功能,事后根據(jù)core文件進(jìn)行定位問題和調(diào)試
云服務(wù)器默認(rèn)是將進(jìn)程以core形式退出,進(jìn)行了特定的設(shè)定,默認(rèn)core是被關(guān)閉的
為什么默認(rèn)關(guān)閉core dump?
??Linux系統(tǒng)是有服務(wù)關(guān)閉自動(dòng)重啟的功能的,如果服務(wù)一異常關(guān)閉,然后重啟,一關(guān)閉又重啟…就會(huì)留下很多的core文件,時(shí)間一長(zhǎng)就會(huì)導(dǎo)致云服務(wù)器的磁盤被占滿->防止未知的core dump一直在進(jìn)行,導(dǎo)致磁盤服務(wù)器被打滿
設(shè)置了core文件大小但是沒有產(chǎn)生core文件的可能原因
??我在第一次使用已經(jīng)設(shè)置了core文件的大小但是出現(xiàn)異常時(shí)并沒有出現(xiàn)core文件,我查了一下,有以下幾種說法
- 程序設(shè)置了用戶id(即調(diào)用setuid),但當(dāng)前用戶并非該程序文件的所有者
- 程序設(shè)置了組id(即調(diào)用setgid),但當(dāng)前用戶并非該程序文件的組所有者
- 用戶沒有當(dāng)前目錄或指定core文件產(chǎn)生目錄的寫權(quán)限
- core文件太大,磁盤空間不足
??但是我檢查了這幾點(diǎn)發(fā)現(xiàn)這些都不是原因,后來我了解到,可能是core文件產(chǎn)生的位置有關(guān),core的缺省位置是程序所在目錄,可以通過修改/proc/sys/kernel/core_pattern
來指定core文件生成位置了名稱。
??然后我查看了core_pattern文件,發(fā)現(xiàn)文件內(nèi)容是一段腳本程序,后來查看說明文件,才知道core_pattern中如果首先指定了一個(gè) ‘|’ 管道符,則會(huì)將生成的core文件傳遞給后面所跟的腳本去處理。
??至此,也就確定了問題的原因,| 管道符后面的腳本將我們的core文件給吞了,解決方法自然就是去掉這個(gè)腳本,換成自己指定的目錄
??但直接去修改core_pattern文件并沒有成功,保存時(shí)會(huì)提示FSync錯(cuò)誤,查閱資料得知,這個(gè)文件有特殊限制,只能通過命令:
sudo bash -c "echo 這里是寫入內(nèi)容 > /proc/sys/kernel/core_pattern "
我使用的是
sudo bash -c "echo core > /proc/sys/kernel/core_pattern "
??來進(jìn)行寫入,即指定程序所在目錄為core文件生成目錄,core文件名稱為"core"。
通過系統(tǒng)函數(shù)向進(jìn)程發(fā)信號(hào)
??kill函數(shù)是向任意進(jìn)程發(fā)送信號(hào),而raise是向自己發(fā)送信號(hào)。兩個(gè)函數(shù)的返回都是成功返回0,錯(cuò)誤返回-1
??abort函數(shù)的作用是使當(dāng)前進(jìn)程接收到信號(hào)而異常重者,和kill(getpid(),6)
用法有點(diǎn)相似(6號(hào)信號(hào)是SIGABRT
)
由軟件條件產(chǎn)生信號(hào)
??SIGPIPE就是很典型的由軟件條件產(chǎn)生的信號(hào),這個(gè)信號(hào)是在進(jìn)程間通信方式之一——管道中,讀端已經(jīng)關(guān)閉而寫端仍然還在寫入時(shí),這個(gè)時(shí)候系統(tǒng)就會(huì)通過向?qū)懚税l(fā)送SIGPIPE信號(hào)關(guān)閉寫端
??還有個(gè)信號(hào)SIGALRM信號(hào)
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
??調(diào)用alarm函數(shù)可以設(shè)定一個(gè)鬧鐘,也就是告訴內(nèi)核在seconds秒之后給當(dāng)前進(jìn)程發(fā)SIGALRM信號(hào), 該信號(hào)的默認(rèn)處理動(dòng)作是終止當(dāng)前進(jìn)程。
??這個(gè)函數(shù)的返回值是0或者是以前設(shè)定的鬧鐘時(shí)間還余下的秒數(shù)。打個(gè)比方,某人要小睡一覺,設(shè)定鬧鐘為30分鐘之后響,20分鐘后被人吵醒了,還想多睡一會(huì)兒,于是重新設(shè)定鬧鐘為15分鐘之后響,“以前設(shè)定的鬧鐘時(shí)間還余下的時(shí)間”就是10分鐘。如果seconds值為0,表示取消以前設(shè)定的鬧鐘,函數(shù)的返回值仍然是以前設(shè)定的鬧鐘時(shí)間還余下的秒數(shù)
??但是"鬧鐘"只能響一次,所以如果要讓這個(gè)鬧鐘多次響,需要捕捉SIGARM信號(hào),在處理完信號(hào)后,再設(shè)置一個(gè)鬧鐘
#include<iostream>
#include<signal.h>int main()
{int cnt=0;alarm(1);while(true){std::cout<<cnt<<std::endl;cnt++;}return 0;
}
??這個(gè)代碼的作用就是在1秒鐘之內(nèi)不停的數(shù)數(shù),1秒種到了因接收到了SIGARM就停止
硬件異常產(chǎn)生信號(hào)
??硬件異常被硬件以某種方式被硬件檢測(cè)到并通知內(nèi)核,然后內(nèi)核向當(dāng)前進(jìn)程發(fā)送適當(dāng)?shù)男盘?hào)。例如當(dāng)前進(jìn)程執(zhí)行了除以0的指令,CPU的運(yùn)算單元會(huì)產(chǎn)生異常,內(nèi)核將這個(gè)異常解釋 為SIGFPE信號(hào)發(fā)送給進(jìn)程。再比如當(dāng)前進(jìn)程訪問了非法內(nèi)存地址,MMU會(huì)產(chǎn)生異常,內(nèi)核將這個(gè)異常解釋為SIGSEGV信號(hào)發(fā)送給進(jìn)程。
一點(diǎn)簡(jiǎn)單的拓展,有關(guān)于鍵盤產(chǎn)生信號(hào)
??鍵盤是一個(gè)硬件,作為一個(gè)鍵盤它需要知道以下幾點(diǎn)
-
按鍵按下了
-
哪些按鍵按下了
-
字符輸入(字符設(shè)備(鍵盤都是字符輸入)),鍵盤還可以進(jìn)行組合鍵輸入(ctrl +c),相當(dāng)于輸入的是命令
??不管你是鍵盤輸入還是組合鍵輸入,在鍵盤層面上,就是按鍵,但是到底輸入的字符還是命令,這個(gè)是由操作系統(tǒng)決定的(鍵盤和OS聯(lián)合解釋),因?yàn)橐磺薪晕募?#xff0c;所以鍵盤設(shè)備也可以看作是操作系統(tǒng)打開的一個(gè)文件,所以鍵盤獲取到的數(shù)據(jù)就會(huì)放到鍵盤的輸入緩沖區(qū)里,上層就通過0號(hào)文件描述符將其讀出來
??操作系統(tǒng)怎么知道鍵盤上面有數(shù)據(jù)輸入?
??要么是操作系統(tǒng)去定期的檢測(cè)鍵盤是否有數(shù)據(jù)被按下,但是這樣的話效率就太低了
??其實(shí)操作系統(tǒng)時(shí)使用了一種硬件中斷的技術(shù),當(dāng)操作系統(tǒng)剛開機(jī)的時(shí)候,已經(jīng)形成了一張表,這張表上已經(jīng)注冊(cè)了很多對(duì)軟硬件進(jìn)行操作的方法
??這個(gè)所謂的中斷向量表就是函數(shù)指針數(shù)組。此時(shí)的CPU正在執(zhí)行進(jìn)程代碼。這個(gè)中斷向量表其實(shí)也是屬于操作系統(tǒng)的數(shù)據(jù)
??CPU只和內(nèi)存打交道,并且CPU內(nèi)部有很多的針腳(物理性的),這些針腳在主板上是可以和鍵盤進(jìn)行連接的(每個(gè)針腳都有特定的編號(hào)),未來鍵盤在進(jìn)行按鍵的時(shí)候會(huì)給CPU中特定的針腳觸發(fā)硬件中斷,CPU就知道哪個(gè)針腳上面有高電平了就可以識(shí)別這個(gè)針腳。
??總之,CPU已經(jīng)知道了有觸發(fā)的針腳上面已經(jīng)有高電平了,有因?yàn)槊總€(gè)針腳上面又有特定的編號(hào),也就意味著當(dāng)鍵盤被按下時(shí)會(huì)向CPU的2號(hào)針腳處發(fā)送高電平(也可以把這個(gè)2號(hào)稱之為中斷號(hào))。即CPU能識(shí)別到 -
有高電平
-
是幾號(hào)針腳
??CPU內(nèi)部還有寄存器,寄存器req就會(huì)將中斷號(hào)存起來(到此,硬件到軟件的動(dòng)作就做完了)
??然后,CPU識(shí)別到對(duì)應(yīng)的寄存器中有數(shù)字,CPU就直接要求操作系統(tǒng)先暫停當(dāng)前進(jìn)程,然后讓操作系統(tǒng)拿著中斷號(hào)去中斷向量表去查對(duì)應(yīng)的方法,然后根據(jù)中斷向量表中對(duì)應(yīng)的方法去執(zhí)行讀取數(shù)據(jù)的任務(wù)。因?yàn)橹袛嘞蛄勘碇械姆椒ǘ际遣僮飨蛄勘硖峁┑?#xff0c;所以在調(diào)用里面的方法的時(shí)候,操作系統(tǒng)就能夠知道數(shù)據(jù)到來了并將數(shù)據(jù)讀入內(nèi)存中
??讀到數(shù)據(jù)之后就要對(duì)數(shù)據(jù)做“判定”,看是字符還是控制命令,如果是字符,就將其放入到緩沖區(qū)中讓上層讀取;如果是控制命令,就將其解釋成信號(hào),讓后再將這個(gè)信號(hào)發(fā)送給進(jìn)程(解釋信號(hào)就是到進(jìn)程的pcb中將位圖上對(duì)應(yīng)的比特位由0置1)
??之前提到,當(dāng)信號(hào)在到來的時(shí)候,如果進(jìn)程正在處理更重要的事情,就會(huì)暫時(shí)不處理信號(hào),而是將信號(hào)進(jìn)行臨時(shí)保存,保存在哪里呢?保存在進(jìn)程的pcb中。一個(gè)進(jìn)程可能會(huì)收到多個(gè)信號(hào),所以pcb內(nèi)部就需要對(duì)收到的信號(hào)進(jìn)行管理,因?yàn)樾盘?hào)的編號(hào)是1、2……31,所以pcb中采用的就是位圖的方法保存信號(hào)
關(guān)于異常產(chǎn)生信號(hào)
emsp;?CPU中有很多寄存器,有些寄存器是用來做計(jì)算的,也有些寄存器是用來CPU內(nèi)部中的管理,其中就有一個(gè)寄存器叫做EFLAGS/RFLAGS,這個(gè)是標(biāo)志寄存器,包含了狀態(tài)和控制標(biāo)志,其中有一個(gè)標(biāo)志稱為CF(這個(gè)標(biāo)志稱作為是溢出標(biāo)志,Overfliow Flag),當(dāng)CPU內(nèi)部進(jìn)行運(yùn)算
int a=10;
a/=0;
??CPU內(nèi)部會(huì)將10放到一個(gè)寄存器中(假設(shè)為eax),然后將0放到一個(gè)寄存器中(假設(shè)為ebx),按照代碼就是讓eax中數(shù)初一abx中的數(shù),然后將結(jié)果寫回到eax中,但是在運(yùn)算10除以0的時(shí)候,得到的是一個(gè)巨大的數(shù)字,據(jù)發(fā)生了溢出,然后溢出標(biāo)志位設(shè)為1,也就是說計(jì)算出現(xiàn)了錯(cuò)誤(計(jì)算錯(cuò)誤,表現(xiàn)到了CPU的寄存器上也就是硬件上),此時(shí)操作系統(tǒng)識(shí)別到了操作系統(tǒng)內(nèi)部有錯(cuò)誤,CPU就不再調(diào)度進(jìn)程了,CPU就告訴操作系統(tǒng),操作系統(tǒng)發(fā)現(xiàn)確實(shí)出現(xiàn)了異常,就直接向?qū)?yīng)進(jìn)程的pcb中發(fā)送對(duì)應(yīng)的溢出信號(hào)(修改對(duì)應(yīng)的位圖),至此,就完成了信號(hào)的發(fā)送