Linux進程——信號詳解(上)

文章目錄

  • 信號入門
    • 生活角度的信號
    • 技術應用角度的信號
    • 用kill -l命令可以察看系統定義的信號列表
    • 信號處理常見方式概述
  • 產生信號
    • 通過鍵盤進行信號的產生,```ctrl+c```向前臺發送2號信號
    • 通過系統調用
    • 異常
    • 軟件條件

信號入門

生活角度的信號

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

技術應用角度的信號

#include <stdio.h>
#include <unistd.h>int main()
{while (1){printf("I am a process, I am waiting signal!\n");sleep(1);}
}

在這里插入圖片描述

我們知道上述代碼是死循環的,我們使用ctrl+c來終止掉這個進程,本質是鍵盤向CPU發送了一個中斷被操作系統獲取并解釋成信號(ctrl+c被解釋成2號信號),最后操作系統將2號信號發送給目標前臺進程,當前臺進程收到2號信號后就會退出。
按照文章開頭所談的話,進程就是我,操作系統就是快遞員,信號就是快遞

注意:

  • ctrl+c產生的信號只能發給前臺進程。
  • shell可以同時運行一個前臺進程和任意多個后臺進程,只有前臺進程才能接到像 ```ctrl+c``這種控制鍵產生的信號
  • 前臺進程在運行過程中用戶隨時可能按下 ctrl+c而產生一個信號,也就是說該進程的用戶空間代碼執行到任何地方都有可能收到 SIGINT 信號而終止,所以信號相對于進程的控制流程來說是異步(Asynchronous)的。
  1. 我們可以使用jobs來查看正在執行的任務
  2. fg number將后臺中的命令調至前臺繼續運行
  3. bg number將一個在后臺暫停的命令變成繼續執行
  4. &加在一個命令的最后,可以把這個命令放到后臺執行;
    (./xxx &)這樣就可以將進程放在后臺
  5. ctrl+z可以將一個正在前臺執行的命令放到后臺,并且處于暫停狀態,不可執行

用kill -l命令可以察看系統定義的信號列表

kil -l
在這里插入圖片描述
其中1 - 31號信號是普通信號,34 - 64號信號是實時信號

我們看到上面這一堆的大寫字母 + 數字,不難想到它們是使用了宏


信號是如何記錄的?
使用位圖,如果該位置為1即該信號被收到

在這里插入圖片描述
所以信號產生的本質上就是操作系統直接去修改目標進程的task_struct中的信號位圖。

信號處理常見方式概述

  • 執行該信號的默認處理動作。
  • 忽略此信號。
  • 提供一個信號處理函數,內核在處理該信號時切換到用戶態執行這個處理函數,這種方式稱為捕捉(Catch)一個信號;自定義的——信號的捕捉

我們可以使用man 7 signal來查看信號的默認處理動作。
在這里插入圖片描述

產生信號

通過鍵盤進行信號的產生,ctrl+c向前臺發送2號信號

#include <stdio.h>
#include <unistd.h>int main()
{while (1){printf("hello signal!\n");sleep(1);}return 0;
}

除此之外,我們還可以使用ctrl+\來終止進程
在這里插入圖片描述
那么它們兩個有什么區別呢???
在這里插入圖片描述

我們發現ctrl+c對應的行為是Term,而ctrl+\對應的行為是Core,二者都代表著終止進程,但是Core會多進行一個動作,那就是核心轉儲
在這里插入圖片描述

何為核心轉儲???
首先, 在云服務器中,核心轉儲是默認被關掉的,我們可以通過使用ulimit -a命令查看當前資源限制的設定。
在這里插入圖片描述

第一行顯示core文件的大小為0,即表示核心轉儲是被關閉的。

我們可以通過ulimit -c size命令來設置core文件的大小。
在這里插入圖片描述

core文件的大小設置完畢后,就將核心轉儲功能打開了。此時如果我們再使用ctrl+\對進程進行終止,就會發現終止進程后會顯示core dumped
在這里插入圖片描述

并且在該路徑下生成了一個core.pid文件
其中pid,是一串數字,而這一串數字就是發生這一次核心轉儲的進程的PID。在這里插入圖片描述

核心轉儲有什么用呢???
當我們的代碼報錯以后,我們總需要找到報錯原因;
當我們的程序在運行過程中崩潰了,我們會通過調試來進行逐步查找程序崩潰的原因;
當我們的程序在運行結束了,那么我們可以通過退出碼來判斷代碼出錯的原因;
而在某些特殊情況下,我們會使用核心轉儲,核心轉儲是操作系統在進程收到某些信號而終止運行時,將該進程地址空間的內容以及有關進程狀態的其他信息轉而存儲到一個磁盤文件當中,這個磁盤文件也叫做核心轉儲文件,一般命名為core.pid

#include <stdio.h>int main()
{printf("I am Div\n");int a = 10;a /= 0;return 0;
}

在這里插入圖片描述

顯然,上述代碼出現了除零錯誤


ctrl+其他有什么效果呢???

我們可以通過以下代碼,將1~31號信號全部進行捕捉,將收到信號后的默認處理動作改為打印收到信號的編號。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>void handler(int signal)
{printf("get a signal:%d\n", signal);
}
int main()
{int signo;for (signo = 1; signo <= 31; signo++){signal(signo, handler);}while (1){sleep(1);}return 0;
}

在這里插入圖片描述

但我們發送9號進程,它并不會收到,而是執行收到9號信號后的默認處理動作,即被殺死。
所以,對于某些信號是不可以被自定義處理的。比如9號信,因為如果所有信號都能被捕捉的話,那么進程就可以將所有信號全部進行捕捉并將動作設置為忽略,此時該進程將無法被殺死,即便是操作系統也做不到。

通過系統調用

我們可以以kill -信號編號 進程ID的形式進行發送。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>int main()
{while (true){printf("I am a running process..., mypid = %d\n", getpid());sleep(1);}return 0;
}

在這里插入圖片描述

kill函數

int kill(pid_t pid, int sig);

實際上,kill指令的底層就是 kill函數

kill函數用于向進程IDpid的進程發送sig號信號,如果信號發送成功,則返回0,否則返回-1。


raise函數
raise函數可以給當前進程發送指定信號,即自己給自己發送信號

int raise(int sig);

用raise函數每隔一秒向自己發送一個2號信號。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>void handler(int signo)
{printf("get a signal:%d\n", signo);
}
int main()
{signal(2, handler);while (1){sleep(1);raise(2);}return 0;
}

在這里插入圖片描述


abort函數
raise函數可以給當前進程發送SIGABRT信號,使得當前進程異常終止

void abort(void);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>void handler(int signo)
{printf("get a signal:%d\n", signo);
}
int main()
{signal(6, handler);while (1){sleep(1);abort();}return 0;
}

在這里插入圖片描述
abort函數的作用是異常終止進程
exit 函數的作用是正常終止進程

異常

當我們程序當中出現類似于除0、野指針、越界之類的錯誤時,為什么程序會崩潰?本質上是因為進程在運行過程中收到了操作系統發來的信號進而被終止,那操作系統是如何識別到一個進程觸發了某種問題的呢?

我們知道,CPU當中有一堆的寄存器,當我們需要對兩個數進行算術運算時,我們是先將這兩個操作數分別放到兩個寄存器當中,然后進行算術運算并把結果寫回寄存器當中。此外,CPU當中還有一組寄存器叫做狀態寄存器,它可以用來標記當前指令執行結果的各種狀態信息,如有無進位、有無溢出等等。而操作系統是軟硬件資源的管理者,在程序運行過程中,若操作系統發現CPU內的某個狀態標志位被置位,而這次置位就是因為出現了某種除0錯誤而導致的,那么此時操作系統就會馬上識別到當前是哪個進程導致的該錯誤,并將所識別到的硬件錯誤包裝成信號發送給目標進程,本質就是操作系統去直接找到這個進程的task_struct,并向該進程的位圖中寫入8信號,寫入8號信號后這個進程就會在合適的時候被終止。

那對于下面的野指針問題,或者越界訪問的問題時,操作系統又是如何識別到的呢?
在這里插入圖片描述

在這里插入圖片描述
當我們要訪問一個變量時,一定要先經過頁表的映射,將虛擬地址轉換成物理地址,然后才能進行相應的訪問操作。

其中頁表屬于一種軟件映射關系,而實際上在從虛擬地址到物理地址映射的時候還有一個硬件叫做MMU,它負責處理CPU的內存訪問請求的計算機硬件,因此映射工作不是由CPU做的,而是MMU做的,但現在MMU已經集成到CPU當中了。
當需要進行虛擬地址到物理地址的映射時,我們先將頁表的左側的虛擬地址導給MMU,然后MMU會計算出對應的物理地址,我們再通過這個物理地址進行相應的訪問。
而MMU既然是硬件單元,那么它當然也有相應的狀態信息,當我們要訪問不屬于我們的虛擬地址時,MMU在進行虛擬地址到物理地址的轉換時就會出現錯誤,然后將對應的錯誤寫入到自己的狀態信息當中,這時硬件上面的信息也會立馬被操作系統識別到,進而將對應進程發送SIGSEGV信號。


C/C++程序會崩潰,是因為程序當中出現的各種錯誤最終一定會在硬件層面上有所表現,進而會被操作系統識別到,然后操作系統就會發送相應的信號將當前的進程終止。

軟件條件

SIGPIPE信號
SIGPIPE信號實際上就是一種由軟件條件產生的信號,當進程在使用管道進行通信時,讀端進程將讀端關閉,而寫端進程還在一直向管道寫入數據,那么此時寫端進程就會收到SIGPIPE信號進而被操作系統終止。

下面代碼當中,創建匿名管道進行父子進程之間的通信,其中父進程是讀端進程,子進程是寫端進程,但是一開始通信父進程就將讀端關閉了,那么此時子進程在向管道寫入數據時就會收到SIGPIPE信號,進而被終止。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{int fd[2] = { 0 };if (pipe(fd) < 0){ //使用pipe創建匿名管道perror("pipe");return 1;}pid_t id = fork(); //使用fork創建子進程if (id == 0){//childclose(fd[0]); //子進程關閉讀端//子進程向管道寫入數據const char* msg = "hello father, I am child...";int count = 10;while (count--){write(fd[1], msg, strlen(msg));sleep(1);}close(fd[1]); //子進程寫入完畢,關閉文件exit(0);}//fatherclose(fd[1]); //父進程關閉寫端close(fd[0]); //父進程直接關閉讀端(導致子進程被操作系統殺掉)int status = 0;waitpid(id, &status, 0);printf("child get signal:%d\n", status & 0x7F); //打印子進程收到的信號return 0;
}

在這里插入圖片描述


alarm函數

unsigned int alarm(unsigned int seconds);

alarm函數可以設定一個鬧鐘,也就是告訴操作系統在若干時間后發送SIGALRM信號給當前進程

alarm函數的返回值:

  • 若調用alarm函數前,進程已經設置了鬧鐘,則返回上一個鬧鐘時間的剩余時間,并且本次鬧鐘的設置會覆蓋上一次鬧鐘的設置。
  • 如果調用alarm函數前,進程沒有設置鬧鐘,則返回值為0。

用下面的代碼,測試自己的云服務器一秒時間內可以將一個變量累加到多大。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>int main()
{int count = 0;alarm(1);while (1){count++;printf("count: %d\n", count);}return 0;
}

在這里插入圖片描述
我們發現加的變量好像有點小,原有有兩點:

  1. 由于我們每進行一次累加就進行了一次打印操作,而與外設之間的IO操作所需的時間要比累加操作的時間更長
  2. 我當前使用的是云服務器,因此在累加操作后還需要將累加結果通過網絡傳輸將服務器上的數據發送過來,因此最終顯示的結果要比實際一秒內可累加的次數小得多。

為了盡可能避免上述問題,我們可以先讓count變量一直執行累加操作,直到一秒后進程收到SIGALRM信號后再打印累加后的數據。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>int count = 0;
void handler(int signo)
{printf("get a signal: %d\n", signo);printf("count: %d\n", count);exit(1);
}
int main()
{signal(SIGALRM, handler);alarm(1);while (1){count++;}return 0;
}

在這里插入圖片描述
count變量在一秒內被累加的次數變成了四億多,所以,我們得出一個結論與計算機單純的計算相比較,計算機與外設進行IO時的速度是非常慢的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/715259.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/715259.shtml
英文地址,請注明出處:http://en.pswp.cn/news/715259.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

前端面試練習24.3.2-3.3

HTMLCSS部分 一.說一說HTML的語義化 在我看來&#xff0c;它的語義化其實是為了便于機器來看的&#xff0c;當然&#xff0c;程序員在使用語義化標簽時也可以使得代碼更加易讀&#xff0c;對于用戶來說&#xff0c;這樣有利于構建良好的網頁結構&#xff0c;可以在優化用戶體…

vue3項目中如何一個vue組件中的一個div里面的圖片鋪滿整個屏幕樣式如何設置

在Vue 3項目中&#xff0c;要使一個div內的圖片鋪滿整個屏幕&#xff0c;你需要確保幾個關鍵點&#xff1a;div元素和圖片元素的樣式設置正確&#xff0c;以及確保它們能夠覆蓋整個視口&#xff08;viewport&#xff09;。以下是一個簡單的步驟和代碼示例&#xff0c;幫助你實現…

代碼隨想錄算法訓練營第四八天 | 買股票

目錄 只買賣一次可買賣多次 LeetCode 121. 買賣股票的最佳時機 LeetCode 122. 買賣股票的最佳時機II 只買賣一次 給定一個數組 prices &#xff0c;它的第 i 個元素 prices[i] 表示一支給定股票第 i 天的價格。 你只能選擇 某一天 買入這只股票&#xff0c;并選擇在 未來的某…

瀏覽器輸入URL到頁面渲染經歷了哪些過程?

瀏覽器輸入URL到頁面渲染的過程可以分為以下幾個步驟&#xff1a; 解析URL&#xff1a;當用戶在瀏覽器的地址欄輸入URL后&#xff0c;瀏覽器會首先解析這個URL&#xff0c;判斷其是否合法。查找緩存&#xff1a;瀏覽器會查看自己的緩存&#xff0c;判斷是否有之前訪問過的這個U…

論文閱讀--Diffusion Models for Reinforcement Learning: A Survey

一、論文概述 本文主要內容是關于在強化學習中應用擴散模型的綜述。文章首先介紹了強化學習面臨的挑戰&#xff0c;以及擴散模型如何解決這些挑戰。接著介紹了擴散模型的基礎知識和在強化學習中的應用方法。然后討論了擴散模型在強化學習中的不同角色&#xff0c;并對其在多個…

【JavaSE】實用類——String、日期等

目錄 String類常用方法String類的equals()方法String中equals()源碼展示 “”和equals()有什么區別呢&#xff1f; StringBuffer類常用構造方法常用方法代碼示例 面試題&#xff1a;String類、StringBuffer類和StringBuilder類的區別&#xff1f;日期類Date類Calendar類代碼示例…

leetcode169. 多數元素的四種解法

leetcode169. 多數元素 題目描述 給定一個大小為 n 的數組 nums &#xff0c;返回其中的多數元素。多數元素是指在數組中出現次數 大于? n/2 ? 的元素。 你可以假設數組是非空的&#xff0c;并且給定的數組總是存在多數元素。 1.哈希 class Solution { public:int majority…

【vue3】命令式組件封裝,message封裝示例;(函數式組件?)

僅做代碼示例&#xff1b;當然改進的地方還是不少的&#xff0c;僅作為該類組件封裝方式的初步啟發&#xff1b; 理想大成肯定是想要像 餓了么 這些組件庫一樣。 有的人叫這函數式組件&#xff0c;有的人叫這命令式組件&#xff0c;我個人還是偏向于命令式組件的稱呼。因為以vu…

Django配置靜態文件

Django配置靜態文件 目錄 Django配置靜態文件靜態文件配置調用方法 一般我們將html文件都放在默認templates目錄下 靜態文件放在static目錄下 static目錄大致分為 js文件夾css文件夾img文件夾plugins文件夾 在瀏覽器輸入url能夠看到對應的靜態資源&#xff0c;如果看不到說明…

向爬蟲而生---Redis 探究篇4<Redis主從復制(2)>

前言: 繼續上一篇向爬蟲而生---Redis 探究篇4&#xff1c;Redis主從復制(1)&#xff1e;-CSDN博客 正文: 讀寫操作和一致性保證 主節點和從節點對讀寫操作的不同處理方式 在Redis主從復制中&#xff0c;主節點和從節點對讀寫操作有不同的處理方式&#xff1a; 主節點&…

vim文本編輯器 的命令及快捷鍵

vim文本編輯器常用的命令及快捷鍵 vim文本編輯器功能命令 命令功能i從光標當前位置進入插入模式a從光標下一位進入插入模式ESC鍵退出編輯模式dd刪除2dd刪除兩行u撤銷上一步操作wq保存并退出0光標移動至文本開頭G光標移至文本末尾$光標移動至行尾^光標移動至行首q或q!退出不保…

支持向量機算法(帶你了解原理 實踐)

引言 在機器學習和數據科學中&#xff0c;分類問題是一種常見的任務。支持向量機&#xff08;Support Vector Machine, SVM&#xff09;是一種廣泛使用的分類算法&#xff0c;因其出色的性能和高效的計算效率而受到廣泛關注。本文將深入探討支持向量機算法的原理、特點、應用&…

13. Springboot集成Protobuf

目錄 1、前言 2、Protobuf簡介 2.1、核心思想 2.2、Protobuf是如何工作的&#xff1f; 2.3、如何使用 Protoc 生成代碼&#xff1f; 3、Springboot集成 3.1、引入依賴 3.2、定義Proto文件 3.3、Protobuf生成Java代碼 3.4、配置Protobuf的序列化和反序列化 3.5、定義…

【中英對照】【自譯】【精華】麻省理工學院MIT技術雙月刊(Bimonthly MIT Technology Review)2024年3/4月刊內容概覽

一、說明 Notation 僅供學習、參考&#xff0c;請勿用于商業行為。 二、本期封面、封底 Covers 本期雜志購于新加坡樟宜機場Changi Airport Singapore&#xff0c;售價為20.50新元。 本期仍然關注倫敦的AI大會。&#xff08;筆者十分想去&#xff0c;在倫敦和MIT校園均設有會…

IDEA的安裝教程

1、下載軟件安裝包 官網下載&#xff1a;https://www.jetbrains.com/idea/ 2、開始安裝IDEA軟件 解壓安裝包&#xff0c;找到對應的idea可執行文件&#xff0c;右鍵選擇以管理員身份運行&#xff0c;執行安裝操作 3、運行之后&#xff0c;點擊NEXT&#xff0c;進入下一步 4、…

手動、半自動、全自動探針臺有何區別

手動探針臺、半自動探針臺和全自動探針臺是三種不同類型的探針臺&#xff0c;它們在使用類型、功能、操作方式和價格等方面都有所不同。 手動探針臺是一種手動控制的探針臺&#xff0c;通常用于沒有很多待測器件需要測量或數據需要收集的情況下。該類探針臺的優點是靈活、可變…

python difflib --- 計算差異的輔助工具

此模塊提供用于比較序列的類和函數。 例如&#xff0c;它可被用于比較文件&#xff0c;并可產生多種格式的不同文件差異信息&#xff0c;包括 HTML 和上下文以及統一的 diff 數據。 有關比較目錄和文件&#xff0c;另請參閱 filecmp 模塊。 class difflib.SequenceMatcher 這…

WebAssembly 是啥東西

WebAssembly&#xff08;簡稱Wasm&#xff09;是一種為網絡瀏覽器設計的二進制指令格式&#xff0c;它旨在成為一個高效的編程語言的編譯目標&#xff0c;從而允許在網絡上部署客戶端和服務器應用程序。WebAssembly的主要設計目標是實現高性能應用&#xff0c;同時維持網絡的安…

GraphPad Prism 10: 你的數據,我們的魔法 mac/win版

GraphPad Prism 10是GraphPad Software公司推出的一款功能強大的數據分析和可視化軟件。它集數據整理、統計分析、圖表制作和報告生成于一體&#xff0c;為科研工作者、學者和數據分析師提供了一個高效、便捷的工作平臺。 GraphPad Prism 10軟件獲取 Prism 10擁有豐富的圖表類…

2023義烏最全“電商+跨境+直播”數據總結篇章!

值得收藏&#xff5c;2023義烏最全“電商跨境直播”數據總結篇章&#xff01; 麥琪享資訊2024-01-20 14:28浙江 新年伊始&#xff0c;央視就把鏡頭對準了義烏電商&#xff0c;以電商的蓬勃之勢展現這座國際商城的開放與活力。 過去的一年 義烏電商量質齊升 實力出圈 跑出了…