国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

這篇具有很好參考價值的文章主要介紹了【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

「前言」文章是關(guān)于Linux進程信號方面的知識,本文的內(nèi)容是Linux進程信號第一講,講解會比較細,下面開始!

「歸屬專欄」Linux系統(tǒng)編程

「主頁鏈接」個人主頁

「筆者」楓葉先生(fy)

?「楓葉先生有點文青病」「每篇一句」?

人生天地間,忽如遠行客。
——《樂府·青青陵上柏》

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

目錄

一、認識信號

1.1 生活中的信號

1.2 將1.1的概念遷移到進程

1.3?信號概念

1.4 查看系統(tǒng)定義信號列表

1.5?man 7 signal

1.6 解釋1.2的代碼樣例

1.7 信號處理常見方式概覽

二、產(chǎn)生信號

2.1 signal函數(shù)

2.2?通過終端按鍵產(chǎn)生信號

2.3?調(diào)用系統(tǒng)函數(shù)向進程發(fā)信號

2.3.1 kill函數(shù)

2.3.2?raise函數(shù)

2.3.3?abort函數(shù)

2.4 硬件異常產(chǎn)生信號

2.4.1 除0操作產(chǎn)生的異常?

2.4.2 空指針異常

2.5?由軟件條件產(chǎn)生信號


一、認識信號

1.1 生活中的信號

日常生活中,常見的信號有:發(fā)令槍、紅綠燈、消息提醒、電話鈴聲、鬧鐘等等,以快遞為例。

  1. 你在網(wǎng)上買了很多件商品,再等待不同商品快遞的到來。但即便快遞沒有到來,你也知道快遞來臨時,你該怎么處理快遞。也就是你能“識別快遞”;
  2. 識別快遞包含了兩個信息:1、認識:你是認識 “快遞” 的? 2、行為產(chǎn)生:“快遞” 到來,你會產(chǎn)生相應(yīng)的行為;
  3. 當 “快遞” 到來,快遞小哥給你打電話,但是你正在打游戲,需5min之后才能去 “取快遞”。那么在在這5min之內(nèi),你并沒有去取快遞,但是你是知道有快遞到來了。也就是 ”取快遞” 的行為并不是一定要立即執(zhí)行,可以理解成 “在合適的時候去取”;
  4. 在收到通知,再到你拿到快遞期間,是有一個時間窗口的,在這段時間,你并沒有拿到快遞,但是你知道有一個快遞已經(jīng)來了。本質(zhì)上是你 “記住了有一個快遞要去取”;
  5. 當你時間合適,順利拿到快遞之后,就要開始處理快遞了。而處理快遞一般方式有三種:1. 執(zhí)行默認動作(打開快遞,使用商品)2. 執(zhí)行自定義動作(快遞是零食,你要送給你的女朋友)3. 忽略快遞(快遞拿上來之后,放在一旁,繼續(xù)開一把游戲);
  6. 快遞到來的整個過程,對你來講是異步的,你不能準確斷定快遞員什么時候給你打電話。

快遞便可視為信號,對信號的處理大致分以下三步:

  1. 識別信號:你認識這個信號,信號到來你會產(chǎn)生相應(yīng)的行為;
  2. 信號到來(即將處理信號):不一定立即處理信號,也可以在某個合適時間進行處理信號,但是必須要把這個信號記住;
  3. 拿到信號(處理信號):拿到信號后分三種行為 (1、默認動作:拿到信號后使用信號? 2、忽略動作:拿到信號后忽略信號,什么事也不干? 3、自定義動作:拿到信號去干別的事)

解釋異步概念:

以生活例子為例:你在煮著面條。當你在煮面條時,你可以同時做其他事情,例如準備醬料或者切菜。你不需要一直站在爐子旁邊等待面條煮熟,而是可以在面條煮熟之前做其他事情,這就是異步;

而同步則是:你必須等待面條煮熟了,你才能干其他事,這是同步。

以信號為例:假設(shè)你正在等待信號的到來。在同步中,你會等待信號到達后再繼續(xù)下一個任務(wù),這意味著你會阻塞其他任務(wù)直到信號到達。

另一方面,在異步中,你不會等待信號到達,這意味著在等待信號到來的時可以做其他任務(wù)。

1.2 將1.1的概念遷移到進程

首先知道,信號是給進程發(fā)送的,比如我們之前的 kill -9 ,給進程發(fā)送9號信號終止進程。

  • 進程是如何識別信號:也是 認識信號 + 行為動作(進程之所以能夠認識信號,是因為程序員將對應(yīng)的信號種類和邏輯已經(jīng)寫好了);
  • 進程收到信號時:進程不一定立即處理這個信號,進程可能干著其他更重要的事情;
  • 進程立即處理 或 不一定立即處理這個信號的時候,進程本身必須要有對信號的保存能力;
  • 進程處理信號的時候,一般有三個動作(默認、忽略、自定義),進程處理信號稱為信號被捕捉。

進程本身必須要有對信號的保存能力,信號保存在哪里?答案是保存在進程的PCB里面,即task_struct。?

信號如何保存?是否收到了指定的信號,是否即兩態(tài):二進制表示非0即1,即用1代表收到了信號,0則代表沒有收到信號

這種結(jié)構(gòu)稱為位圖結(jié)構(gòu),之前有過相關(guān)解釋,C++專欄也有

指定的信號在Linux是:

用 kill -l 命令可以察看系統(tǒng)定義的信號列表

kill -l 

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

其中 [1, 31] 號信號是普通信號,[34, 64] 號信號是實時信號(信號學(xué)習(xí)中,這里我們只學(xué)習(xí)普通信號)?

即普通信號就可以用32個比特位來表示,即四字節(jié)的整型,即在 task_struct?里面一定存在一個字段 unsigned int

struct task_struct
{
	//進程屬性
    //.......
    
	unsigned int signal;

	//.......
}

這個字段可以表示所有的普通信號:

第一個比特位代表 1號信號,以此類推(位圖結(jié)構(gòu))

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

如何理解信號的發(fā)送?也就是發(fā)送信號的本質(zhì)

發(fā)送信號的本質(zhì)就是:修改PCB中的信號位圖,即task_struct 的信號位圖,也就是上面圖中所說的位圖,比如發(fā)送1號信號,發(fā)送1號信號就是把信號位圖的第一個比特位由0置1

而?task_struct 是內(nèi)核維護的一種數(shù)據(jù)結(jié)構(gòu)對象,所以?task_struct 的管理者是OS,只有OS才有權(quán)利修改?task_struct 里面的內(nèi)容,所以以此推導(dǎo):無論在未來學(xué)習(xí)多少種發(fā)送信號的方式,本質(zhì)都是通過OS向目標進程發(fā)送信號(誰都沒有權(quán)利修改OS內(nèi)的數(shù)據(jù)結(jié)構(gòu),只有OS自己可以)

所以我們用戶要操作信號,OS必須提供發(fā)送信號、處理信號的相關(guān)系統(tǒng)調(diào)用

?比如之前一直使用的 kill 命令,底層一定調(diào)用了對應(yīng)的系統(tǒng)調(diào)用

以代碼展示信號:

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
    while(true)
    {
        cout << "我是一個進程,pid: " << getpid() << endl;
        sleep(1);
    }

    return 0;
}

該程序的運行結(jié)果就是死循環(huán)地進行打印,而對于死循環(huán)來說,常用方式就是使用 Ctrl+C 終止進程

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

為什么使用 Ctrl+C 后,該進程就終止了??

實際上當用戶按Ctrl+C時,這個鍵盤輸入會產(chǎn)生一個硬中斷,被操作系統(tǒng)獲取并解釋成信號(Ctrl+C被解釋成2號信號),然后操作系統(tǒng)將2號信號發(fā)送給目標前臺進程,當前臺進程收到2號信號后就會退出

2號信號是:SIGINT

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

這里只是簡單介紹,下面詳細解釋?

1.3?信號概念

信號是進程之間事件異步通知的一種方式,屬于軟中斷

1.4 查看系統(tǒng)定義信號列表

kill -l 命令可以察看系統(tǒng)定義的信號列表

kill -l

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

1.2?有過大概解釋,這不介紹了,我們可以使用信號數(shù)字的編號,也可以直接使用宏定義,比如2號信號,我們可以使用 2 也可以使用?SIGINT

每個信號都有一個編號和一個宏定義名稱,這些宏定義可以在 signum.h 中找到,例如其中有定 義 #define SIGINT 2

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

查看 signum.h

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

1.5?man 7 signal

普通信號各自在什么條件下產(chǎn)生,默認的處理動作是什么,在signal(7)中都有詳細說明,直接看文檔即可

man 7 signal 查看:

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

普通信號的默認動作:?

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

1.6 解釋1.2的代碼樣例

man 7 signal 查看二號信號的作用:

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

Interrupt from keyboard:從鍵盤獲取中斷
默認動作:Term
Term: Default action is to terminate the process.
翻譯:默認操作是終止進程

?所以,到這里我們就知道為什么 2號信號可以終止進程

我們可以使用signal函數(shù)對2號信號進行捕捉,證明當我們按 Ctrl+C 時進程確實是收到了2號信號。使用 signal 函數(shù)時,我們需要傳入兩個參數(shù),第一個是需要捕捉的信號編號,第二個是對捕捉信號的處理方法,該處理方法的參數(shù)是int,返回值是void

注:signal 函數(shù)這里是使用,詳細下面才解釋,這里只是演示

下面的代碼中將2號信號進行了捕捉,當該進程運行起來后,若該進程收到了2號信號就會打印出收到信號的信號編號

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;

void handler(int signo)
{
    cout << "捕捉到一個信號,該信號編號是:" << signo << endl;
}

int main()
{
    signal(2, handler);
    while(true)
    {
        cout << "我是一個進程,pid: " << getpid() << endl;
        sleep(1);
    }

    return 0;
}

此時當該進程收到2號信號后,就會執(zhí)行我們給出的handler方法,而不會像之前一樣直接退出了,因為此時我們已經(jīng)將2號信號的處理方式由默認改為了自定義了(默認行為:結(jié)束進程? -> 變成 自定義行為:handler方法)

進程是無法 Ctrl+C 結(jié)束進程,直接發(fā)送 9 號信號終止進程,kill -9 進程pid

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

注意:?

  1. Ctrl+C 產(chǎn)生的信號只能發(fā)給前臺進程。一個命令后面加個&可以放到后臺運行,這樣Shell不必等待進程結(jié)束就可以接受新的命令,啟動新的進程。
  2. Shell可以同時運行一個前臺進程和任意多個后臺進程,只有前臺進程才能接到像 Ctrl+C 這種控制鍵產(chǎn)生的信號。
  3. 前臺進程在運行過程中用戶隨時可能按下 Ctrl+C 而產(chǎn)生一個信號,也就是說該進程的用戶空間代碼執(zhí)行到任何地方都有可能收到 SIGINT 信號而終止,所以信號相對于進程的控制流程來說是異步(Asynchronous)的

1.7 信號處理常見方式概覽

信號處理動作有以下三種,也就是上面概念所提到的

  1. 默認:執(zhí)行該信號的默認處理動作;
  2. 忽略:忽略此信號;
  3. 自定義:提供一個信號處理函數(shù),要求內(nèi)核在處理該信號時切換到用戶態(tài)執(zhí)行這個處理函數(shù),這種方式稱為捕捉(Catch)一個信號

二、產(chǎn)生信號

前面第一大點都是信號預(yù)備知識,這里第二大點講的是信號產(chǎn)生

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

2.1 signal函數(shù)

對上面使用signal函數(shù)進行補充:signal函數(shù)的作用是用于處理信號(自定義行為),對信號進行捕捉?

man 2 signal 查看一下

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

signal

頭文件:
 #include <signal.h>

函數(shù)原型:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

參數(shù):
    第一個參數(shù)signum:需要捕捉的信號編號
    第二個參數(shù)handler:對信號自定義的行為,對捕捉信號的處理方法(函數(shù)),handler是一個回調(diào)函數(shù),該處理方法的參數(shù)是 int,返回值是void
    
    sighandler_t是一個函數(shù)指針

2.2?通過終端按鍵產(chǎn)生信號

測試代碼

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
    while(true)
    {
        cout << "我是一個進程,pid: " << getpid() << endl;
        sleep(1);
    }

    return 0;
}

該代碼是一個死循環(huán),前面已經(jīng)說過可以使用 Ctrl+C 來終止進程,發(fā)送的信號是 2號信號 SIGINT

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

小提示:進程運行了,該進程就變成了前臺進程(命令行在當前進程無效),bash就會變成后臺進程,進程結(jié)束后,bash又會變回前臺進程,命令行生效?

實際上除了按 Ctrl+C 之外,按 Ctrl+\ 也可以終止該進程

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

按 Ctrl+C 和按 Ctrl+\ 都可以終止進程,但是兩者有什么區(qū)別?

Ctrl+C?發(fā)送的信號是2號信號 SIGINT,Ctrl+\?發(fā)送的信號是3號信號 SIGQUIT

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

這兩個信號的默認行為(Action)不一樣:2號信號默認行為是 Term,3號信號默認行為是 Core

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

Term 上面解釋過了,不解釋了

Core?在終止進程的時候會進行一個動作,那就是核心轉(zhuǎn)儲

Default action is to terminate the process and dump core (see core(5)).

dump core:核心轉(zhuǎn)儲

什么是核心轉(zhuǎn)儲?

Term 把進程終止了就不做其他工作了,核心轉(zhuǎn)儲Core 把進程終止后還做其他的工作

注意:在云服務(wù)器中,核心轉(zhuǎn)儲是默認被關(guān)掉的,我們需要打開才能觀察到現(xiàn)象

以通過使用 ulimit -a 命令查看當前資源限制的設(shè)定

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

其中,第一行顯示core文件的大小為0,即表示核心轉(zhuǎn)儲是被關(guān)閉的

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

我們可以通過?ulimit -c size命令來設(shè)置core文件的大小,即打開核心轉(zhuǎn)儲

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

core文件的大小設(shè)置完畢后,就相當于將核心轉(zhuǎn)儲功能打開了

再次運行上面的程序,Ctrl+\把進程終止(發(fā)送3號信號,默認動作Core),現(xiàn)象就會出現(xiàn):core dumped

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

并且會在當前路徑下生成一個core文件,該文件以一串數(shù)字為后綴,而這一串數(shù)字實際上就是發(fā)生這一次核心轉(zhuǎn)儲的進程的PID

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

?核心轉(zhuǎn)儲就是:當進程出現(xiàn)異常的時候,我們將進程在對應(yīng)的時刻,在內(nèi)存中的有效數(shù)據(jù)轉(zhuǎn)儲到磁盤中,也就是上面的文件

那么核心轉(zhuǎn)儲有什么用??

當我們的代碼出錯了,我們最關(guān)心的是我們的代碼是什么原因出錯的。如果我們的代碼運行結(jié)束了,那么我們可以通過退出碼來判斷代碼出錯的原因,而如果一個代碼是在運行過程中出錯的,那么我們也要有辦法判斷代碼是什么原因出錯的

當我們的程序在運行過程中崩潰了,我們一般會通過調(diào)試來進行逐步查找程序崩潰的原因。而在某些特殊情況下,我們會用到核心轉(zhuǎn)儲,核心轉(zhuǎn)儲指的是操作系統(tǒng)在進程收到某些信號而終止運行時,將該進程地址空間的內(nèi)容以及有關(guān)進程狀態(tài)的其他信息轉(zhuǎn)而存儲到一個磁盤文件當中,這個磁盤文件也叫做核心轉(zhuǎn)儲文件(支持調(diào)試

可以使用gdb進行調(diào)試

為了方便演示,使用 2.4.2?的空指針例子演示,代碼如下:

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
    while(true)
    {
         cout << "我是一個進程,正在運行中,pid: " << getpid() << endl;
        sleep(2);

        //空指針(野指針)
        int *p = nullptr;
        *p = 10;
    }
    return 0;
}

?注:Linux默認是release,調(diào)試需要增加 -g選項

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

運行結(jié)果

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

?使用gdb對當前可執(zhí)行程序進行調(diào)試

gdb 可執(zhí)行程序, 進入調(diào)試

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

然后直接使用 core-file 核心轉(zhuǎn)儲文件 命令加載 core文件,即可判斷出該程序在終止時收到了11號信號,并且定位到了產(chǎn)生該錯誤的具體代碼,錯誤信息也詳細列出

core-file core.11467

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

?事后用調(diào)試器檢查core文件以查清錯誤原因,這種調(diào)試方式叫做事后調(diào)試

這就是 Term 和 Core 的區(qū)別,Term是正常終止進程?

2.3?調(diào)用系統(tǒng)函數(shù)向進程發(fā)信號

2.3.1 kill函數(shù)

kill函數(shù)是一個系統(tǒng)調(diào)用,kill命令就是通過 kill函數(shù)來實現(xiàn)的

測試 kill命令?

使用kill命令向一個進程發(fā)送信號時,我們可以用 kill -信號編號 進程pid 的形式進行發(fā)送信號

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

kill函數(shù)的作用是:向目標進程發(fā)送指定信號

man 2 kill 查看:

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

函數(shù):kill

頭文件
#include <sys/types.h>
#include <signal.h>

函數(shù)原型:
int kill(pid_t pid, int sig);

參數(shù)
    第一個參數(shù)pid就是進程的pid
    第二個參數(shù)sig是信號的編號

返回值
發(fā)送成功,返回0,否則返回-1

使用 kill函數(shù)模擬 kill命令(mykill):

#include <iostream>
#include <sys/types.h>
#include <signal.h>
#include <string>
#include <unistd.h>
using namespace std;

//使用手冊
static void Usage(const string& proc)
{
    cout << "\nUsage: " << proc << " pid signo\n" << endl;
}

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    //把字符串轉(zhuǎn)整型
    pid_t id = atoi(argv[1]);
    int signo = atoi(argv[2]);

    int n = kill(id, signo);
    if(n != 0)
    {
        perror("kill");
    }

    return 0;
}

另一個測試代碼,死循環(huán),test.cc

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
    while(true)
    {
        cout << "我是一個進程,pid: " << getpid() << endl;
        sleep(1);
    }

    return 0;
}

選運行死循環(huán)測試代碼,再使用 mykill殺掉死循環(huán),這樣就實現(xiàn)了一個 kill命令

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

2.3.2?raise函數(shù)

raise函數(shù)的作用是:給自己發(fā)送信號

man 3?raise 查看:

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

函數(shù):raise

頭文件
#include <signal.h>

函數(shù)原型
int raise(int sig);

參數(shù):sig是要發(fā)送的信號編號

返回值
發(fā)送成功,則返回0,否則返回一個非零值

這個函數(shù)也可以通過 kill函數(shù)實現(xiàn):kill(getpid(), sig)?

測試代碼:

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;

int main()
{
    int cnt = 0;
    while(true)
    {
        cout << "我是一個進程,pid: " << getpid() << " cnt: " << cnt++ << endl;
        sleep(1);
        //cnt>=5,就給自己發(fā)送3號信號
        if(cnt >= 5)
            raise(3);
    }

    return 0;
}

運行結(jié)果

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

2.3.3?abort函數(shù)

abort函數(shù)用于給自己發(fā)送指定信號:6號信號SIGABRT,6號信號的默認動作也是終止進程

man 3 abort 查看

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號??

函數(shù):abort

頭文件
#include <stdlib.h>

函數(shù)原型
void abort(void);

?這個函數(shù)也可以通過 kill函數(shù)實現(xiàn):kill( getpid(),??SIGABRT?)?

?測試代碼:

#include <iostream>
#include <cstdlib>
#include <unistd.h>
using namespace std;

int main()
{
    int cnt = 0;
    while(true)
    {
        cout << "我是一個進程,pid: " << getpid() << " cnt: " << cnt++ << endl;
        sleep(1);
        //cnt>=5,就給自己發(fā)送指定信號
        if(cnt >= 5)
            abort();
    }

    return 0;
}

運行結(jié)果

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

abort函數(shù)總是會成功的,所以沒有返回值

進程收到的大部分信號,默認動作都是終止進程

2.4 硬件異常產(chǎn)生信號

信號的產(chǎn)生,不一定非得用戶顯示發(fā)送,有些信號會在OS內(nèi)部自動產(chǎn)生,比如硬件異常產(chǎn)生的信號

硬件異常被硬件以某種方式被硬件檢測到并通知內(nèi)核,然后內(nèi)核向當前進程發(fā)送適當?shù)男盘?

2.4.1 除0操作產(chǎn)生的異常?

比如進行進程除0操作,VS會直接報錯終止進程

下面在g++下進行測試,測試代碼如下

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
    while(true)
    {
        cout << "我在運行中..." << endl;
        sleep(1);

        //除0操作
        int a = 10;
        a /= 0;
    }
    
    return 0;
}

運行結(jié)果,編譯警告不用理會

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

?為什么除0會終止進程??

因為進程收到了來自O(shè)S的信號,該信號是SIGFPE,8號信號

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

該信號的默認動作也是 Core,也是終止進程

 Floating point exception:浮點異常

?下面對該信號進行捕捉,捕捉后進行自定義動作

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;

//自定義行為
void handler(int signo)
{
    cout << "捕捉到一個信號,該信號編號是:" << signo << endl;
}

int main()
{
    signal(8, handler);
    while(true)
    {
        cout << "我在運行中..." << endl;
        sleep(1);

        //除0操作
        int a = 10;
        a /= 0;
    }

    return 0;
}

?運行結(jié)果

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

?運行結(jié)果說明,進程確實收到了8信號,可是為什么一直對該信號一直捕獲??OS為什么一直發(fā)送8號信號???

下面講解對于除0的理解:

?在CPU中有很多的寄存器,例如eax,ebx,eip等等

CPU會將代碼中的變量拿到寄存器中進行運算,如果有需要,運算結(jié)果需要返回

?進行對兩個數(shù)進行算術(shù)運算時,我們是先將這兩個操作數(shù)分別放到兩個寄存器當中,然后進行算術(shù)運算并把結(jié)果寫回寄存器當中

(這里就簡單略過具體的寄存器,方便理解,圖也是簡略化)

CPU當中還有一組寄存器叫做狀態(tài)寄存器,它可以用來標記當前指令執(zhí)行結(jié)果的各種狀態(tài)信息,如有無進位、有無溢出等等

除0操作,對于計算機來說,是除一個無窮小的數(shù),得到的結(jié)果是無窮大的數(shù),寄存器存不下這個數(shù),這時候狀態(tài)寄存器的溢出標志位由0置1,這時CPU就會發(fā)出運算異常的信號,OS就會識別到這個異常,OS就會給指定的進程發(fā)送這個信號,這個信號就是8號信號,CPU報的這個異常歸屬于硬件異常,OS檢測到,由OS主動發(fā)送給目標進程

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

接下來解釋為什么會一直死循環(huán)捕獲到8號信號???

?代碼在CPU中執(zhí)行的時候,此時CPU內(nèi)的寄存器的內(nèi)容屬于該進程的上下文數(shù)據(jù),因為寄存器只有一份,代碼沒有運行完,當該進程的時間片到了,就會從CPU上切下去,該進程的上下文數(shù)據(jù)也被會保存,包括溢出標志位

進程被來回切換,就有無數(shù)次寄存器的內(nèi)容被保存會恢復(fù)的過程,所以每次恢復(fù)的時候,OS都會識別到CPU內(nèi)部的狀態(tài)寄存器溢出標志位為1,OS就會給該進程發(fā)送8號信號

由于我們把該信號捕獲了,執(zhí)行自定義行為,該信號的默認行為就不會執(zhí)行,每次恢復(fù)的時候,進程還沒有被終止,OS依舊會識別到CPU內(nèi)部的狀態(tài)寄存器溢出標志位為1,就又發(fā)8號信號給該進程,以此往復(fù),就陷入死循環(huán),所以我們會看到8號信號一直被捕獲

2.4.2 空指針異常

空指針(野指針)問題在程序中可能會遇到,空指針VS直接崩潰,終止程序,下面在g++下測試

測試代碼:

#include <iostream>
#include <unistd.h>
using namespace std;


int main()
{
    while(true)
    {
         cout << "我是一個進程,正在運行中,pid: " << getpid() << endl;
        sleep(2);

        //空指針(野指針)
        int *p = nullptr;
        *p = 10;
    }

    return 0;
}

運行結(jié)果

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

結(jié)果發(fā)現(xiàn),空指針操作進程直接被終止了,為什么進程會被終止?

因為進程收到了來自O(shè)S的信號,該信號是11號信號 SIGSEGV

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

該信號的默認動作也是 Core,也是終止進程

Invalid memory reference:無效內(nèi)存引用
Segmentation fault:段錯誤

?下面對該信號進行捕捉,捕捉后進行自定義動作

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;

//自定義行為
void handler(int signo)
{
    cout << "捕捉到一個信號,該信號編號是:" << signo << endl;
}

int main()
{
    signal(11, handler);
    while(true)
    {
         cout << "我是一個進程,正在運行中,pid: " << getpid() << endl;
        sleep(2);

        //空指針(野指針)
        int *p = nullptr;
        *p = 10;
    }

    return 0;
}

運行結(jié)果?

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

?OS怎么知道空指針異常??

我們必須知道的是,當我們要訪問一個物理內(nèi)存時,一定要先經(jīng)過頁表的映射,將虛擬地址轉(zhuǎn)換成物理地址,然后才能進行相應(yīng)的訪問操作

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

在從虛擬地址映射到物理地址的過程中,必須經(jīng)過頁表,頁表上有一個硬件叫做MMU,MMU被集成在CPU里面,MMU用于計算映射關(guān)系,在MMU算出物理內(nèi)存的映射關(guān)系之后,CPU可以直接進行物理內(nèi)存訪問

當我們要訪問不屬于我們的虛擬地址時(訪問空地址,空指針的使用,空地址是不允許訪問的),MMU在進行虛擬地址到物理地址的轉(zhuǎn)換時就會出現(xiàn)錯誤,MMU就會出現(xiàn)異常,OS在這時也會識別這個異常,然后OS就會向目標進程發(fā)送11號信號SIGSEGV,該信號的默認動作就是終止進程

死循環(huán)捕捉信號,解釋與上面除0的類似,不解釋了

2.5?由軟件條件產(chǎn)生信號

SIGPIPE信號實際上就是一種由軟件條件產(chǎn)生的信號,當進程在使用管道進行通信時,讀端進程將讀端關(guān)閉,而寫端進程還在一直向管道寫入數(shù)據(jù),那么此時寫端進程就會收到13號信號SIGPIPE 進而被操作系統(tǒng)終止

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

該信號的默認行為是 Term,也是終止進程,這個就解釋到這

下面介紹 alarm函數(shù) 和 SIGALRM信號

?alarm 函數(shù)的作用是:設(shè)定一個鬧鐘,也就是告訴內(nèi)核在 seconds秒之后給當前進程發(fā) SIGALRM信號, 該信號的默認處理動作是終止當前進程

man 2 alarm 查看

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

函數(shù):alarm

頭文件:
#include <unistd.h>

函數(shù)原型:
unsigned int alarm(unsigned int seconds);

參數(shù):傳入一個時間,單位秒

返回值:
若調(diào)用alarm函數(shù)前,進程已經(jīng)設(shè)置了鬧鐘,則返回上一個鬧鐘時間的剩余時間,并且本次鬧鐘的設(shè)置會覆蓋上一次鬧鐘的設(shè)置
如果調(diào)用alarm函數(shù)前,進程沒有設(shè)置鬧鐘,則返回值為0

?測試代碼

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
    //1秒后才發(fā)送信號
    alarm(1);
    int cnt = 0;
    while(true)
    {
         cout << "cnt: " << cnt << endl;
         cnt++;
    }
    return 0;
}

運行結(jié)果

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

下面進行捕捉該信號

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;

 int cnt = 0;
//自定義行為
void handler(int signo)
{
    cout << "捕捉到一個信號,該信號編號是:" << signo << endl;
    cout << "cnt: "<< cnt << endl;
    exit(1);//自定義行為,退出進程
}

int main()
{
    signal(14, handler);
    //1秒后才發(fā)送信號
    alarm(1);
   
    while(true)
    {
         cnt++;
    }
    return 0;
}

?運行結(jié)果

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

14號信號默認動作是 Term,也是終止進程

【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號

兩次cnt實驗結(jié)果數(shù)據(jù)級別相差較大,由此也證明了,與計算機單純的計算相比較,計算機與外設(shè)進行IO時的速度是非常慢的

注意:9號信號不支持捕捉,這是禁止的,這是一個管理員信號

信號產(chǎn)生,完結(jié),下一篇進入信號保存和處理

--------------------- END ----------------------?文章來源地址http://www.zghlxwxcb.cn/news/detail-425062.html

「 作者 」 楓葉先生
「 更新 」 2023.4.4
「 聲明 」 余之才疏學(xué)淺,故所撰文疏漏難免,
          或有謬誤或不準確之處,敬請讀者批評指正。

到了這里,關(guān)于【Linux】第八講:Linux進程信號詳解(一)_ 認識信號 | 產(chǎn)生信號的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 【Linux】進程信號概念 | 核心轉(zhuǎn)儲 | 信號的產(chǎn)生

    【Linux】進程信號概念 | 核心轉(zhuǎn)儲 | 信號的產(chǎn)生

    進程信號重點: 1.掌握Linux信號的基本概念 2.掌握信號產(chǎn)生的一般方式 3.理解信號遞達和阻塞的概念,原理。 4.掌握信號捕捉的一般方式。 5.重新了解可重入函數(shù)的概念。 6.了解競態(tài)條件的情景和處理方式 7.了解SIGCHLD信號, 重新編寫信號處理函數(shù)的一般處理機制 學(xué)習(xí)的順序

    2024年02月22日
    瀏覽(24)
  • Linux入門之進程信號|信號產(chǎn)生的方式

    Linux入門之進程信號|信號產(chǎn)生的方式

    文章目錄 一、信號入門 1.linux信號的基本概念 2.使用kill -l 命令可以查看系統(tǒng)定義的信號列表 3.信號處理常見方式 二、產(chǎn)生信號 1.通過終端按鍵產(chǎn)生信號 2.通過調(diào)用系統(tǒng)函數(shù)向進程發(fā)信號 3.由軟條件產(chǎn)生信號 4.硬件異常產(chǎn)生信號 ??? 1. /0異常 ??? 2.模擬野指針 信號是進程之

    2024年02月09日
    瀏覽(20)
  • 【Linux】進程信號 --- 信號的產(chǎn)生 保存 捕捉遞達

    【Linux】進程信號 --- 信號的產(chǎn)生 保存 捕捉遞達

    被愛情困住的是傻子 1. 關(guān)于信號這個話題我們其實并不陌生,早在以前的時候,我們想要殺死某個后臺進程的時候,無法通過ctrl+c熱鍵終止進程時,我們就會通過kill -9的命令來殺死信號。 查看信號也比較簡單,通過kill -l命令就可以查看信號的種類,雖然最大的信號編號是

    2023年04月23日
    瀏覽(20)
  • 【Linux】進程信號 -- 信號產(chǎn)生 | 系統(tǒng)調(diào)用、硬件、軟件的信號發(fā)送

    【Linux】進程信號 -- 信號產(chǎn)生 | 系統(tǒng)調(diào)用、硬件、軟件的信號發(fā)送

    kill -l 是一個在 Linux 和 Unix 系統(tǒng)中使用的命令,用于列出可用的信號列表。 在Linux和Unix系統(tǒng)中,進程可以通過發(fā)送信號來與其他進程或操作系統(tǒng)交互。kill 命令可以向指定的進程發(fā)送一個特定的信號,以便對其進行控制,例如終止進程或重新啟動進程等。 kill -l 命令會列出可

    2024年02月16日
    瀏覽(59)
  • 【探索Linux】—— 強大的命令行工具 P.16(進程信號 —— 信號產(chǎn)生 | 信號發(fā)送 | 核心轉(zhuǎn)儲)

    【探索Linux】—— 強大的命令行工具 P.16(進程信號 —— 信號產(chǎn)生 | 信號發(fā)送 | 核心轉(zhuǎn)儲)

    在現(xiàn)代社會中,信號無處不在。我們的生活充滿了各種各樣的信號,它們指引著我們前進的方向,使我們能夠了解周圍環(huán)境的變化。正如在計算機編程中一樣,Linux進程信號也是一種重要的信號,它們扮演著相似的角色。 想象一下,在繁忙的城市街道上行駛,交通信號燈是我

    2024年02月05日
    瀏覽(17)
  • 【Linux】進程信號(完整版) --- 信號產(chǎn)生 信號保存 信號捕捉 可重入函數(shù) volatile SIGCHLD信號等

    【Linux】進程信號(完整版) --- 信號產(chǎn)生 信號保存 信號捕捉 可重入函數(shù) volatile SIGCHLD信號等

    ?? 作者: 阿潤菜菜 ?? 專欄: Linux系統(tǒng)編程 我們想要殺死某個后臺進程的時候,無法通過ctrl+c熱鍵終止進程時,我們就會通過kill -9的命令來殺死信號。 查看信號也比較簡單,通過 kill -l 命令就可以查看所有信號的種類,雖然最大的信號編號是64,但實際上所有信號只有6

    2024年02月04日
    瀏覽(26)
  • 第八講 端口和激勵詳解

    第八講 端口和激勵詳解

    1.概述 (1)激勵 ·所謂激勵,顧名思義就是指在進行仿真分析時需要提供的激勵信號源 ·在CST微波工作室中,提供了多種不同類型的激勵源,用于分析不同類型問題的分析 ·在運行仿真分析之前,至少要設(shè)置一個激勵源作為結(jié)構(gòu)的輸入信號激勵 (2)激勵類型 端口激勵(Port

    2024年02月15日
    瀏覽(22)
  • 【Linux】進程信號篇Ⅰ:信號的產(chǎn)生(signal、kill、raise、abort、alarm)、信號的保存(core dump)

    【Linux】進程信號篇Ⅰ:信號的產(chǎn)生(signal、kill、raise、abort、alarm)、信號的保存(core dump)

    kill -l 可以查看所有信號: 其中,前面的數(shù)字就是信號,后面的大寫英文就是信號名稱,實際就是宏。 我們需要關(guān)注的是 1~31 號普通信號,關(guān)注他們有沒有產(chǎn)生(可以用 0 或者 1 表示)。 所以,進程的 pcb 中,需要對產(chǎn)生的信號先用 位圖 保存起來,再按照一定的順序去處理

    2024年01月25日
    瀏覽(31)
  • 【Linux】詳解信號產(chǎn)生的方式

    【Linux】詳解信號產(chǎn)生的方式

    在命令行中通過 kill -數(shù)字 pid 指令可以給指定進程發(fā)送指定信號。這里說明一下幾個常見的信號: SIGINT(2號信號):中斷信號,通常由用戶按下Ctrl+C產(chǎn)生,用于通知進程終止。 SIGQUIT(3號信號):?終止進程并產(chǎn)生core文件,用于后續(xù)分析程序崩潰時的狀態(tài)和數(shù)據(jù)。 SIGKILL(

    2024年04月28日
    瀏覽(28)
  • MyBatis第八講:MyBatis插件機制詳解與實戰(zhàn)

    MyBatis提供了一種插件(plugin)的功能,雖然叫做插件,但其實這是攔截器功能。那么攔截器攔截MyBatis中的哪些內(nèi)容呢?本文是MyBatis第8講,對MyBatis插件機制詳解。

    2024年02月13日
    瀏覽(31)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包