?個(gè)人主頁(yè): 北 海
??所屬專欄: Linux學(xué)習(xí)之旅
??操作環(huán)境: CentOS 7.6 阿里云遠(yuǎn)程服務(wù)器
??前言
在 Linux
中,進(jìn)程具有獨(dú)立性,進(jìn)程在運(yùn)行后可能 “放飛自我”,這是不利于管理的,于是需要一種約定俗成的方式來控制進(jìn)程的運(yùn)行,這就是 進(jìn)程信號(hào)
,本文將會(huì)從什么是進(jìn)程信號(hào)開篇,講述各種進(jìn)程信號(hào)的產(chǎn)生方式及作用
不同的信號(hào)指示燈代表著不同的執(zhí)行動(dòng)作
???正文
1、進(jìn)程信號(hào)基本概念
1.1、什么是信號(hào)?
信號(hào) 是信息傳遞的承載方式,一種信號(hào)往往代表著一種執(zhí)行動(dòng)作,比如:
- 雞叫 => 天快亮了
- 鬧鐘 => 起床、完成任務(wù)
- 紅綠燈 => 紅燈停,綠燈行
- ……
當(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)槭苓^教育,學(xué)習(xí)了執(zhí)行動(dòng)作,但對(duì)進(jìn)程來說,它可沒有接受過九年義務(wù)教育,也不知道什么時(shí)候該干什么事
于是程序員們給操作系統(tǒng)植入了一批 指令,一個(gè)指令表示一種特殊動(dòng)作,而這些指令就是 信號(hào)(進(jìn)程信號(hào))
通過 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);剩下的 34~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)
實(shí)時(shí)操作系統(tǒng)
普通信號(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)使用過 信號(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)功能是什么呢?
- 可以通過
man 7 signal
進(jìn)行查詢
man 7 signal
簡(jiǎn)單總結(jié)一下,1~31
號(hào)信號(hào)對(duì)應(yīng)的功能如下(表格內(nèi)容引用自 2021dragon
Linux中的31個(gè)普通信號(hào))
信號(hào)編號(hào) | 信號(hào)名 | 功能 |
---|---|---|
1 | SIGHUP |
如果終端接口檢測(cè)到一個(gè)連接斷開,則會(huì)將此信號(hào)發(fā)送給與該終端相關(guān)的控制進(jìn)程,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程 |
2 | SIGINT |
當(dāng)用戶按組合鍵(一般采用 Ctrl + C )時(shí),終端驅(qū)動(dòng)程序產(chǎn)生此信號(hào)并發(fā)送至前臺(tái)進(jìn)程組中的每一個(gè)進(jìn)程,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程 |
3 | SIGQUIT |
當(dāng)用戶按組合鍵(一般采用 Ctrl + \ )時(shí),終端驅(qū)動(dòng)程序產(chǎn)生此信號(hào)并發(fā)送至前臺(tái)進(jìn)程組中的每一個(gè)進(jìn)程,該信號(hào)不僅終止前臺(tái)進(jìn)程組,同時(shí)會(huì)產(chǎn)生一個(gè) core 文件 |
4 | SIGILL |
此信號(hào)表示進(jìn)程已執(zhí)行一條非法指令,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程,同時(shí)產(chǎn)生一個(gè) core 文件 |
5 | SIGTRAP |
該信號(hào)由斷點(diǎn)指令或其他 trap 指令產(chǎn)生,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程,同時(shí)會(huì)產(chǎn)生一個(gè) core 文件 |
6 | SIGABRT |
調(diào)用 abort 函數(shù)是產(chǎn)生此信號(hào),進(jìn)程異常終止,同時(shí)會(huì)產(chǎn)生一個(gè) core 文件 |
7 | SIGBUS |
當(dāng)出現(xiàn)某些類型的內(nèi)存故障時(shí),常常產(chǎn)生該信號(hào),,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程,同時(shí)產(chǎn)生一個(gè) core 文件 |
8 | SIGFPE |
此信號(hào)表示一個(gè)算術(shù)運(yùn)算異常,比如除0、浮點(diǎn)溢出等,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程,同時(shí)產(chǎn)生一個(gè) core 文件 |
9 | SIGKILL |
該信號(hào)不能被捕捉或忽略,它向系統(tǒng)管理員提供了一種可以殺死任一進(jìn)程的可靠方法 |
10 | SIGUSR1 |
這是一個(gè)用戶定義的信號(hào),即程序員可以在程序中定義并使用該信號(hào),該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程 |
11 | SIGSEGV |
指示進(jìn)程進(jìn)行了一次無(wú)效的內(nèi)存訪問(比如訪問了一個(gè)未初始化的指針),該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程并產(chǎn)生一個(gè) core 文件 |
12 | SIGUSR2 |
這是另一個(gè)用戶定義的信號(hào),與 SIGUSR1 相似,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程 |
13 | SIGPIPE |
如果在管道的讀進(jìn)程已終止時(shí)對(duì)管道進(jìn)行寫入操作,則會(huì)收到此信號(hào),該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程 |
14 | SIGALRM |
當(dāng)用 alarm 函數(shù)設(shè)置的定時(shí)器超時(shí)時(shí)產(chǎn)生此信號(hào),或由 setitimer 函數(shù)設(shè)置的間隔時(shí)間已經(jīng)超時(shí)時(shí)也產(chǎn)生會(huì)此信號(hào) |
15 | SIGTERM |
該信號(hào)是由應(yīng)用程序捕獲的,使用該信號(hào)讓程序有機(jī)會(huì)在退出之前做好清理工作。與 SIGKILL 信號(hào)不同的是,該信號(hào)可以被捕捉或忽略,通常用來表示程序正常退出 |
16 | SIGSTKFLT |
該信號(hào)指示協(xié)處理器上的堆棧故障(未使用),該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程 |
17 | SIGCHLD |
在一個(gè)進(jìn)程終止或停止時(shí),SIGCHLD 信號(hào)被發(fā)送給其父進(jìn)程。按系統(tǒng)默認(rèn),將忽略此信號(hào)。如果父進(jìn)程希望被告知其子進(jìn)程的這種狀態(tài)改變,則應(yīng)捕捉此信號(hào)。信號(hào)捕捉函數(shù)中通常要調(diào)用一種 wait 函數(shù)以取得子進(jìn)程 PID 及其終止?fàn)顟B(tài) |
18 | SIGCONT |
可以通過發(fā)送該信號(hào)讓一個(gè)停止的進(jìn)程繼續(xù)運(yùn)行 |
19 | SIGSTOP |
這時(shí)一個(gè)作業(yè)控制信號(hào),該信號(hào)用于停止一個(gè)進(jìn)程,類似于交互停止信號(hào)( SIGTSTP ),但是該信號(hào)不能被捕捉或忽略 |
20 | SIGTSTP |
交互停止信號(hào),當(dāng)用戶按組合鍵(一般采用 Ctrl+Z )時(shí),終端驅(qū)動(dòng)程序產(chǎn)生此信號(hào)并發(fā)送至前臺(tái)進(jìn)程組中的每一個(gè)進(jìn)程 |
21 | SIGTTIN |
后臺(tái)進(jìn)程讀終端控制臺(tái)時(shí),由終端驅(qū)動(dòng)程序產(chǎn)生此信號(hào)并發(fā)送給該后臺(tái)進(jìn)程,該信號(hào)的默認(rèn)處理動(dòng)作是暫停進(jìn)程 |
22 | SIGTTOU |
后臺(tái)進(jìn)程向終端控制臺(tái)輸出數(shù)據(jù),由終端驅(qū)動(dòng)程序產(chǎn)生此信號(hào)并發(fā)送給該后臺(tái)進(jìn)程,該信號(hào)的默認(rèn)處理動(dòng)作是暫停進(jìn)程 |
23 | SIGURG |
套接字上有緊急數(shù)據(jù)時(shí),向當(dāng)前正在運(yùn)行的進(jìn)程發(fā)出此信號(hào),報(bào)告有緊急數(shù)據(jù)到達(dá),該信號(hào)的默認(rèn)處理動(dòng)作是忽略 |
24 | SIGXCPU |
進(jìn)程執(zhí)行時(shí)間超過了分配給該進(jìn)程的 CPU 時(shí)間,系統(tǒng)產(chǎn)生該信號(hào)并發(fā)送給該進(jìn)程,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程,同時(shí)會(huì)產(chǎn)生一個(gè) core 文件 |
25 | SIGXFSZ |
如果進(jìn)程寫文件時(shí)超過了文件的最大長(zhǎng)度設(shè)置,則會(huì)收到該信號(hào),該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程,同時(shí)會(huì)產(chǎn)生一個(gè) core 文件 |
26 | SIGVTALRM |
虛擬時(shí)鐘超時(shí)時(shí)產(chǎn)生該信號(hào),與 SIGALRM 信號(hào)類似,但是該信號(hào)只計(jì)算該進(jìn)程占用 CPU 的使用時(shí)間,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程 |
27 | SIGPROF |
該信號(hào)類似與 SIGVTALRM ,它不僅包括該進(jìn)程占用 CPU 的時(shí)間還包括執(zhí)行系統(tǒng)調(diào)用的時(shí)間,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程 |
28 | SIGWINCH |
當(dāng)窗口大小發(fā)生變化時(shí),內(nèi)核會(huì)將該信號(hào)發(fā)送至前臺(tái)進(jìn)程組,該信號(hào)的默認(rèn)處理動(dòng)作是忽略 |
29 | SIGIO |
此信號(hào)指示一個(gè)異步 I/O 事件,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程 |
30 | SIGPWR |
電源故障,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程 |
31 | SIGSYS |
該信號(hào)指示一個(gè)無(wú)效的系統(tǒng)調(diào)用,該信號(hào)的默認(rèn)處理動(dòng)作是終止進(jìn)程,同時(shí)會(huì)產(chǎn)生一個(gè) core 文件 |
注意:其中的 9
號(hào) 和 19
號(hào)信號(hào)是非常特殊的,不能修改其默認(rèn)動(dòng)作
1.3、信號(hào)的基本認(rèn)知
進(jìn)程信號(hào)由 信號(hào)編號(hào) + 執(zhí)行動(dòng)作 構(gòu)成,一個(gè)信號(hào)對(duì)應(yīng)一種動(dòng)作,對(duì)于進(jìn)程來說,動(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ì)于問題分析是非常不友好的,所以才會(huì)將信號(hào)細(xì)分化,搞出這么多信號(hào),目的就是為了方便定位、分析、解決問題
- 并且 普通信號(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)作(后面會(huì)專門講信號(hào)處理相關(guān)內(nèi)容)
信號(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ú)信息
//信號(hào)結(jié)構(gòu)體源碼(部分)
struct signal_struct {
atomic_t sigcnt;
atomic_t live;
int nr_threads;
wait_queue_head_t wait_chldexit; /* for wait4() */
/* current thread group signal load-balancing target: */
struct task_struct *curr_target;
/* shared signal handling: */
struct sigpending shared_pending;
/* thread group exit support */
int group_exit_code;
/* overloaded:
* - notify group_exit_task when ->count is equal to notify_count
* - everyone except group_exit_task is stopped during signal delivery
* of fatal signals, group_exit_task processes the signal.
*/
int notify_count;
struct task_struct *group_exit_task;
/* thread group stop support, overloads group_exit_code too */
int group_stop_count;
unsigned int flags; /* see SIGNAL_* flags below */
/*
* PR_SET_CHILD_SUBREAPER marks a process, like a service
* manager, to re-parent orphan (double-forking) child processes
* to this process instead of 'init'. The service manager is
* able to receive SIGCHLD signals and is able to investigate
* the process until it calls wait(). All children of this
* process will inherit a flag if they should look for a
* child_subreaper process at exit.
*/
unsigned int is_child_subreaper:1;
unsigned int has_child_subreaper:1;
//……
};
下面對(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)程來說是異步的,隨時(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)生 部分相關(guān)知識(shí),下面正式開始學(xué)習(xí) 信號(hào)產(chǎn)生
===== 信號(hào)產(chǎn)生的方式 =====
2、鍵盤鍵入
信號(hào)產(chǎn)生(發(fā)送)的第一種方式:鍵盤鍵入
通俗來說就是命令行操作
2.1、ctrl+c 終止前臺(tái)進(jìn)程
系統(tǒng)卡死遇到過吧?程序死循環(huán)遇到過吧?這些都是比較常見的問題,當(dāng)發(fā)生這些問題時(shí),我們可以通過 鍵盤鍵入 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)呢?
證明自有方法,前面說過,一個(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ú)法終止的,可以通過kill -9 PID
發(fā)出9
信號(hào)終止它
2.1.1、signal 注冊(cè)執(zhí)行動(dòng)作
signal
函數(shù)可以用來 修改信號(hào)的執(zhí)行動(dòng)作,也叫注冊(cè)自定義執(zhí)行動(dòng)作
signal
調(diào)用成功返回上一個(gè)執(zhí)行方法的值(其實(shí)就是下標(biāo),后面介紹),失敗則返回 SIG_ERR
,并設(shè)置錯(cuò)誤碼
返回值可以不用關(guān)注,重點(diǎn)在于 signal
的參數(shù)
- 參數(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
失效后,可以通過 ctrl + \
終止進(jìn)程,發(fā)出的是 3
號(hào)信號(hào)(3
號(hào)信號(hào)在發(fā)出后,會(huì)生成 核心轉(zhuǎn)儲(chǔ) 文件)
普通信號(hào)只有 31
個(gè),如果把所有普通信號(hào)的執(zhí)行動(dòng)作都改了,會(huì)發(fā)生什么呢?難道會(huì)得到一個(gè)有著 金剛不壞 之身的進(jìn)程嗎?
#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()
{
//給所有普通信號(hào)注冊(cè)新方法
for(int i = 1; i < 32; i++)
signal(i, handler);
while(true)
{
cout << "我是一個(gè)進(jìn)程,我正在運(yùn)行…… PID: " << getpid() << endl;
sleep(1);
}
return 0;
}
大部分信號(hào)的執(zhí)行動(dòng)作都被修改了,但 9
號(hào)信號(hào)沒有,因?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)過特殊設(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ì)一相連,并通過 中斷控制器(比如 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í)行方法,通過鍵盤的讀取方法,讀取到 ctrl + c
這個(gè)信息,轉(zhuǎn)化后,就是 2
號(hào)信號(hào),執(zhí)行終止前臺(tái)進(jìn)程的動(dòng)作
鍵盤被按下 和 鍵盤哪些位置被按下 是不一樣的
- 首先鍵盤先按下,
CPU
確定對(duì)應(yīng)的讀取方法 - 其次才是通過
讀取方法
從鍵盤中讀取數(shù)據(jù)
注:鍵盤讀取方法如何進(jìn)行讀取,這是驅(qū)動(dòng)的事,我們不用關(guān)心
硬件中斷 的流程與 進(jìn)程信號(hào) 的流程雷同,同樣是 先檢測(cè)到信號(hào),然后再去執(zhí)行相應(yīng)的動(dòng)作,不過此時(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) 與 硬件 間的解耦
3、系統(tǒng)調(diào)用
除了可以通過 鍵盤鍵入 發(fā)送信號(hào)外,還可以通過直接調(diào)用 系統(tǒng)接口 發(fā)送信號(hào),畢竟 bash
也是一個(gè)進(jìn)程,本質(zhì)上就是在進(jìn)行程序替換而已
3.1、kill 函數(shù)
信號(hào)的發(fā)送主要是通過 kill
函數(shù)進(jìn)行發(fā)送
返回值:成功返回 0
,失敗返回 -1
并設(shè)置錯(cuò)誤碼
參數(shù)1:待操作進(jìn)程的 PID
參數(shù)2:待發(fā)送的信號(hào)
下面來簡(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
這里就直接利用 命令行參數(shù) 簡(jiǎn)單實(shí)現(xiàn)了
#include <iostream>
#include <string>
#include <signal.h>
using namespace std;
void Usage(string proc)
{
// 打印使用信息
cout << "\tUsage: \n\t";
cout << proc << " 信號(hào)編號(hào) 目標(biāo)進(jìn)程" << endl;
exit(2);
}
int main(int argc, char *argv[])
{
// 參數(shù)個(gè)數(shù)要嚴(yán)格限制
if (argc != 3)
{
Usage(argv[0]);
}
//獲取兩個(gè)參數(shù)
int signo = atoi(argv[1]);
int pid = atoi(argv[2]);
//執(zhí)行信號(hào)發(fā)送
kill(pid, signo);
return 0;
}
下面隨便跑一個(gè)進(jìn)程,然后用自己寫的 myKill
命令給進(jìn)程發(fā)信號(hào)
我們可以把這個(gè)程序改造下,改成進(jìn)程替換的方式,讓后將自己寫的命令進(jìn)行安裝,就能像 kill
一樣直接使用了
3.3、raise 函數(shù)
發(fā)送信號(hào)的還有一個(gè) raise
函數(shù),這個(gè)函數(shù)比較奇怪,只能 自己給自己發(fā)信號(hào)
返回值:成功返回 0
,失敗返回 非0
就只有一個(gè)參數(shù):待發(fā)送的信號(hào)
可以這樣理解:raise
是對(duì) kill
函數(shù)的封裝,每次傳遞的都是自己的 PID
#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)
raise(SIGKILL); //自己殺死自己
}
return 0;
}
3.4、abort 函數(shù)
abort
是 C
語(yǔ)言提供的一個(gè)函數(shù),它的作用是 給自己發(fā)送 6
號(hào) SIGABRT
信號(hào)
沒有返回值,也沒有參數(shù)
值得一提的是,abort
函數(shù)即使在修改執(zhí)行動(dòng)作后,最后仍然會(huì)發(fā)送 6
號(hào)信號(hào)
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
void handler(int signo)
{
cout << "收到了 " << signo << " 號(hào)信號(hào),已執(zhí)行新動(dòng)作" << endl;
}
int main()
{
signal(6, handler);
// signal(SIGABRT, handler); //這種寫法也是可以的
int n = 1;
while (true)
{
cout << "我是一個(gè)進(jìn)程,已經(jīng)運(yùn)行了 " << n << " 秒 PID: " << getpid() << endl;
sleep(1);
n++;
if (n > 5)
abort();
}
return 0;
}
即使執(zhí)行了我們新注冊(cè)的方法,abort
最后仍然會(huì)發(fā)出 6
號(hào)信號(hào)終止進(jìn)程
同樣是終止進(jìn)程,C
語(yǔ)言 還提供了一個(gè)更好用的函數(shù):exit()
,所以 abort
用的比較少,了解即可
總的來說,系統(tǒng)調(diào)用中舉例的這三個(gè)函數(shù)關(guān)系是:kill
包含 raise
,raise
包含 abort
,作用范圍是在逐漸縮小的
4、軟件條件
信號(hào)產(chǎn)生(發(fā)送)的第三種方式:軟件條件
其實(shí)這種方式我們之前就接觸過了:管道讀寫時(shí),如果讀端關(guān)閉,那么操作系統(tǒng)會(huì)發(fā)送信號(hào)終止寫端,這個(gè)就是 軟件條件 引發(fā)的信號(hào)發(fā)送,發(fā)出的是 13
號(hào) SIGPIPE
信號(hào)
4.1、alarm 設(shè)置鬧鐘
系統(tǒng)為我們提供了 鬧鐘(報(bào)警):alarm
,這個(gè) 鬧鐘 可不是用來起床的,而是用來 定時(shí) 的
返回值:如果上一個(gè)鬧鐘還有剩余時(shí)間,則返回剩余時(shí)間,否則返回 0
參數(shù):想要設(shè)定的時(shí)間,單位是秒
當(dāng)時(shí)間到達(dá)鬧鐘中的預(yù)設(shè)時(shí)間時(shí),鬧鐘會(huì)響,并且發(fā)送 14
號(hào) SIGALRM
信號(hào)
比如這樣:
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
int main()
{
alarm(5); //設(shè)定一個(gè)五秒后的鬧鐘
int n = 1;
while (true)
{
cout << "我是一個(gè)進(jìn)程,已經(jīng)運(yùn)行了 " << n << " 秒 PID: " << getpid() << endl;
sleep(1);
n++;
}
return 0;
}
我們也可以更改 14
號(hào) SIGALRM
信號(hào)的執(zhí)行動(dòng)作,讓鬧鐘不斷響起(自舉)
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
void handler(int signo)
{
cout << "收到了 " << signo << " 號(hào)信號(hào),已執(zhí)行新動(dòng)作" << endl;
int n = alarm(10);
cout << "上一個(gè)鬧鐘剩余時(shí)間: " << n << endl;
}
int main()
{
signal(SIGALRM, handler);
alarm(10); //設(shè)定一個(gè)十秒后的鬧鐘
while(true)
{
cout << "我是一個(gè)進(jìn)程,我正在運(yùn)行…… PID: " << getpid() << endl;
sleep(1);
};
return 0;
}
系統(tǒng)中不止一個(gè)鬧鐘,所以 OS
需要 先描述,再組織,將這些鬧鐘管理起來
可以借助鬧鐘,簡(jiǎn)單測(cè)試一下當(dāng)前服務(wù)器的算力
4.2、測(cè)試算力
如何簡(jiǎn)單粗暴的測(cè)試算力? 設(shè)個(gè) 1
秒后響起的鬧鐘,看看程序能將一個(gè)值累加至多少
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
int main()
{
alarm(1); //設(shè)定一個(gè)一秒后的鬧鐘
int n = 0;
while(true)
{
cout << n++ << endl;
};
return 0;
}
這個(gè)云服務(wù)這么拉嗎?只能累加幾萬(wàn)次
其實(shí)不是,主要是因?yàn)楫?dāng)前程序涉及了 IO
,這是非常耗時(shí)間的,可以取消 IO
并修改 SIGALRM
的執(zhí)行動(dòng)作為打印變量,看看能累加多少次
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
int n = 0;
void handler(int signo)
{
cout << n << endl;
exit(1);
}
int main()
{
signal(SIGALRM, handler);
alarm(1); //設(shè)定一個(gè)一秒后的鬧鐘
while(true)
n++;
return 0;
}
可以看到,取消 IO
后,累加了 5
億多次,近 10000
倍的差距
通過這個(gè)簡(jiǎn)單的小程序證明了一件事:IO
是非常慢的,能不 IO
就不 IO
注:因?yàn)楫?dāng)前是云服務(wù)器,存在 網(wǎng)絡(luò)延遲 的影響,所以實(shí)際差異更大
注意:鬧鐘是一次性的,只能響一次
5、硬件異常
最后一種產(chǎn)生(發(fā)送)信號(hào)的方式是:硬件異常
所謂 硬件異常 其實(shí)就是我們?cè)趯懗绦蜃畛S龅降母鞣N報(bào)錯(cuò),比如 除 0、野指針
5.1、除 0 導(dǎo)致異常
先來看一段簡(jiǎn)單的錯(cuò)誤代碼
#include <iostream>
using namespace std;
int main()
{
int n = 10;
n /= 0;
return 0;
}
顯然是會(huì)報(bào)錯(cuò)的是,畢竟 0
不能作為常數(shù)
根據(jù)報(bào)錯(cuò)信息,可以推測(cè)出此時(shí)發(fā)送的是 8
號(hào) SIGFPE
信號(hào)(浮點(diǎn)異常)
讓我們通過 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
中,存在很多 寄存器,其中大部分主要用來存儲(chǔ)數(shù)據(jù)信息,用于運(yùn)算,除此之外,還存在一種特殊的 寄存器 =》 狀態(tài)寄存器,這個(gè) 寄存器 專門用來檢測(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)?異常 的都不是小問題,需要立即終止進(jìn)程,然后尋找、解決問題
畢竟如果讓 除 0 變?yōu)楹戏ǎ亲罱K的結(jié)果是多少呢?所以操作系統(tǒng)才會(huì)不斷發(fā)送信號(hào),目的就是 終止進(jìn)程的運(yùn)行
5.3、野指針導(dǎo)致異常
除了 除 0 異常外,還有一個(gè) 臭名昭著 的異常:野指針問題
比如:
#include <iostream>
using namespace std;
int main()
{
int* ptr = nullptr;
*ptr = 10;
return 0;
}
Segmentation fault
段錯(cuò)誤 這是每個(gè) C/C++
程序猿都會(huì)遇到的問題,因?yàn)樘菀子|發(fā)了,出現(xiàn)段錯(cuò)誤問題時(shí),操作系統(tǒng)會(huì)發(fā)送 11
號(hào) SIGSEGV
信號(hào)終止進(jìn)程,可以通過修改執(zhí)行動(dòng)作驗(yàn)證,這里不再演示
那么 野指針 問題是如何引發(fā)的呢?
借用一下 共享內(nèi)存 中的圖~
野指針問題主要分為兩類:
- 指向不該指向的空間
- 權(quán)限不匹配,比如只讀的區(qū)域,偏要去寫
共識(shí):在執(zhí)行 *ptr = 10
這句代碼時(shí),首先會(huì)進(jìn)行 虛擬地址 -> 真實(shí)(物理)地址 之間的轉(zhuǎn)換
指向不該指向的空間:這很好理解,就是頁(yè)表沒有將 這塊虛擬地址空間 與 真實(shí)(物理)地址空間 建立映射關(guān)系,此時(shí)進(jìn)行訪問時(shí) MMU
識(shí)別到異常,于是 MMU
直接報(bào)錯(cuò),操作系統(tǒng)識(shí)別到 MMU
異常后,向?qū)?yīng)的進(jìn)程發(fā)出終止信號(hào)
C
語(yǔ)言中對(duì)于越界 讀 的檢查不夠嚴(yán)格,屬于抽查行為,因此野指針越界讀還不一定報(bào)錯(cuò),但越界寫是一定會(huì)報(bào)錯(cuò)的
權(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)
頁(yè)表中的屬性
- 是否命中
RW
權(quán)限UK
權(quán)限(不必關(guān)心)
注:MMU
是內(nèi)存管理單元,主要負(fù)責(zé) 虛擬地址 與 物理地址 間的轉(zhuǎn)換工作,同時(shí)還會(huì)識(shí)別各種異常行為
一旦引發(fā)硬件層面的問題,操作系統(tǒng)會(huì)直接發(fā)信號(hào),立即終止進(jìn)程
到目前為止,我們學(xué)習(xí)了很多信號(hào),分別對(duì)應(yīng)著不同的情況,其中有些信號(hào)還反映了異常信息,所以將信號(hào)進(jìn)行細(xì)分,還是很有必要的
6、核心轉(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)來說,當(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ǔ)文件(前提是此功能已打開),再終止進(jìn)程
但在前面的學(xué)習(xí)中,我們用過 3
、6
、8
、11
號(hào)信號(hào),都沒有發(fā)現(xiàn) 核心轉(zhuǎn)儲(chǔ) 文件啊
難道是我們的環(huán)境有問題嗎?
確實(shí),當(dāng)前環(huán)境確實(shí)有問題,因?yàn)樗?云服務(wù)器,而 云服務(wù)器 中默認(rèn)是關(guān)閉核心轉(zhuǎn)儲(chǔ)功能的
6.2、打開與關(guān)閉核心轉(zhuǎn)儲(chǔ)
通過指令 ulimit -a
查看當(dāng)前系統(tǒng)中的資源限制情況
ulimit -a
可以看到,當(dāng)前系統(tǒng)中的核心轉(zhuǎn)儲(chǔ)文件大小為 0
,即不生成核心轉(zhuǎn)儲(chǔ)文件
通過指令手動(dòng)設(shè)置核心轉(zhuǎn)儲(chǔ)文件大小
ulimit -c 1024
現(xiàn)在可以生成核心轉(zhuǎn)儲(chǔ)文件了
就拿之前的 野指針 代碼測(cè)試,因?yàn)樗l(fā)送的是 11
號(hào)信號(hào),會(huì)產(chǎn)生 core dump
文件
核心轉(zhuǎn)儲(chǔ)文件是很大的,而有很多信號(hào)都會(huì)產(chǎn)生核心轉(zhuǎn)儲(chǔ)文件,所以云服務(wù)器一般默認(rèn)是關(guān)閉的
云服務(wù)器上是可以部署服務(wù)的,一般程序發(fā)生錯(cuò)誤后,會(huì)立即重啟
如果打開了核心轉(zhuǎn)儲(chǔ),一旦程序 不斷掛掉、又不斷重啟,那么必然會(huì)產(chǎn)生大量的核心轉(zhuǎn)儲(chǔ)文件,當(dāng)文件足夠多時(shí),磁盤被擠滿,導(dǎo)致系統(tǒng)IO
異常,最終會(huì)導(dǎo)致整個(gè)服務(wù)器掛掉的
還有一個(gè)重要問題是core
文件中可能包含用戶密碼等敏感信息,不安全
關(guān)閉核心轉(zhuǎn)儲(chǔ)很簡(jiǎn)單,設(shè)置為 0
就好了
ulimit -c 0
6.3、核心轉(zhuǎn)儲(chǔ)的作用
如此大的核心轉(zhuǎn)儲(chǔ)文件有什么用呢?
答案是 調(diào)試
沒錯(cuò),核心轉(zhuǎn)儲(chǔ)文件可以調(diào)試,并且直接從出錯(cuò)的地方開始調(diào)試
這種調(diào)試方式叫做 事后調(diào)試
調(diào)試方法:
gcc / g++
編譯時(shí)加上-g
生成可調(diào)試文件- 運(yùn)行程序,生成
core-dump
文件 gdb 程序
進(jìn)入調(diào)試模式core-file core.file
利用核心轉(zhuǎn)儲(chǔ)文件,快速定位至出錯(cuò)的地方
之前在 進(jìn)程創(chuàng)建、控制、等待 中,我們談到了 當(dāng)進(jìn)程異常退出時(shí)(被信號(hào)終止),不再設(shè)置退出碼,而是設(shè)置 core dump
位 及 終止信號(hào)
也就是說,父進(jìn)程可以借此判斷子進(jìn)程是否產(chǎn)生了 核心轉(zhuǎn)儲(chǔ) 文件
??總結(jié)
以上就是本次關(guān)于 Linux進(jìn)程信號(hào)【信號(hào)產(chǎn)生】的全部?jī)?nèi)容了,作為進(jìn)程信號(hào)系列的開篇之作,包含了很多內(nèi)容,首先是對(duì)信號(hào)的產(chǎn)生、保存、處理相關(guān)概念進(jìn)行了學(xué)習(xí),然后針對(duì)信號(hào)產(chǎn)生,闡述了四種不同的方式,最后學(xué)習(xí)了核心轉(zhuǎn)儲(chǔ)的相關(guān)概念,掌握了一種特殊的調(diào)試方式
相關(guān)文章推薦 Linux進(jìn)程間通信 ===== :>
【消息隊(duì)列、信號(hào)量】、【共享內(nèi)存】、【命名管道】、【匿名管道】
Linux基礎(chǔ)IO ===== :>
【軟硬鏈接與動(dòng)靜態(tài)庫(kù)】、【深入理解文件系統(tǒng)】、【模擬實(shí)現(xiàn)C語(yǔ)言文件流】、【重定向及緩沖區(qū)理解】、【文件理解與操作】
Linux進(jìn)程控制 ===== :>
【簡(jiǎn)易版bash】、【進(jìn)程程序替換】、【創(chuàng)建、終止、等待】
Linux進(jìn)程學(xué)習(xí) ===== :>
【進(jìn)程地址】、【環(huán)境變量】、【進(jìn)程狀態(tài)】、【基本認(rèn)知】
Linux基礎(chǔ) ===== :>文章來源:http://www.zghlxwxcb.cn/news/detail-500602.html
【gdb】、【git】、【gcc/g++】、【vim】、Linux 權(quán)限理解和學(xué)習(xí)、聽說Linux基礎(chǔ)指令很多?這里都幫你總結(jié)好了文章來源地址http://www.zghlxwxcb.cn/news/detail-500602.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)!