前言
在?Linux
?中,進(jìn)程具有獨(dú)立性,進(jìn)程在運(yùn)行后可能 “放飛自我”,這是不利于管理的,于是需要一種約定俗成的方式來(lái)控制進(jìn)程的運(yùn)行,這就是?進(jìn)程信號(hào)
,本文將會(huì)從什么是進(jìn)程信號(hào)開(kāi)篇,講述各種進(jìn)程信號(hào)的產(chǎn)生方式及作用。
一、信號(hào)是什么
1.1 信號(hào)的概念
信號(hào) 是信息傳遞的承載方式,一種信號(hào)往往代表著一種執(zhí)行動(dòng)作:上課鈴聲,紅綠燈,電話鈴聲
????????當(dāng)然這些都是生活中的 信號(hào),當(dāng)產(chǎn)生這些 信號(hào) 時(shí),我們會(huì)立馬想到對(duì)應(yīng)的 動(dòng)作 ,這是因?yàn)?我們認(rèn)識(shí)并能處理這些信號(hào)
????????我們能進(jìn)行處理是因?yàn)槭苓^(guò)教育,學(xué)習(xí)了執(zhí)行動(dòng)作,但對(duì)進(jìn)程來(lái)說(shuō),它可沒(méi)有接受過(guò)九年義務(wù)教育,也不知道什么時(shí)候該干什么事
????????于是程序員們給操作系統(tǒng)植入了一批 指令,一個(gè)指令表示一種特殊動(dòng)作,而這些指令就是 信號(hào)(進(jìn)程信號(hào))
通過(guò)?kill -l
?查看當(dāng)前系統(tǒng)中的信號(hào)集合表
kill -l
這些就是當(dāng)前系統(tǒng)中的?進(jìn)程信號(hào),一共?62
?個(gè),其中?1~31
?號(hào)信號(hào)為?普通信號(hào)(學(xué)習(xí)目標(biāo)),用于?分時(shí)操作系統(tǒng);剩下的?32~64
?號(hào)信號(hào)為?實(shí)時(shí)信號(hào),用于?實(shí)時(shí)操作系統(tǒng)
- 分時(shí)操作系統(tǒng):根據(jù)時(shí)間片實(shí)行公平調(diào)度,適用于個(gè)人電腦
- 實(shí)時(shí)操作系統(tǒng):高響應(yīng),適合任務(wù)較少、需要快速處理的平臺(tái),比如汽車車機(jī)、火箭發(fā)射控制臺(tái)
普通信號(hào)只保存它有無(wú)產(chǎn)生,實(shí)時(shí)信號(hào)可以保持很長(zhǎng)時(shí)間
因?yàn)槲覀兊南到y(tǒng)屬于?分時(shí)操作系統(tǒng),所以只需要研究?1~31
?號(hào)信號(hào)即可,當(dāng)然也不是全部研究,部分信號(hào)只做了解即可
1.2 信號(hào)的作用
早在 《Linux進(jìn)程學(xué)習(xí)【進(jìn)程狀態(tài)】》 我們就已經(jīng)使用過(guò)?信號(hào)?了,比如:
kill -9 pid
?終止進(jìn)程運(yùn)行kill -19 pid
?暫停進(jìn)程運(yùn)行kill -18 pid
?恢復(fù)進(jìn)程運(yùn)行
就連常用的?ctrl+c
?和?ctrl+d
?熱鍵本質(zhì)上也是?信號(hào)
這么多信號(hào),其對(duì)應(yīng)功能是什么呢?
- 可以通過(guò)?
man 7 signal
?進(jìn)行查詢
其中:
1.3 信號(hào)的組成級(jí)原理
進(jìn)程信號(hào)由?信號(hào)編號(hào) + 執(zhí)行動(dòng)作?構(gòu)成,一個(gè)信號(hào)對(duì)應(yīng)一種動(dòng)作,對(duì)于進(jìn)程來(lái)說(shuō),動(dòng)作無(wú)非就這幾種:終止進(jìn)程、暫停進(jìn)程、恢復(fù)進(jìn)程,3
?個(gè)信號(hào)就夠用了啊,為什么要搞這么多信號(hào)?
- 創(chuàng)造信號(hào)的目的不只是控制進(jìn)程,還要便于管理進(jìn)程,進(jìn)程的終止原因有很多種,如果一概而論的話,對(duì)于問(wèn)題分析是非常不友好的,所以才會(huì)將信號(hào)細(xì)分化,搞出這么多信號(hào),目的就是為了方便定位、分析、解決問(wèn)題
- 并且 普通信號(hào) 就?
31
?個(gè),這就是意味著所有普通信號(hào)都可以存儲(chǔ)在一個(gè)?int
?中,表示是否收到該信號(hào)(信號(hào)的保存)
所以信號(hào)被細(xì)化了,不同的信號(hào)對(duì)應(yīng)不同的執(zhí)行動(dòng)作,雖然大部分最終都是終止進(jìn)程
進(jìn)程的執(zhí)行動(dòng)作是可修改的,默認(rèn)為系統(tǒng)預(yù)設(shè)的?默認(rèn)動(dòng)作
- 默認(rèn)動(dòng)作
- 忽略
- 自定義動(dòng)作
所以我們可以?更改信號(hào)的執(zhí)行動(dòng)作
信號(hào)有這么多個(gè),并且多個(gè)進(jìn)程可以同時(shí)產(chǎn)生多個(gè)信號(hào),操作系統(tǒng)為了管理,先描述、再組織,在?PCB
?中增加了 信號(hào)相關(guān)的數(shù)據(jù)結(jié)構(gòu):signal_struct
,在這個(gè)結(jié)構(gòu)體中,必然存在一個(gè) 位圖結(jié)構(gòu)?uint32_t signals
?存儲(chǔ)?1~31
?號(hào)信號(hào)的有無(wú)信息
下面對(duì)?進(jìn)程信號(hào)?做一波概念性的總結(jié):
1.信號(hào)是執(zhí)行的動(dòng)作的信息載體,程序員在設(shè)計(jì)進(jìn)程的時(shí)候,早就已經(jīng)設(shè)計(jì)了其對(duì)信號(hào)的識(shí)別能力
2.信號(hào)對(duì)于進(jìn)程來(lái)說(shuō)是異步的,隨時(shí)可能產(chǎn)生,如果信號(hào)產(chǎn)生時(shí),進(jìn)程在處理優(yōu)先級(jí)更高的事情,那么信號(hào)就不能被立即處理,此時(shí)進(jìn)程需要保存信號(hào),后續(xù)再處理
3.進(jìn)程可以將 多個(gè)信號(hào) 或 還未處理 的信號(hào)存儲(chǔ)在 signal_struct 這個(gè)結(jié)構(gòu)體中,具體信號(hào)編號(hào),存儲(chǔ)在 uint32_t signals 這個(gè)位圖結(jié)構(gòu)中
4.所謂的 “發(fā)送” 信號(hào),其實(shí)就是寫入信號(hào),修改進(jìn)程中位圖結(jié)構(gòu)中對(duì)應(yīng)的比特位,由 0 置為 1,表示該信號(hào)產(chǎn)生了
5.signal_struct 屬于內(nèi)核數(shù)據(jù)結(jié)構(gòu),只能由 操作系統(tǒng) 進(jìn)行同一修改,無(wú)論信號(hào)是如何產(chǎn)生的,最終都需要借助 操作系統(tǒng) 進(jìn)行發(fā)送
6.信號(hào)并不是立即處理的,它會(huì)在合適的時(shí)間段進(jìn)行統(tǒng)一處理
所以?進(jìn)程信號(hào)?可以分為三步:信號(hào)產(chǎn)生 =》 信號(hào)保存 =》 信號(hào)處理
------------------------------------------------信號(hào)的產(chǎn)生方式------------------------------------------------
二、鍵盤鍵入
信號(hào)產(chǎn)生(發(fā)送)的第一種方式:鍵盤鍵入,通俗來(lái)說(shuō)就是命令行操作
2.1 ctrl+c 終止前臺(tái)進(jìn)程(前臺(tái)進(jìn)程只有一個(gè))
系統(tǒng)卡死,程序死循環(huán)。這些都是比較常見(jiàn)的問(wèn)題,當(dāng)發(fā)生這些問(wèn)題時(shí),我們可以通過(guò)?鍵盤鍵入?ctrl + c
?發(fā)出?2
?號(hào)信號(hào)終止前臺(tái)進(jìn)程的運(yùn)行
下面寫一段死循環(huán)代碼:
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
while(true)
{
cout << "我是一個(gè)進(jìn)程,我正在運(yùn)行…… PID: " << getpid() << endl;
sleep(1);
}
return 0;
}
運(yùn)行程序后,會(huì)一直循環(huán)打印,此時(shí)如果想要終止進(jìn)程,可以直接按?ctrl + c
?發(fā)出?2
?號(hào)信號(hào),終止前臺(tái)進(jìn)程
此時(shí)發(fā)出了一個(gè)?2
?號(hào)信號(hào)?SIGINT
?終止了該進(jìn)程的運(yùn)行
如何證明呢?如何證明按?ctrl + c
?發(fā)出的是?2
?號(hào)信號(hào)呢?
證明自有方法,前面說(shuō)過(guò),一個(gè)信號(hào)配有一個(gè)執(zhí)行動(dòng)作,并且執(zhí)行動(dòng)作是可以修改的,需要用到?signal
?函數(shù)(屬于 信號(hào)處理 部分的內(nèi)容,這里需要提前用一下)
ctrl + c
?終止的是當(dāng)前正在運(yùn)行的前臺(tái)進(jìn)程,如果在程序運(yùn)行時(shí)加上?&
?表示讓其后臺(tái)運(yùn)行,此時(shí)會(huì)發(fā)現(xiàn)無(wú)法終止進(jìn)程
像這種后臺(tái)進(jìn)程?
ctrl + c
?是無(wú)法終止的,可以通過(guò)?kill -9 PID
?發(fā)出?9
?信號(hào)終止它
2.1.1 signal注冊(cè)執(zhí)行動(dòng)作
signal
?函數(shù)可以用來(lái)?修改信號(hào)的執(zhí)行動(dòng)作,也叫注冊(cè)自定義執(zhí)行動(dòng)作
調(diào)用成功返回上一個(gè)執(zhí)行方法的值(其實(shí)就是下標(biāo),后面介紹),失敗則返回?SIG_ERR
,并設(shè)置錯(cuò)誤碼
- 參數(shù)1?待操作信號(hào)的編號(hào)
- 參數(shù)2?待注冊(cè)的新方法
參數(shù)1?就是信號(hào)編號(hào),為?int
,單純地傳遞?信號(hào)名也是可以的,因?yàn)樾盘?hào)名其實(shí)就是信號(hào)編號(hào)的宏定義
參數(shù)2?是一個(gè)函數(shù)指針,意味著需要傳遞一個(gè)?參數(shù)為?int
,返回值為空的函數(shù)對(duì)象
- 參數(shù)?
int
?是執(zhí)行動(dòng)作的信號(hào)編號(hào)void handler(int) //其中的函數(shù)名可以自定義
顯然,
signal
?函數(shù)是一個(gè)?回調(diào)函數(shù),當(dāng)信號(hào)發(fā)出時(shí),會(huì)去調(diào)用相應(yīng)的函數(shù),也就是執(zhí)行相應(yīng)的動(dòng)作
我們先對(duì)?2
?號(hào)信號(hào)注冊(cè)新動(dòng)作,在嘗試按下?ctrl + c
,看看它發(fā)出的究竟是不是?2
?號(hào)信號(hào)
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
void handler(int signo)
{
cout << "當(dāng)前 " << signo << " 號(hào)信號(hào)正在嘗試執(zhí)行相應(yīng)的動(dòng)作" << endl;
}
int main()
{
//給 2 號(hào)信號(hào)注冊(cè)新方法
signal(2, handler);
while(true)
{
cout << "我是一個(gè)進(jìn)程,我正在運(yùn)行…… PID: " << getpid() << endl;
sleep(1);
}
return 0;
}
????????當(dāng)我們修改 2 號(hào)信號(hào)的執(zhí)行動(dòng)作后,再次按下 ctrl + c 嘗試終止前臺(tái)進(jìn)程,結(jié)果失敗了!執(zhí)行動(dòng)作變成了我們注冊(cè)的新動(dòng)作
????????這足以證明 ctrl + c 就是在給前臺(tái)進(jìn)程發(fā)出 2 號(hào)信號(hào),ctrl + c 失效后,可以通過(guò) ctrl + \ 終止進(jìn)程,發(fā)出的是 3 號(hào)信號(hào)(3 號(hào)信號(hào)在發(fā)出后,會(huì)生成 核心轉(zhuǎn)儲(chǔ) 文件)
需要注意:
大部分信號(hào)的執(zhí)行動(dòng)作都可以被修改了,但?9
?號(hào)信號(hào)沒(méi)有,因?yàn)?9
?號(hào)信號(hào)是?SIGKILL
,專門用于殺死進(jìn)程,只要是進(jìn)程,他都能干掉
????????19
?號(hào)信號(hào)?SIGSTOP
?也無(wú)法修改執(zhí)行動(dòng)作,?9
?號(hào)?SIGKILL
?和?19
?號(hào)?SIGSTOP
?信號(hào)是很特殊的,經(jīng)過(guò)特殊設(shè)計(jì),不能修改其執(zhí)行動(dòng)作!
2.2 硬件中斷
當(dāng)我們從鍵盤按下?ctrl + c
?時(shí),發(fā)生了這些事:CPU
?獲取到鍵盤 “按下” 的信號(hào),調(diào)用鍵盤相應(yīng)的 “方法” ,從鍵盤中讀取數(shù)據(jù),讀取數(shù)據(jù)后解析,然后發(fā)出?3
?號(hào)信號(hào)
其中?CPU
?捕獲鍵盤 “按下” 信號(hào)的操作稱為?硬件中斷
CPU
?中有很多的針腳,不同的硬件對(duì)應(yīng)著不同的針腳,每一個(gè)針腳都有自己的編號(hào),硬件與針腳一對(duì)一相連,并通過(guò) 中斷控制器(比如?8259
)進(jìn)行控制,當(dāng)我們按下鍵盤后
- 中斷控制器首先給?
CPU
?發(fā)送信息,包括鍵盤對(duì)應(yīng)的針腳號(hào) - 然后?
CPU
?將獲取到的針腳號(hào)(中斷號(hào))寫入 寄存器 中 - 最后根據(jù) 寄存器 里的 中斷號(hào),去 中斷向量表 中查表,找到對(duì)應(yīng)硬件的方法,執(zhí)行它的讀取方法就行了
這樣?CPU
?就知道是?鍵盤?發(fā)出的信號(hào),然后就會(huì)去調(diào)用?鍵盤?的執(zhí)行方法,通過(guò)鍵盤的讀取方法,讀取到?ctrl + c
?這個(gè)信息,轉(zhuǎn)化后,就是?2
?號(hào)信號(hào),執(zhí)行終止前臺(tái)進(jìn)程的動(dòng)作
鍵盤被按下 和 鍵盤哪些位置被按下 是不一樣的
- 首先鍵盤先按下,
CPU
?確定對(duì)應(yīng)的讀取方法 - 其次才是通過(guò)?
讀取方法
?從鍵盤中讀取數(shù)據(jù)
硬件中斷 的流程與 進(jìn)程信號(hào) 的流程雷同,同樣是 先檢測(cè)到信號(hào),然后再去執(zhí)行相應(yīng)的動(dòng)作,不過(guò)此時(shí)發(fā)送的是 中斷信號(hào),執(zhí)行的是 調(diào)用相應(yīng)方法罷了
信號(hào) 與 動(dòng)作 的設(shè)計(jì)方式很實(shí)用,操作系統(tǒng)只需要關(guān)注是否有信號(hào)發(fā)出,發(fā)出后去中斷向量表中調(diào)用相應(yīng)的方法即可,不用管硬件是什么樣、如何變化,做到了 操作系統(tǒng) 與 硬件 間的解耦
三、系統(tǒng)調(diào)用
????????除了可以通過(guò)?鍵盤鍵入?發(fā)送信號(hào)外,還可以通過(guò)直接調(diào)用?系統(tǒng)接口?發(fā)送信號(hào),畢竟?bash
?也是一個(gè)進(jìn)程,本質(zhì)上就是在進(jìn)行程序替換而已
3.1 kill函數(shù)
信號(hào)的發(fā)送主要是通過(guò)?kill
?函數(shù)進(jìn)行發(fā)送
返回值:成功返回?0
,失敗返回?-1
?并設(shè)置錯(cuò)誤碼
參數(shù)1:待操作進(jìn)程的?PID
參數(shù)2:待發(fā)送的信號(hào)
下面來(lái)簡(jiǎn)單用一下(程序運(yùn)行?5
?秒后,自己把自己殺死)
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
int main()
{
int n = 1;
while (true)
{
cout << "我是一個(gè)進(jìn)程,已經(jīng)運(yùn)行了 " << n << " 秒 PID: " << getpid() << endl;
sleep(1);
n++;
if (n > 5)
kill(getpid(), SIGKILL);
}
return 0;
}
kill
?函數(shù)當(dāng)然也可以發(fā)送其他信號(hào),這里就不一一展示了,其實(shí)命令行中的?kill
?命令就是對(duì)?kill
?函數(shù)的封裝,kill -信號(hào)編號(hào) -PID
?其中的參數(shù)2、3不正是?kill
?函數(shù)所需要的參數(shù)嗎?所以我們可以嘗試自己搞一個(gè)?myKill
?命令
3.2 模擬實(shí)現(xiàn) mykill
1 #include <cstdlib>
2 #include <iostream>
3 #include <string>
4 #include <signal.h>
5 using namespace std;
6
7 void Usage(string proc)
8 {
9 // 打印使用信息
10 cout << "\tUsage: \n\t";
11 cout << proc << " 信號(hào)編號(hào) 目標(biāo)進(jìn)程" << endl;
12 exit(2);
13 }
14
15 int main(int argc, char *argv[])
16 {
17 // 參數(shù)個(gè)數(shù)要嚴(yán)格限制
18 if (argc != 3)
19 {
20 Usage(argv[0]);
21 }
22
23 //獲取兩個(gè)參數(shù)
24 int signo = atoi(argv[1]);
25 int pid = atoi(argv[2]);
26
27 //執(zhí)行信號(hào)發(fā)送
28 kill(pid, signo);
29
30 return 0;
31 }
3.3raise 函數(shù)
發(fā)送信號(hào)的還有一個(gè)?raise
?函數(shù),這個(gè)函數(shù)比較奇怪,只能?自己給自己發(fā)信號(hào)
返回值:成功返回?0
,失敗返回?非0
就只有一個(gè)參數(shù):待發(fā)送的信號(hào)
可以這樣理解:raise
?是對(duì)?kill
?函數(shù)的封裝,每次傳遞的都是自己的?PID
3.4 abort 函數(shù)
abort
?是?C
?語(yǔ)言提供的一個(gè)函數(shù),它的作用是?給自己發(fā)送?6
?號(hào)?SIGABRT
?信號(hào)
沒(méi)有返回值,也沒(méi)有參數(shù)
值得一提的是,abort
?函數(shù)即使在修改執(zhí)行動(dòng)作后,最后仍然會(huì)發(fā)送?6
?號(hào)信號(hào)
同樣是終止進(jìn)程,C
語(yǔ)言 還提供了一個(gè)更好用的函數(shù):exit()(頭文件<cstdlib.h>)
,所以?abort
?用的比較少,了解即可
總的來(lái)說(shuō),系統(tǒng)調(diào)用中舉例的這三個(gè)函數(shù)關(guān)系是:kill
?包含?raise
,raise
?包含?abort
,作用范圍是在逐漸縮小的
四、軟件條件
信號(hào)產(chǎn)生(發(fā)送)的第三種方式:軟件條件
其實(shí)這種方式我們之前就接觸過(guò)了:管道讀寫時(shí),如果讀端關(guān)閉,那么操作系統(tǒng)會(huì)發(fā)送信號(hào)終止寫端,這個(gè)就是 軟件條件 引發(fā)的信號(hào)發(fā)送,發(fā)出的是?13
?號(hào)?SIGPIPE
?信號(hào)
五、硬件異常
最后一種產(chǎn)生(發(fā)送)信號(hào)的方式是:硬件異常
所謂?硬件異常?其實(shí)就是我們?cè)趯懗绦蜃畛S龅降母鞣N報(bào)錯(cuò),比如?除 0、野指針
5.1 除 0 導(dǎo)致異常
先來(lái)看一段簡(jiǎn)單的錯(cuò)誤代碼
#include <iostream>
using namespace std;
int main()
{
int n = 10;
n /= 0;
return 0;
}
顯然是會(huì)報(bào)錯(cuò)的是,畢竟?0
?不能作為常數(shù)
讓我們通過(guò)?signal
?更改?8
?號(hào)信號(hào)的執(zhí)行動(dòng)作,嘗試逆天改命,讓?除 0 合法?
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
void handler(int signo)
{
cout << "雖然除 0 了,但我不終止進(jìn)程" << endl;
}
int main()
{
signal(SIGFPE, handler);
int n = 10;
n /= 0;
return 0;
}
結(jié)果:一直在死循環(huán)似的發(fā)送信號(hào),明明只發(fā)生了一次 除 0 行為
想要明白背后的原理,需要先認(rèn)識(shí)一下?狀態(tài)寄存器
5.2 狀態(tài)寄存器
????????在?CPU
?中,存在很多?寄存器,其中大部分主要用來(lái)存儲(chǔ)數(shù)據(jù)信息,用于運(yùn)算,除此之外,還存在一種特殊的?寄存器?=》?狀態(tài)寄存器,這個(gè)?寄存器?專門用來(lái)檢測(cè)當(dāng)前進(jìn)程是否出現(xiàn)錯(cuò)誤行為,如果有,就會(huì)把?狀態(tài)寄存器(位圖結(jié)構(gòu))中對(duì)應(yīng)的比特位置?1
,意味著出現(xiàn)了 異常
????????當(dāng)操作系統(tǒng)檢測(cè)到?狀態(tài)寄存器?出現(xiàn)異常時(shí),會(huì)根據(jù)其中的值,向出現(xiàn)異常的進(jìn)程?輪詢式?的發(fā)送信號(hào),目的就是讓進(jìn)程退出
????????比如上面的?除 0 代碼,發(fā)生異常后,CPU
?將?狀態(tài)寄存器?修改,變成?異常狀態(tài),操作系統(tǒng)檢測(cè)到?異常?后會(huì)向進(jìn)程發(fā)送?8
?號(hào)信號(hào),即使我們修改了?8
?號(hào)信號(hào)的執(zhí)行動(dòng)作,但?因?yàn)闋顟B(tài)寄存器仍然處于異常狀態(tài),所以操作系統(tǒng)才會(huì)不斷發(fā)送?8
?號(hào)信號(hào),所以才會(huì)死循環(huán)式的打印
能讓?狀態(tài)寄存器?變?yōu)?異常?的都不是小問(wèn)題,需要立即終止進(jìn)程,然后尋找、解決問(wèn)題
畢竟如果讓?除 0?變?yōu)楹戏?,那最終的結(jié)果是多少呢?所以操作系統(tǒng)才會(huì)不斷發(fā)送信號(hào),目的就是?終止進(jìn)程的運(yùn)行
5.3 野指針導(dǎo)致異常信號(hào)
除了?除 0?異常外,還有一個(gè)?臭名昭著?的異常:野指針問(wèn)題
#include <iostream>
using namespace std;
int main()
{
int* ptr = nullptr;
*ptr = 10;
return 0;
}
那么?野指針?問(wèn)題是如何引發(fā)的呢?
野指針問(wèn)題主要分為兩類:
- 指向不該指向的空間
- 權(quán)限不匹配,比如只讀的區(qū)域,偏要去寫
共識(shí):在執(zhí)行?*ptr = 10
?這句代碼時(shí),首先會(huì)進(jìn)行 虛擬地址 -> 真實(shí)(物理)地址 之間的轉(zhuǎn)換
指向不該指向的空間:這很好理解,就是頁(yè)表沒(méi)有將?這塊虛擬地址空間 與 真實(shí)(物理)地址空間 建立映射關(guān)系,此時(shí)進(jìn)行訪問(wèn)時(shí)?MMU
?識(shí)別到異常,于是?MMU
?直接報(bào)錯(cuò),操作系統(tǒng)識(shí)別到?MMU
?異常后,向?qū)?yīng)的進(jìn)程發(fā)出終止信號(hào)
權(quán)限不匹配:頁(yè)表中除了保存映射關(guān)系外,還會(huì)保存該區(qū)域的權(quán)限情況,比如?是否命中 / RW?
等權(quán)限,當(dāng)發(fā)生操作與權(quán)限不匹配時(shí),比如?nullptr
?只允許讀取,并不允許其他行為,此時(shí)解引用就會(huì)觸發(fā)?MMU
?異常,操作系統(tǒng)識(shí)別到后,同樣會(huì)對(duì)對(duì)應(yīng)的進(jìn)程發(fā)出終止信號(hào)
注:MMU
?是內(nèi)存管理單元,主要負(fù)責(zé) 虛擬地址 與 物理地址 間的轉(zhuǎn)換工作,同時(shí)還會(huì)識(shí)別各種異常行為
一旦引發(fā)硬件層面的問(wèn)題,操作系統(tǒng)會(huì)直接發(fā)信號(hào),立即終止進(jìn)程
到目前為止,我們學(xué)習(xí)了很多信號(hào),分別對(duì)應(yīng)著不同的情況,其中有些信號(hào)還反映了異常信息,所以將信號(hào)進(jìn)行細(xì)分,還是很有必要的
六、核心轉(zhuǎn)儲(chǔ)
Linux
?中提供了一種系統(tǒng)級(jí)別的能力,當(dāng)一個(gè)進(jìn)程在出現(xiàn)異常的時(shí)候,OS
?可以將該進(jìn)程在異常的時(shí)候,核心代碼部分進(jìn)行?核心轉(zhuǎn)儲(chǔ),將內(nèi)存中進(jìn)程的相關(guān)數(shù)據(jù),全部?dump
?到磁盤中,一般會(huì)在當(dāng)前進(jìn)程的運(yùn)行目錄下,形成?core.pid
?這樣的二進(jìn)制文件(核心轉(zhuǎn)儲(chǔ)?文件)
6.1 核心轉(zhuǎn)儲(chǔ)的概念
對(duì)于某些信號(hào)來(lái)說(shuō),當(dāng)終止進(jìn)程后,需要進(jìn)行?core dump
,產(chǎn)生核心轉(zhuǎn)儲(chǔ)文件
比如:3號(hào) SIGQUIT
、4號(hào) SIGILL
、5號(hào) SIGTRAP
、6號(hào) SIGABRT
、7號(hào) SIGBUS
、8號(hào) SIGFPE
、11號(hào) SIGSEGV
、24號(hào) SIGXCPU
、25號(hào) SIGXFSZ
、31號(hào) SIGSYS
?都是可以產(chǎn)生核心轉(zhuǎn)儲(chǔ)文件的
不同信號(hào)的動(dòng)作(
Action
)
Trem
?-> 單純終止進(jìn)程Core
?-> 先發(fā)生核心轉(zhuǎn)儲(chǔ),生成核心轉(zhuǎn)儲(chǔ)文件(前提是此功能已打開(kāi)),再終止進(jìn)程
但在前面的學(xué)習(xí)中,我們用過(guò)?3
、6
、8
、11
?號(hào)信號(hào),都沒(méi)有發(fā)現(xiàn)?核心轉(zhuǎn)儲(chǔ)?文件啊
難道是我們的環(huán)境有問(wèn)題嗎?
確實(shí),當(dāng)前環(huán)境確實(shí)有問(wèn)題,因?yàn)樗?云服務(wù)器,而?云服務(wù)器?中默認(rèn)是關(guān)閉核心轉(zhuǎn)儲(chǔ)功能的
6.2 打開(kāi)與關(guān)閉核心轉(zhuǎn)儲(chǔ)
通過(guò)指令?ulimit -a
?查看當(dāng)前系統(tǒng)中的資源限制情況
可以看到,當(dāng)前系統(tǒng)中的核心轉(zhuǎn)儲(chǔ)文件大小為?0
,即不生成核心轉(zhuǎn)儲(chǔ)文件
通過(guò)指令手動(dòng)設(shè)置核心轉(zhuǎn)儲(chǔ)文件大小
ulimit -c 1024
現(xiàn)在可以生成核心轉(zhuǎn)儲(chǔ)文件了
6.3 核心轉(zhuǎn)儲(chǔ)的作用
如此大的核心轉(zhuǎn)儲(chǔ)文件有什么用呢?
答案是?調(diào)試
沒(méi)錯(cuò),核心轉(zhuǎn)儲(chǔ)文件可以調(diào)試,并且直接從出錯(cuò)的地方開(kāi)始調(diào)試
這種調(diào)試方式叫做?事后調(diào)試
調(diào)試方法:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-832939.html
gcc / g++
?編譯時(shí)加上?-g
?生成可調(diào)試文件- 運(yùn)行程序,生成?
core-dump
?文件 gdb 程序
?進(jìn)入調(diào)試模式core-file core.file
?利用核心轉(zhuǎn)儲(chǔ)文件,快速定位至出錯(cuò)的地方
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-832939.html
到了這里,關(guān)于Linux進(jìn)程(一)信號(hào)-----信號(hào)產(chǎn)生的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!