中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

單頁面網(wǎng)站做排名網(wǎng)絡(luò)推廣外包搜索手機(jī)蛙軟件

單頁面網(wǎng)站做排名,網(wǎng)絡(luò)推廣外包搜索手機(jī)蛙軟件,wordpress做微信推廣,個(gè)人一般注冊(cè)什么類型的公司🌎 Linux信號(hào)詳解 文章目錄: Linux信號(hào)詳解 信號(hào)入門 技術(shù)應(yīng)用角度的信號(hào) 信號(hào)及信號(hào)的產(chǎn)生 ??????信號(hào)的概念 ??????信號(hào)的處理方式 信號(hào)的產(chǎn)生方式 ????????鍵盤產(chǎn)生信號(hào) ????????系統(tǒng)調(diào)用產(chǎn)生信號(hào) ????????軟件…

🌎 Linux信號(hào)詳解


文章目錄:

Linux信號(hào)詳解

????信號(hào)入門

????技術(shù)應(yīng)用角度的信號(hào)

????信號(hào)及信號(hào)的產(chǎn)生
??????信號(hào)的概念
??????信號(hào)的處理方式

??????信號(hào)的產(chǎn)生方式
????????鍵盤產(chǎn)生信號(hào)
????????系統(tǒng)調(diào)用產(chǎn)生信號(hào)
????????軟件條件產(chǎn)生信號(hào)
????????異常產(chǎn)生信號(hào)

????對(duì)信號(hào)產(chǎn)生方式的理解
??????鍵盤產(chǎn)生信號(hào)
??????異常信號(hào)的理解


🚀 信號(hào)入門


現(xiàn)實(shí)生活中的信號(hào)

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

🚀技術(shù)應(yīng)用角度的信號(hào)

??用戶輸入命令,在Shell下運(yùn)行一個(gè)前臺(tái)進(jìn)程,用戶鍵盤輸入 Ctrl C (2號(hào)信號(hào))則會(huì)產(chǎn)生一個(gè)硬件中斷,被OS獲取,解釋成為信號(hào),發(fā)送給目標(biāo)前臺(tái)進(jìn)程,前臺(tái)進(jìn)程收到信號(hào)之后,引起進(jìn)程退出。

在這里插入圖片描述

??運(yùn)行程序,生成前臺(tái)進(jìn)程,我們使用信號(hào)殺死前臺(tái)進(jìn)程:

在這里插入圖片描述

注意

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

🚀信號(hào)及信號(hào)的產(chǎn)生

??信號(hào)的概念

??信號(hào)是進(jìn)程之間事件異步通知的一種方式,屬于 軟中斷。

??Linux中存在許多信號(hào),我們可以使用 kill -l 命令查看Linux中有哪些信號(hào):

在這里插入圖片描述

??Linux中,有 62種信號(hào),前31種(1~31)信號(hào)被稱為 標(biāo)準(zhǔn)信號(hào),每個(gè)信號(hào)都有特殊的含義及用途。后31種(34~64)信號(hào)被稱為 實(shí)時(shí)信號(hào),可用于實(shí)時(shí)應(yīng)用程序的拓展信號(hào),提供了更多的靈活性。注意中間并沒有32和33號(hào)信號(hào)。

??我們可以通過man手冊(cè)來查詢不同信號(hào)的含義 man 7 signal :

在這里插入圖片描述

??并且每個(gè)信號(hào)的編號(hào)都有自己的名字,這些 名字 其實(shí)就 C 語言的 ,如果調(diào)用信號(hào),既可以通過信號(hào)的名稱調(diào)用,也可通過信號(hào)的編號(hào)調(diào)用。當(dāng)然,這么多的信號(hào)并不需要你全部記下來,我們?cè)谶\(yùn)用的過程中就會(huì)知道哪些信號(hào)常用,哪些不常用。


??信號(hào)的處理方式

??信號(hào)的 常見處理方式 有以下幾種:

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

??我們以信號(hào)的方式來終止進(jìn)程,我們通過一下代碼進(jìn)行測(cè)試:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>int main()
{while(true){std::cout << "I'am a proc, I'm running now..., pid: " << getpid() << std::endl;sleep(1);}return 0;
}

在這里插入圖片描述

??我們使用9號(hào)信號(hào)和2號(hào)信號(hào)殺死了進(jìn)程,這種發(fā)命令做出對(duì)應(yīng)行為的方式 就是進(jìn)程執(zhí)行了信號(hào)的默認(rèn)動(dòng)作。好似阿熊在十字路口知道紅燈停、綠燈行一樣。

??但是今天我們以 信號(hào)捕捉 的方式來處理發(fā)來的信號(hào),我們就需要用到 signal 接口:

typedef void (*signhandler_t)(int);
signhandler_t signal(int signum, sighandler_t handler);

在這里插入圖片描述

  • ?signum參數(shù)傳入需要捕捉的信號(hào)(名字或編號(hào)),當(dāng)進(jìn)程收到與其相匹配的信號(hào)時(shí)則會(huì)調(diào)用第二個(gè)參數(shù),否則不會(huì)有任何動(dòng)作。
  • ?handler參數(shù)handlder方法,此方法為自定義方法,當(dāng)收到signum信號(hào)則不會(huì)執(zhí)行該信號(hào)的默認(rèn)動(dòng)作,變?yōu)閳?zhí)行該方法。
  • ?返回值返回前一個(gè)信號(hào)處理方法

??值得注意的是,我們?cè)谠O(shè)置信號(hào)捕捉時(shí),并不需要將此接口放入循環(huán)之中,只需要調(diào)用該接口一次,在整個(gè)程序中則一直循環(huán)有效。我們?cè)O(shè)置一個(gè)signal方法做測(cè)試:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>void handler(int signo)// 回調(diào)函數(shù),收到信號(hào)則執(zhí)行handler方法
{std::cout << "get a sig, number is: " << signo << std::endl;
}int main()
{signal(SIGINT, handler);// 設(shè)置信號(hào)捕捉while(true){std::cout << "I'am a proc, I'm running now..., pid: " << getpid() << std::endl;sleep(1);}return 0;
}

在這里插入圖片描述

??signal函數(shù)執(zhí)行時(shí)并不會(huì)立刻對(duì)handler執(zhí)行回調(diào),只有當(dāng)收到對(duì)應(yīng)的信號(hào)時(shí),才會(huì)執(zhí)行回調(diào)。比如,阿熊跟室友打賭輸了,接下來一周等紅綠燈需要聽室友的,本來紅燈停變?yōu)榱思t燈唱歌,但是我們并不知道室友什么時(shí)候讓阿熊唱歌。也就是說,因?yàn)?信號(hào)是異步觸發(fā),所以只有收到信號(hào)時(shí)才會(huì)執(zhí)行回調(diào)。

??信號(hào)的默認(rèn)處理方式還剩下忽略信號(hào),也就是信號(hào)發(fā)送到進(jìn)程,但是進(jìn)程對(duì)其不管不顧。在程序中,使用捕捉的方式實(shí)現(xiàn)信號(hào)忽略,那么signal第二個(gè)參數(shù)應(yīng)該調(diào)用 SIG_IGN(signal ignore):

在這里插入圖片描述

??在底層,SIG_IGN 是將整形1強(qiáng)轉(zhuǎn)為 __sighandler_t 類型,于是就可以告訴OS,進(jìn)程運(yùn)行時(shí)以忽略的方式將 signal 函數(shù)的第一個(gè)參數(shù)忽略:

在這里插入圖片描述


??信號(hào)的產(chǎn)生方式
🚩鍵盤產(chǎn)生信號(hào)

??上面我們已經(jīng)將以kill命令產(chǎn)生信號(hào)的方式介紹完了,而信號(hào)的產(chǎn)生方式還有一種,鍵盤產(chǎn)生信號(hào),我們?cè)谏厦嬉舱f了,使用 Ctrl-C 的方式可以終止進(jìn)程,這就是一種鍵盤產(chǎn)生信號(hào)的方式,鍵盤產(chǎn)生信號(hào)的流程:

鍵盤特定輸入 ——> OS解釋為信號(hào) ——> 向目標(biāo)進(jìn)程發(fā)送信號(hào) ——> 進(jìn)程收到信號(hào) ——> 進(jìn)程做出響應(yīng)

??當(dāng)我們把二號(hào)信號(hào)進(jìn)行捕捉,并且回調(diào)函數(shù)只對(duì)信號(hào)進(jìn)行打印動(dòng)作,我們?cè)偈褂?Ctrl-C 就殺不死該進(jìn)程了:

在這里插入圖片描述

??而鍵盤中并不只有 Ctrl-C 組合,還有 Ctrl-\ 組合,這里我使用的就是 Ctrl-\ 退出進(jìn)程的,其對(duì)應(yīng)信號(hào)的 3號(hào)信號(hào)。


🚩系統(tǒng)調(diào)用產(chǎn)生信號(hào)

??除了鍵盤產(chǎn)生信號(hào),我們還可以使用系統(tǒng)調(diào)用產(chǎn)生信號(hào),Linux中存在 kill 接口:

int kill(pid_t pid, int signo);
  • ?功能給指定的進(jìn)程發(fā)送指定的信號(hào)。
  • ?pid參數(shù)傳入進(jìn)程pid
  • ?signo參數(shù)傳入對(duì)進(jìn)程發(fā)送的信號(hào)。
  • ?返回值發(fā)送成功返回0,否則返回-1,并設(shè)置錯(cuò)誤碼。

??我們編寫一段代碼測(cè)試kill調(diào)用:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <cerrno>
#include <cstring>
#include <sys/types.h>int main(int argc, char* argv[])
{if(argc != 3)// 參數(shù)控制為三個(gè){std::cout << "Usage: " << argv[0] << " -signumber pid" << std::endl;return 1;}// argv[0]: ./testSig 運(yùn)行可執(zhí)行程序// argv[1]: 發(fā)送的信號(hào)// argv[2]: 進(jìn)程pidint signumber = std::stoi(argv[1]);// argv[1]int pid = std::stoi(argv[2]);// argv[2]int n = kill(pid, signumber);if(n < 0){std::cerr << "kill error, " << strerror(errno) << std::endl;}return 0;
}

在這里插入圖片描述

??除了kill 系統(tǒng)調(diào)用之外,Linux還提供了一個(gè) raise 接口:

int raise(int sig);

在這里插入圖片描述

  • ?功能傳入信號(hào)給當(dāng)前進(jìn)程(調(diào)用此接口的進(jìn)程)。
  • ?sig參數(shù)需要傳入的信號(hào)。
  • ?返回值0表示成功,非0為失敗

??我們編寫一段代碼測(cè)試raise調(diào)用:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <cerrno>
#include <string>
#include <cstring>
#include <sys/types.h>int main(int argc, char* argv[])
{int cnt = 0;while(true){std::cout << "cnt: " << cnt++ << std::endl;sleep(1);if(cnt == 5)// 5s 后退出進(jìn)程{std::cout << "send 9 signal to caller" << std::endl;raise(9);}}return 0;
}

在這里插入圖片描述
??除了raise系統(tǒng)調(diào)用之外,Linux還提供了一個(gè) abort 接口:

void abort(void)

在這里插入圖片描述

  • ?功能對(duì)自己發(fā)送指定的信號(hào),為6號(hào)信號(hào) SIGABRT。

我們編寫一段代碼測(cè)試abort調(diào)用:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <cerrno>
#include <string>
#include <cstring>
#include <sys/types.h>int main(int argc, char* argv[])
{int cnt = 0;while(true){std::cout << "cnt: " << cnt++ << std::endl;sleep(1);if(cnt == 5)// 5s 后abort進(jìn)程{std::cout << "send 6 signal to caller" << std::endl;abort();}}return 0;
}

在這里插入圖片描述


🚩軟件條件產(chǎn)生信號(hào)

??我們?cè)?jīng)學(xué)過管道,而管道四種特性有一種是 當(dāng)沒有讀端時(shí),寫端會(huì)被終止。管道不具備寫的軟件條件了,所以觸發(fā)了終止信號(hào),這種信號(hào)為 SIGPIPE(13號(hào)信號(hào))。

??除此之外,由軟件條件產(chǎn)生的信號(hào)還有 alarm 函數(shù) 和 SIGALRM(14號(hào)信號(hào)) 信號(hào):

unsigned int alarm(unsigned int seconds);

在這里插入圖片描述

  • ?功能用于試著進(jìn)程鬧鐘,指定時(shí)間(以秒為單位)后,向調(diào)用它的進(jìn)程發(fā)送 SIGALRM 信號(hào)。
  • ?seconds參數(shù)表示在多少秒后發(fā)送14號(hào)新號(hào),如果為0,則任何未響應(yīng)的 鬧鐘被取消
  • ?返回值無符號(hào)整形,表示上次設(shè)置的鬧鐘還剩余的秒數(shù)。之前未設(shè)置鬧鐘,則返回0

??我們?cè)诖a上直觀感受一下鬧鐘的相應(yīng):

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <cerrno>
#include <string>
#include <cstring>
#include <sys/types.h>int main(int argc, char* argv[])
{// 設(shè)置一個(gè)鬧鐘alarm(1);int cnt = 0;while(true){std::cout << "cnt: " << cnt++ << std::endl;}return 0;
}

在這里插入圖片描述
??大概有3萬七千次左右,雖然我們?cè)品?wù)器配置不高,但是3萬7千次未免太少了。我們把打印信息注釋掉,并且設(shè)置一個(gè)全局變量,讓其在循環(huán)內(nèi)一直做++,對(duì)14號(hào)信號(hào)再進(jìn)行捕捉,捕捉回調(diào)方法打印全局變量:

在這里插入圖片描述

??這次運(yùn)行居然有5億多次累加,至于為什么我們前面打印次數(shù)如此的少,這里我給出兩個(gè)原因:

??1.在Linux下,一切皆文件,前面對(duì)屏幕打印文字的行為,本質(zhì)上是對(duì)顯示器文件進(jìn)行瘋狂打印。在前后對(duì)比下,我們能直觀的發(fā)現(xiàn)其實(shí) IO很慢。
??2.由于我們是使用了云服務(wù)器,真正運(yùn)行并不在本地運(yùn)行,我們向云服務(wù)器發(fā)送命令,以及云服務(wù)器將信息從遠(yuǎn)端發(fā)到本地,都是經(jīng)過網(wǎng)絡(luò)的。

??鬧鐘在被設(shè)置的時(shí)候,其默認(rèn)動(dòng)作只會(huì)響一次!如果我們想要設(shè)置多個(gè)鬧鐘,我們可以在回調(diào)handler方法里再加上n秒的鬧鐘,這樣,第一次鬧鐘響了之后,進(jìn)程收到鬧鐘信號(hào)執(zhí)行回調(diào)方法,而main函數(shù)是被循環(huán)卡死的,所以往后就每隔n秒響一次鬧鐘。

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <cerrno>
#include <string>
#include <cstring>
#include <sys/types.h>void handler(int signo)// 收到鬧鐘信號(hào)進(jìn)行回調(diào)
{   std::cout << "get a sign: " << signo << std::endl;int n = alarm(2);// exit(0);
}int main(int argc, char* argv[])
{signal(SIGALRM, handler);// 設(shè)置一個(gè)鬧鐘alarm(4);int cnt = 0;while(true){sleep(1);std::cout << "cnt: " << cnt++ << std::endl;}return 0;
}

在這里插入圖片描述

??如果我們?cè)O(shè)定一個(gè)鬧鐘需要很久之后才會(huì)響應(yīng),但是我在此期間發(fā)送14號(hào)信號(hào)提前對(duì)SIGALRM進(jìn)行捕捉,執(zhí)行handler方法的回調(diào),返回值是什么呢?

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <cerrno>
#include <string>
#include <cstring>
#include <sys/types.h>void handler(int signo)// 收到鬧鐘信號(hào)進(jìn)行回調(diào)
{   std::cout << "get a sign: " << signo << std::endl;unsigned int n = alarm(2);std::cout << "鬧鐘剩余時(shí)間: " << n << std::endl;// exit(0);
}int main(int argc, char* argv[])
{signal(SIGALRM, handler);// 設(shè)置一個(gè)鬧鐘alarm(100);int cnt = 0;while(true){sleep(1);std::cout << "cnt: " << cnt++ << ", pid is: " << getpid() << std::endl;}return 0;
}

在這里插入圖片描述

??我們只對(duì)鬧鐘進(jìn)行了一次kill信號(hào),我們第一個(gè)鬧鐘設(shè)置了100s,而提示信息是每隔一秒打印一次,算下來剛好過去5s,打印出的返回值為95s,而第二次回調(diào)的時(shí)候鬧鐘剩余時(shí)間就變?yōu)?了,這也就證實(shí)了alarm接口返回值是上一次鬧鐘剩余時(shí)間。

??接下來我來解釋一下為什么鬧鐘也能作為軟件條件?我們知道,alarm接口是系統(tǒng)調(diào)用接口,也就是說,設(shè)定鬧鐘,實(shí)際上是在操作系統(tǒng)內(nèi)部設(shè)定的。而操作系統(tǒng)中存在的鬧鐘定然不止一個(gè),所以O(shè)S一定要對(duì)這些鬧鐘做管理,如何管理?先描述,再組織

??根據(jù)以往經(jīng)驗(yàn),鬧鐘一定是有自己的結(jié)構(gòu)體,結(jié)構(gòu)體內(nèi)有著必要的屬性成員,那么在結(jié)構(gòu)體的屬性成員當(dāng)中,就必定有鬧鐘的過期時(shí)間成員,用來記錄鬧鐘的過期時(shí)間!

struct alarm
{uint64_t expired_time;// 鬧鐘過期時(shí)間// ...其他屬性字段
}

??而鬧鐘的過期時(shí)間實(shí)際上是一個(gè)時(shí)間戳,一個(gè)線性增長(zhǎng)的時(shí)間。那我們應(yīng)該以什么樣的結(jié)構(gòu)組織起來這些鬧鐘呢?經(jīng)常看我博客的小伙伴第一反應(yīng)很可能是鏈表。設(shè)置一個(gè)雙鏈表,按照鬧鐘過期時(shí)間來排序,之后我只要找到第一個(gè)過期的鬧鐘,那么在此之后必然全部都是過期鬧鐘。

??雖然這種想法很好,但是我們有更優(yōu)解,我在很早之前寫過一篇博客:堆與堆排序, 而操作系統(tǒng)就是采用最小堆的方法組織鬧鐘結(jié)構(gòu)!以最小堆的堆頂一定是最近一次即將超時(shí)的鬧鐘。所以往后,OS只需要查找堆頂元素,過期了就釋放掉,再通過堆調(diào)整,將次要過期的鬧鐘調(diào)整到堆頂。


異常產(chǎn)生信號(hào)

??第五種信號(hào)的產(chǎn)生方式,程序出了異常,操作系統(tǒng)定然不會(huì)在放任這個(gè)問題進(jìn)程不管,會(huì)采取一定的措施,OS為了能讓程序員知道程序出了問題,于是設(shè)置了一些常出現(xiàn)的異常信號(hào),當(dāng)進(jìn)程出現(xiàn)異常時(shí),OS將會(huì)對(duì)進(jìn)程發(fā)送異常信號(hào),爆出異常緣由。

??異常并不一定是由程序的語法、邏輯問題帶來的,可很有可能是外部設(shè)備出了問題,所以異常又被分為 軟件異常硬件異常。

??軟件異常通常有除零錯(cuò)誤或者溢出錯(cuò)誤引起的。而硬件異常通常是有進(jìn)程訪問無效地址引起的,一般有段錯(cuò)誤等。具體情況具體分析,我們先對(duì)除零錯(cuò)誤進(jìn)行模擬:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <cerrno>
#include <string>
#include <cstring>
#include <sys/types.h>int main(int argc, char* argv[])
{int a = 10;a /= 0;while(1) sleep(1);return 0;
}

在這里插入圖片描述

??除零錯(cuò)誤會(huì)在Shell上爆出 Floating point exception 錯(cuò)誤信息,并且除零錯(cuò)誤對(duì)應(yīng)的信號(hào)是 SIGFPE(8號(hào)信號(hào))。我們?cè)賮砟M一下野指針異常:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <cerrno>
#include <string>
#include <cstring>
#include <sys/types.h>int main(int argc, char* argv[])
{int* p = nullptr;*p = 50;return 0;
}

在這里插入圖片描述

??野指針異常通常會(huì)爆 Segmentation fault 錯(cuò)誤,想必學(xué)C/C++的小伙伴對(duì)這種報(bào)錯(cuò)會(huì)經(jīng)常見到,而其對(duì)應(yīng)的異常信號(hào)為 SIGSEGV(11號(hào)信號(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)程。


🚀對(duì)信號(hào)產(chǎn)生方式的理解

??鍵盤產(chǎn)生信號(hào)

??鍵盤產(chǎn)生數(shù)據(jù)毫無問題,但是操作系統(tǒng)是如何對(duì)組合鍵做出特殊處理的,OS怎么知道我使用Ctrl C 就是要發(fā)送2號(hào)信號(hào)給進(jìn)程呢?我們都知道,操作系統(tǒng)向下管理軟硬件資源,那么鍵盤硬件資源當(dāng)然也屬于操作系統(tǒng)管理范疇。

??所以,我們從鍵盤輸入組合鍵,是由操作系統(tǒng)說的算的,而你使用的組合鍵是被OS解釋為了命令。輸入方在與鍵盤,解釋方為操作系統(tǒng),也就是說,鍵盤輸入的到底是普通數(shù)據(jù)還是特殊命令,是由鍵盤驅(qū)動(dòng)和OS聯(lián)合解釋的。

??這么看來操作系統(tǒng)只需要解釋我們輸入的信息即可,但是對(duì)于OS來說,用戶輸入的動(dòng)作一定是異步的。而我們?cè)谌粘J褂面I盤的時(shí)候,操作系統(tǒng)似乎是優(yōu)先處理的。這樣對(duì)于操作系統(tǒng)的壓力是不是有些大了?要知道OS可不止需要監(jiān)測(cè)鍵盤,還有其他異步硬件,網(wǎng)卡就是典型的例子。OS并不能保證何時(shí)會(huì)不會(huì)有信息從網(wǎng)絡(luò)發(fā)來。

在這里插入圖片描述

??顯然操作系統(tǒng)如果一直對(duì)這些異步發(fā)生的硬件進(jìn)行監(jiān)視的話,會(huì)將操作系統(tǒng)的性能拉低,而操作系統(tǒng)存在的意義就是,向下對(duì)軟硬件資源管理,向上為用戶提供良好服務(wù)。所以定然不會(huì)無時(shí)無刻監(jiān)視這些硬件,于是就有人提出了 硬件中斷技術(shù)

??早在我們電腦開機(jī)的時(shí)候,操作系統(tǒng)就給我們生成了一張 中斷向量表 ,這張表提前注冊(cè)對(duì)軟硬件資源操作的方法。比如此表的二號(hào)下標(biāo)的方法就是用來讀取鍵盤輸入的數(shù)據(jù)。而所謂的中斷向量表實(shí)際上是一個(gè) 函數(shù)指針數(shù)組。那么其究竟是如何實(shí)現(xiàn)通過程序來訪問硬件資源的呢?

??通過馮諾依曼結(jié)構(gòu),我們知道,CPU在數(shù)據(jù)層面只和內(nèi)存級(jí)打交道。但是在每個(gè)CPU上都存在許多的針腳,這些針腳是物理性的,在主板上可以和各個(gè)硬件相連接,包括鍵盤也是通過CPU的針腳連接的。每個(gè)針腳都有自己的編號(hào)。而未來我們?cè)诎存I盤時(shí),通過針腳,使 CPU觸發(fā)硬件中斷!當(dāng)然也沒這么簡(jiǎn)單,他們之間可能存在 8259可中斷控制器,這個(gè)我們目前不需要了解。

在這里插入圖片描述

??不管如何,鍵盤和CPU可以通過針腳相互連接,而用戶在鍵盤上輸入數(shù)據(jù)時(shí)(發(fā)送高電平),就會(huì)觸發(fā)硬件中斷,此時(shí)CPU就可以檢測(cè)到這個(gè)針腳有高電平,從而識(shí)別到鍵盤。而這時(shí),CPU中的寄存器會(huì)將中斷號(hào)(針腳編號(hào))保存在寄存器內(nèi)部,至此,硬件的動(dòng)作就完成了!

??寄存器收到中斷號(hào)后,被操作系統(tǒng)檢測(cè)到,此時(shí)操作系統(tǒng)就會(huì)停下手頭的工作。拿著這個(gè)中斷號(hào)從中斷向量表中查詢(中斷號(hào)就是中斷向量表的下標(biāo)索引)對(duì)應(yīng)處理鍵盤資源的方法,進(jìn)而調(diào)用這個(gè)方法去收集鍵盤發(fā)來的數(shù)據(jù)了。于是就可以把從鍵盤輸入的數(shù)據(jù)讀取到內(nèi)存當(dāng)中了。嚴(yán)格意義上來說,鍵盤文件也是文件,OS會(huì)先將數(shù)據(jù)讀入到鍵盤文件的緩沖區(qū)里。

在這里插入圖片描述

??讀取到的鍵盤數(shù)據(jù)經(jīng)過操作系統(tǒng)對(duì)字符的判定,判定為數(shù)據(jù)則發(fā)送到當(dāng)前進(jìn)程打開的鍵盤文件緩沖區(qū)中,而被判定為控制命令的組合鍵,則會(huì)被解釋為信號(hào),比如我輸入了Ctrl C,那么OS就會(huì)將其解釋為2號(hào)信號(hào),而并非普通字符信息。這時(shí),這個(gè)信號(hào)就會(huì)發(fā)送給調(diào)用鍵盤文件的進(jìn)程,從而執(zhí)行對(duì)應(yīng)的動(dòng)作。

??那么操作系統(tǒng)如何解釋控制命令?實(shí)際上,信號(hào)在到來時(shí),我們?cè)谔幚砀匾氖虑?#xff0c;暫時(shí)不能處理到來的信號(hào),所以我們一定需把信號(hào)保存到PCB中!要知道信號(hào)可是有整整62種,一個(gè)進(jìn)程可能會(huì)存在多個(gè)信號(hào),所以O(shè)S定要對(duì)這些信號(hào)做管理,如何管理?先描述,再組織

??而這些信號(hào)則是由位圖這結(jié)構(gòu)描述組織的!并且這個(gè)位圖只需要32位比特位,因?yàn)闃?biāo)準(zhǔn)信號(hào)只有31種,所以32位比特位完全夠用。如果用戶通過鍵盤對(duì)當(dāng)前用戶輸入了Ctrl C,則會(huì)被操作系統(tǒng)解釋為2號(hào)信號(hào),通過位圖,將第對(duì)應(yīng)的比特位由0置1即可完成OS對(duì)進(jìn)程發(fā)送信號(hào)。也就是說,操作系統(tǒng)向進(jìn)程發(fā)送信號(hào)的本質(zhì)是對(duì)進(jìn)程PCB的位圖進(jìn)行寫入操作!

在這里插入圖片描述


??異常信號(hào)的理解

??前面出現(xiàn)了 除零錯(cuò)誤(SIGFPE),以及野指針錯(cuò)誤(SIGSEGV)都屬于異常產(chǎn)生的信號(hào),首先我們來分析除零錯(cuò)誤。

計(jì)算錯(cuò)誤

??除零錯(cuò)誤,實(shí)際上也就是計(jì)算錯(cuò)誤,在硬件方面,計(jì)算錯(cuò)誤表現(xiàn)在CPU的寄存器上,我們知道,程序的計(jì)算都是在寄存器內(nèi)完成的,寄存器可以存貯少量數(shù)據(jù),而當(dāng)計(jì)算發(fā)生錯(cuò)誤,CPU停止對(duì)進(jìn)程的操作,轉(zhuǎn)而告訴操作系統(tǒng)當(dāng)前處理的進(jìn)程發(fā)生了計(jì)算錯(cuò)誤。

??在CPU中存在一個(gè)標(biāo)志寄存器,EFLAGS/RFLAGS標(biāo)志寄存器,其 用于檢測(cè)有符號(hào)數(shù)運(yùn)算是否發(fā)生了溢出。這個(gè)寄存器存在一個(gè)名為 OF(OverFlow) 的標(biāo)志位。當(dāng)發(fā)生溢出錯(cuò)誤時(shí),OF被置為1,否則被置為0。

在這里插入圖片描述

??操作系統(tǒng)收到CPU發(fā)來的信息,發(fā)現(xiàn)進(jìn)程不再被調(diào)度了,于是操作系統(tǒng)就會(huì)檢查EFLAGS/RFLAGS寄存器的溢出標(biāo)記位OF,從而檢測(cè)出當(dāng)前進(jìn)程出了計(jì)算異常,于是 OS就對(duì)當(dāng)前進(jìn)程發(fā)送(向pcb內(nèi)寫入)8號(hào)信號(hào)

??當(dāng)我們對(duì)除零錯(cuò)誤的異常信號(hào)進(jìn)行捕捉,并且保持進(jìn)程一直在運(yùn)行狀態(tài):

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <cerrno>
#include <string>
#include <cstring>
#include <sys/types.h>void handler(int signo)
{std::cout << "get a sig: " << signo << std::endl;
}int main(int argc, char* argv[])
{signal(SIGFPE, handler);int a = 10;int b = 0;a = a / b;while(true) sleep(1);return 0;
}

在這里插入圖片描述

??我們把異常信號(hào)的默認(rèn)執(zhí)行方式進(jìn)行了捕捉,從而執(zhí)行我們的handler方法回調(diào)。而handler方法我們僅僅打印了一句話,所以這個(gè)異常信號(hào)依舊存在。我們都知道,在CPU中寄存器只有一套,而 寄存器的數(shù)據(jù)可以有很多,這些數(shù)據(jù)我們稱為 進(jìn)程的上下文數(shù)據(jù)。

??但是當(dāng)前進(jìn)程被我們?cè)O(shè)置為一直在運(yùn)行,異常在進(jìn)程中仍然存在,這個(gè)時(shí)候OS又會(huì)向OF讀取異常數(shù)據(jù),進(jìn)而再一次的對(duì)當(dāng)前進(jìn)程發(fā)送8號(hào)信號(hào),這樣不斷的循環(huán),就導(dǎo)致了上圖的結(jié)果。

野指針異常

??野指針異常是因?yàn)榉欠ㄔL問地址,而我們通過之前的學(xué)習(xí),我們知道野指針內(nèi)的地址一定是虛擬地址,而虛擬地址要映射到物理內(nèi)存需要經(jīng)過 由OS和CPU(MMU) 的 轉(zhuǎn)化。而既然存在轉(zhuǎn)化,就一定存在轉(zhuǎn)化成功或者失敗,我們來討論一下轉(zhuǎn)化失敗的情況。

??在CPU中還存在兩個(gè)很重要的寄存器:CR2CR3 寄存器,其中cr2寄存器 用于存儲(chǔ)導(dǎo)致頁表映射錯(cuò)誤的虛擬地址。而cr3寄存器用于 存儲(chǔ)頁表的基地址,指向當(dāng)前頁表?,F(xiàn)代電腦上的 MMU單元 都是被集成的CPU上的,其用于 虛擬到物理地址之間的轉(zhuǎn)換。

??當(dāng)程序中發(fā)生了野指針錯(cuò)誤,比如對(duì)空地址解引用賦值。此時(shí)當(dāng)CPU執(zhí)行到該語句的時(shí)候,會(huì)將指針內(nèi)保存的虛擬地址由MMU和OS經(jīng)過頁表轉(zhuǎn)化為物理地址,但是在轉(zhuǎn)化的時(shí)候,頁表中可能沒有該虛擬地址的映射或者該映射的物理地址不可被寫入(無w權(quán)限)。此時(shí)MMU就會(huì)轉(zhuǎn)化失敗,于是就向cr2寄存器寫入出錯(cuò)的虛擬地址,cr2將虛擬地址保存。

在這里插入圖片描述

??當(dāng)進(jìn)程出現(xiàn)了野指針異常時(shí),當(dāng)前進(jìn)程就會(huì)停止調(diào)度,OS就會(huì)來檢查為何當(dāng)前進(jìn)程停止調(diào)度,而CPU對(duì)cr2寄存器進(jìn)行讀取,發(fā)現(xiàn)當(dāng)前進(jìn)程出現(xiàn)了野指針錯(cuò)誤,于是OS就對(duì)當(dāng)前進(jìn)程發(fā)送11號(hào)信號(hào)(SIGSEGV)從而終止進(jìn)程!

總結(jié)

??所以產(chǎn)生信號(hào)不論是系統(tǒng)調(diào)用還是軟件條件,亦或者是鍵盤、異常產(chǎn)生的信號(hào),都是由操作系統(tǒng)同一發(fā)送的,因?yàn)镺S作為軟硬件資源的管理者,當(dāng)進(jìn)程出現(xiàn)異常時(shí),需要對(duì)進(jìn)程做相應(yīng)的處理,這也就是為什么我們?cè)趙indows下運(yùn)行一些帶有錯(cuò)誤的程序時(shí),進(jìn)程會(huì)直接終止。


??今天的文章到此結(jié)束,感謝您的觀看,如果對(duì)您有幫助的話還望給博主一個(gè)小小的三連呀~~

http://m.risenshineclean.com/news/61920.html

相關(guān)文章:

  • 當(dāng)日網(wǎng)站收錄查詢統(tǒng)計(jì)網(wǎng)店推廣軟文范例
  • 收到一張網(wǎng)站服務(wù)費(fèi)怎么做憑證在線的crm系統(tǒng)軟件
  • 個(gè)人博客網(wǎng)站下載百度網(wǎng)站制作聯(lián)系方式
  • 有什么做公眾號(hào)封面圖的網(wǎng)站沒廣告的視頻播放器app
  • 微信公眾官網(wǎng)登錄入口整站seo排名外包
  • 新手學(xué)做網(wǎng)站書國(guó)際足聯(lián)世界排名
  • 怎么做國(guó)際購(gòu)物網(wǎng)站搜索引擎調(diào)詞工具哪個(gè)好
  • 珠寶網(wǎng)站開發(fā)免費(fèi)的網(wǎng)站域名查詢app
  • 現(xiàn)在網(wǎng)站建設(shè)用什么軟件如何接廣告賺錢
  • 易語言用電腦做網(wǎng)站服務(wù)器百度關(guān)鍵詞自然排名優(yōu)化公司
  • 平頂山車禍最新新聞事件百度seo什么意思
  • linux下用python做網(wǎng)站百度推廣代理怎么加盟
  • 用網(wǎng)站做自我介紹自己四川seo排名
  • 做流媒體視頻播放網(wǎng)站求助市場(chǎng)營(yíng)銷考試題目及答案2022
  • 廈門網(wǎng)站設(shè)計(jì)公司seo sem論壇
  • html怎么添加背景圖片四川整站優(yōu)化關(guān)鍵詞排名
  • 網(wǎng)站開發(fā)在哪里接活網(wǎng)站查詢工具
  • 做論壇網(wǎng)站需要什么備案廈門seo代運(yùn)營(yíng)
  • 做一個(gè)營(yíng)銷型網(wǎng)站手機(jī)網(wǎng)站建設(shè)公司
  • 吉林省建設(shè)局網(wǎng)站軟文推廣什么意思
  • 衡水哪有做網(wǎng)站的網(wǎng)上宣傳廣告怎么做
  • 南京自助建站網(wǎng)站社群營(yíng)銷策略有哪些
  • 怎么做外貿(mào)推廣網(wǎng)站搜索關(guān)鍵詞優(yōu)化
  • 怎樣做能直接上傳微信的視頻網(wǎng)站莆田網(wǎng)站建設(shè)優(yōu)化
  • 做社交網(wǎng)站開發(fā)怎么建網(wǎng)站免費(fèi)的
  • 網(wǎng)站開發(fā)代做外貿(mào)網(wǎng)站制作推廣
  • 沒網(wǎng)站域名可以做備案嗎百度熱度
  • 塘沽網(wǎng)站建設(shè)網(wǎng)站建設(shè)方案內(nèi)容
  • 域名怎么解析到網(wǎng)站網(wǎng)絡(luò)營(yíng)銷策略的制定
  • 新民正規(guī)網(wǎng)站建設(shè)價(jià)格咨詢高級(jí)seo是什么職位