進程信號 signal

文章目錄

  • 信號基礎
  • 信號的產生
    • OS中的時間
  • 信號的保存
    • sigset_t
    • sigprocmask
    • sigpending
  • 信號的捕捉
    • 用戶態和內核態
    • sigaction
    • volatile
  • SIGCHLD

信號基礎

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

總而言之

  1. 信號沒有產生的時候我們已經知道怎么處理這個信號了
  2. 信號的到來,我們并不清楚具體是什么時候,信號對于我現在正在左的工作是異步產生的。
  3. 信號產生了我們不一定要立即處理它,而是在合適的時候去處理
  4. 因為我們不一定會要立即處理它,所以我們要有對信號的保存能力

信號:信號是一種向目標進程發送通知消息的一種機制。

所以進程在收到信號之前已經知道了有哪些信號并且知道對應信號的處理方法。

在Linux中可以通過kill -l 查看所有的信號。
在這里插入圖片描述
并且在進程能夠通過自己的PCB找到一張函數指針數組,數組的下標對應的就是各個信號的編號,數組的內容就是對應信號的處理方法。這么多的信號中1 - 34 號信號為普通信號,剩下的為實時信號,我們只說普通信號。

一個信號的處理方法分為三種:

  1. 默認行為
  2. 忽略
  3. 自定義

我們是可以通過signal修改對于信號的執行方法。 其中9號信號為管理員信號,默認方法不能被修改。
在這里插入圖片描述
第二個參數設置為SIG_DFL就是默認行為,設置為SIGIGN就是忽略。
假設我們現在修改二號信號的默認行為

#include <iostream>
#include <unistd.h>
#include <signal.h>
void sigcb(int signal)
{std::cout << "get a singal :" << signal << std::endl;exit(0);
}
int main()
{signal(2,sigcb);while(true){std::cout << "run.." << std::endl;sleep(1);}return 0;
}

信號的產生

在命令行shell中,前臺命令(./xxx)只能有一個,后臺命令(./xxx &)可以有多個,前臺進程是不能被暫停(ctrl + z),如果被暫停,該前臺進程要立即被放到后臺。OS會自動的把shell自動的提到前臺或者后臺。ctrl + c一般情況下可以終止一個前臺進程。判斷是不是前臺進程可以看有沒有接受用戶輸入的能力,有就是前臺進程。

LInux中可以通過jobs命令查看后臺進程,fg + num 可以把一個后臺進程提到前臺,bg + num 可以啟動一個被暫停的后臺任務。

OS是怎么知道鍵盤有數據準備就緒了呢?
CPU其實和外設也是相連的,CPU上有很多針腳,硬件中有一個8269,作為針腳和硬件的中間設備,因為外設很多,CPU的針腳有限,所以可以通過這個設備把多的外設和CPU連接起來,然后當鍵盤有數據了,會通過針腳產生硬件中斷,OS中會有一張中斷向量表(函數指針數組),然后每個硬件都有自己的編號,CPU有一個寄存器專門存儲硬件的中斷號,數組的下標就是對于硬件的編號,數組的內容就是硬件的讀取方法,所以CPU接收到了硬件中斷,然后直接通過數組下標找到對于的方法,然后把內容加載到內存。

信號產生的方式:

  1. 可以通過鍵盤產生
    ctrl + c (發送2號信號終止進程)
    ctrl + z (暫停進程,發送19號信號)
    ctrl + \ (終止進程,發送3號信號)

  2. 通過系統調用
    kill命令是調用kill函數實現的。kill函數可以給一個指定的進程發送指定的信號。raise函數可以給當前進程發送指定的信號(自己給自己發信號)。
    在這里插入圖片描述
    abort函數使當前進程接收到信號而異常終止。 并且abort就算被signal重定義,就算最后我們沒有終止進程,它自己最后也會終止進程。
    在這里插入圖片描述

  3. 異常
    硬件異常被硬件以某種方式被硬件檢測到并通知內核,然后內核向當前進程發送適當的信號。例如當前進程執行了除以0的指令,CPU的運算單元會產生異常,內核將這個異常解釋為SIGFPE(8)信號發送給進程。再比如當前進程訪問了非法內存地址,MMU會產生異常,內核將這個異常解釋為SIGSEGV(11)信號發送給進程。

#include <iostream>
#include <signal.h>void handler(int signo)
{std::cout << "run.." << std::endl;
}int main()
{signal(8,handler);int a = 6;a /= 0;return 0;
}

這段代碼會出現死循環的情況 ,原因就是因為出現除0錯誤,然后CPU硬件報錯,然后處理方法就是讓OS給目標進程發信號并且把該進程剝離CPU,但是我們對8號信號進行自定義,沒有退出進程,然后當CPU再一次調度這個進程時,接著出錯,重復之前的動作。

  1. 軟件條件
    管道的一種特性當讀端退出,寫端無意義,OS就會寫端發送SIGPIPE信號,SIGPIPE是一種由軟件條件產生的信號。除了這個以外還有alarm函數 和SIGALRM信號。
    在這里插入圖片描述
    這個函數的返回值是0或者是以前設定的鬧鐘時間還余下的秒數。如果seconds值為0,表示取消以前設定的鬧鐘,函數的返回值仍然是以前設定的鬧鐘時間還余下的秒數。

總而言之信號產生的方式多種多樣,但是信號發送都是由OS來發送的。

OS中的時間

  1. 所有的用戶行為都是以進程的形式在OS中表現的。
  2. OS只要把進程管理號就能完成所有的用戶任務。
  3. CMOS會周期性高頻的像CPU發送時鐘中斷。

我們知道我們自己寫的代碼是由OS來調度執行的,但是OS的代碼是誰來調度的呢?
CMOS向CPU發送時鐘中斷就是讓CPU來執行OS的代碼的,他會給CPU一個操作數,然后OS通過這個操作數在中斷向量表中索引下標,數組的內容就是OS的調度方法,所以OS的執行是基于硬件中斷的。。

所以對OS樸素的理解就是OS在電腦開機時完成各種的初始化工作后,開始進入死循環執行自己的調度方法。

信號的保存

信號的其他概念

  1. 實際執行信號的處理動作稱為信號遞達(Delivery)
  2. 信號從產生到遞達之間的狀態,稱為信號未決(Pending)。
  3. 進程可以選擇阻塞 (Block )某個信號。一旦被阻塞,就不能遞達,直到對該信號解除屏蔽。
  4. 被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行遞達的動作.注意,阻塞和忽略是不同的,只要信號被阻塞就不會遞達,而忽略是在遞達之后可選的一種處理動作。

信號在內核中其實是很好表示的,因為我們只需要表示是否收到了某某信號,所以用位圖這個數據結構就剛剛好。被阻塞也可以這樣表示,都是用位圖就可以表示。

在這里插入圖片描述
每個信號都有兩個標志位分別表示阻塞(block)和未決(pending),還有一個函數指針表示處理動作。信號產生時,內核在進程控制塊中設置該信號的未決標志,直到信號遞達才清除該標志。

所以當收到一個信號是,先看block是否被阻塞,如果沒有阻塞就會遞達,如果阻塞了,就需要等解除阻塞之后再遞達。

sigset_t

OS為了我們對信號集進行操作,設置了sigset_t的數據類型,它本質就是一個位圖,這個類型可以表示每個信號的“有效”或“無效”狀態,在阻塞信號集中“有效”和“無效”的含義是該信號是否被阻塞,而在未決信號集中“有效”和“無效”的含義是該信號是否處于未決狀態。

為了對信號集更好的操作,OS也為我們提供了對信號集的操作函數。
在這里插入圖片描述

  1. 函數sigemptyset初始化set所指向的信號集,使其中所有信號的對應bit清零,表示該信號集不包含任何有效信號。
  2. 函數sigfillset初始化set所指向的信號集,使其中所有信號的對應bit置位,表示該信號集的有效信號包括系統支持的所有信號。

注意,在使用sigset_ t類型的變量之前,一定要調 用sigemptyset或sigfillset做初始化,使信號集處于確定的狀態。初始化sigset_t變量之后就可以在調用sigaddset和sigdelset在該信號集中添加或刪除某種有效信號。這幾個函數都是成功返回0,出錯返回-1。sigismember是一個布爾函數,用于判斷一個信號集的有效信號中是否包含某種 信號,若包含則返回1,不包含則返回0,出錯返回-1。

sigprocmask

調用函數sigprocmask可以讀取或更改進程的信號屏蔽字(阻塞信號集)。
在這里插入圖片描述
如果oset是非空指針,則讀取進程的當前信號屏蔽字通過oset參數傳出。如果set是非空指針,則 更改進程的信號屏蔽字,參數how指示如何更改。如果oset和set都是非空指針,則先將原來的信號 屏蔽字備份到oset里,然后根據set和how參數更改信號屏蔽字。假設當前的信號屏蔽字為mask,下表說明了how參數的可選值。

在這里插入圖片描述

sigpending

在這里插入圖片描述
可以通過這個函數獲取當前進程的pending表。通過set參數傳出。

信號的捕捉

發送信號后,信號不會立即遞達,而是在合適的時候遞達,那什么才算合適的時候呢?
進程從內核態返回用戶態時,進行信號的檢測和處理。

用戶態和內核態

在這里插入圖片描述
用戶態:只能訪問自己的0 - 3GB,是一種受控的狀態,能訪問的資源是有限的。
內核態:可以讓用戶以OS的身份訪問3 - 4GB,是一種OS的工作狀態,可以訪問大部分資源。

我們之前說的所有的地址空間都是用戶空間,里面都是對我們用戶自己的代碼,對應的還有一張用戶級頁表,而內核的進程地址空間都是OS的代碼數據和數據結結構,其中對應的還有一張內核級頁表,因為所有的進程都有自己的進程機地址空間,雖然用戶空間的使用情況可能千奇百怪,但是OS只有一個,所以他們所有的內核空間中的數據都是一樣的,并且在內存中也只會存在一張內存級頁表,所有的進程的內核空間的內容一樣,所以都指向同一張內核級頁表就可以了,我們平時調用函數實在自己的進程地址空間調用,系統調用也是代碼,是OS的代碼,他映射在內核級頁表中,所以我們普通用戶需要進行系統調用一定要發生身份的切換,因為普通用戶是不允許訪問內核級空間的,CPU中有一個CS寄存器,可以標識當前進程是用戶態還是內核態。所以不管是系統調用還是庫函數還是自己寫的函數都可以在自己的進程地址空間進行跳轉和返回,并且無論進程怎么切換,CPU都可以直接找到OS的代碼。

在這里插入圖片描述
在調用自己的方法時,進程是要切換回用戶態的,因為如果不切換的用戶就可以在自定義方法中利用內核身份做不好的事情了。

如果信號的處理動作是用戶自定義函數,在信號遞達時就調用這個函數,這稱為捕捉信號。由于信號處理函數的代碼是在用戶空間的,處理過程比較復雜,舉例如下: 用戶程序注冊了SIGQUIT信號的處理函數sighandler。 當前正在執行main函數,這時發生中斷或異常切換到內核態。 在中斷處理完畢后要返回用戶態的main函數之前檢查到有信號SIGQUIT遞達。 內核決定返回用戶態后不是恢復main函數的上下文繼續執行,而是執行sighandler函 數,sighandler和main函數使用不同的堆棧空間,它們之間不存在調用和被調用的關系,是 兩個獨立的控制流程。 sighandler函數返回后自動執行特殊的系統調用sigreturn再次進入內核態。 如果沒有新的信號要遞達,這次再返回用戶態就是恢復main函數的上下文繼續執行了。信號的捕捉過程中,是要進行4次的身份切換的。

sigaction

在這里插入圖片描述
sigaction函數可以讀取和修改與指定信號相關聯的處理動作。調用成功則返回0,出錯則返回- 1。signum是指定信號的編號。若act指針非空,則根據act修改該信號的處理動作。若oact指針非空,則通過oact傳出該信號原來的處理動作。act和oact指向sigaction結構體.

在這里插入圖片描述
將sa_handler賦值為常數SIG_IGN傳給sigaction表示忽略信號,賦值為常數SIG_DFL表示執行系統默認動作,賦值為一個函數指針表示用自定義函數捕捉信號,或者說向內核注冊了一個信號處理函數,該函數返回值為void,可以帶一個int參數,通過參數可以得知當前信號的編號,這樣就可以用同一個函數處理多種信號。顯然,這也是一個回調函數,不是被main函數調用,而是被系統所調用。

當某個信號的處理函數被調用時,內核自動將當前信號加入進程的信號屏蔽字,當信號處理函數返回時自動恢復原來的信號屏蔽字,這樣就保證了在處理某個信號時,如果這種信號再次產生,那么 它會被阻塞到當前處理結束為止。 如果在調用信號處理函數時,除了當前信號被自動屏蔽之外,還希望自動屏蔽另外一些信號,則用sa_mask字段說明這些需要額外屏蔽的信號,當信號處理函數返回時自動恢復原來的信號屏蔽字。 sa_flags字段包含一些選項,把sa_flags設為0就行,sa_sigaction是實時信號的處理函數。

volatile

保持內存的可見性,告知編譯器,被該關鍵字修飾的變量,不允許被優化,對該變量的任何操作,都必須在真實的內存中進行操作

SIGCHLD

現在我們已經會創建子進程了,子進程在退出的時候什么都沒說嗎?
答案肯定是不是的,子進程在退出是是會給父進程發送SIGCHLD信號的。

我們會用wait和waitpid函數清理僵尸進程,父進程可以阻塞等待子進程結束,也可以非阻塞地查詢是否有子進
程結束等待清理(也就是輪詢的方式)。采用第一種方式,父進程阻塞了就不 能處理自己的工作了;
第二種方式,父進程在處理自己的工作的同時還要記得時不時地輪詢一 下,程序實現復雜。其實,子進程在終止時會給父進程發SIGCHLD信號,該信號的默認處理動作是忽略,父進程可以自定義SIGCHLD信號的處理函數,這樣父進程只需專心處理自己的工作,不必關心子進程了,子進程 終止時會通知父進程,父進程在信號處理函數中調用wait清理子進程即可。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
void handler(int sig)
{pid_t id;while ((id = waitpid(-1, NULL, WNOHANG)) > 0){printf("wait child success: %d\n", id);}printf("child is quit! %d\n", getpid());
}
int main()
{signal(SIGCHLD, handler);pid_t cid;if ((cid = fork()) == 0){ // childprintf("child : %d\n", getpid());sleep(3);exit(1);}while (1){printf("doing some thing!\n");sleep(1);}return 0;
}

事實上,由于UNIX 的歷史原因,要想不產生僵尸進程還有另外一種辦法:父進程調 用sigaction將SIGCHLD的處理動作置為SIG_IGN,這樣fork出來的子進程在終止時會自動清理掉,不 會產生僵尸進程,也不會通知父進程。系統默認的忽略動作和用戶用sigaction函數自定義的忽略 通常是沒有區別的,但這是一個特例。此方法對于Linux可用,但不保證在其它UNIX系統上都可用。

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

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

相關文章

elememt-plus的表格的增刪改查#Vue3無需json數據,無需后端接口

elememt-plus的表格的增刪改查#Vue3無需json數據&#xff0c;無需后端接口 實現效果&#xff1a; <template><!-- 演示地址 --><div class"dem-add"><!-- Search start --><div class"dem-title"><p>演示地址</…

Python裝飾器:深入理解與應用實例

Python裝飾器&#xff1a;深入理解與應用實例 一、引言 在Python編程中&#xff0c;裝飾器&#xff08;Decorators&#xff09;是一個強大且實用的特性&#xff0c;它允許程序員在不修改原有函數或類代碼的情況下&#xff0c;為其添加額外的功能。裝飾器本質上是一個可調用對…

基于springboot的知識管理系統源碼數據庫

基于springboot的知識管理系統源碼數據庫 隨著信息互聯網信息的飛速發展&#xff0c;無紙化作業變成了一種趨勢&#xff0c;針對這個問題開發一個專門適應師生作業交流形式的網站。本文介紹了知識管理系統的開發全過程。通過分析企業對于知識管理系統的需求&#xff0c;創建了…

【Git教程】(十八)拆分大項目 — 概述及使用要求,執行過程及其實現,替代解決方案 ~

Git教程 拆分大項目 1?? 概述2?? 使用要求3?? 執行過程及其實現3.1 拆分模塊版本庫3.2 將拆分出的模塊作為外部版本庫集成 4?? 替代解決方案 通常軟件項目都是由單體小型系統開始的&#xff0c;在開發過程中項目規模和團隊人員不斷擴大&#xff0c; 將項目模塊化會顯得…

C#實現各種Hash計算

C#實現各種Hash計算 文章目錄 C#實現各種Hash計算涉及框架及庫目前支持可計算的類型核心代碼完整可運行代碼 BCrypt總結 涉及框架及庫 自己在NuGet管理器里面安裝即可 BouncyCastle.Cryptography&#xff1a;是加密算法和協議的.NET實現。 目前支持可計算的類型 BLAKE2B_16…

如何在Idea離線情況下安裝vue.js插件

親踐有效&#xff0c;步驟如下: 1. 互聯網環境登陸vue.js官網(Vue.js - IntelliJ IDEs Plugin | Marketplace)。 2. 然后先確定你的IDEA的版本&#xff1a;在你IDEA的安裝文件中找到product-info.json&#xff0c;里面的buildNumber記錄著你IDEA的精確版本號&#xff0c;根據…

【Entity Framework】EF中SaveChanges如何使用

【Entity Framework】EF中SaveChanges如何使用 文章目錄 【Entity Framework】EF中SaveChanges如何使用一、概述二、更改跟蹤和SaveChanges三、SaveChanges優勢四、使用SaveChanges添加數據五、使用SaveChanges更新數據六、使用SaveChanges刪除數據七、單個SaveChanges中的多個…

roscore啟動報錯的解決方法【將環境變量配置于最后】

今天在啟動rviz時發生一個很奇怪的報錯&#xff1a; rviz: error while loading shared libraries: librviz.so: cannot open shared object file: No such file or directory 我感覺很納悶&#xff01;再試著啟動一下roscore&#xff0c;發現如下報錯&#xff1a; [rosout-1…

[muduo網絡庫]——使用muduo庫搭建Echo服務器(剖析muduo網絡庫核心部分、設計思想)

在此之前&#xff0c;我們對于muduo庫的每一類幾乎都進行了逐行的分析&#xff0c;但是一個網絡庫的每個模塊之間總是有千絲萬縷的關系&#xff0c;所以可能有的地方還是有分析的不到位&#xff0c;所以從這一篇開始&#xff0c;我們從muduo的簡單使用----搭建一個Echo服務器&a…

Python爬蟲從入門到精通:一篇涵蓋所有細節的高質量教程

目錄 第一部分&#xff1a;Python爬蟲基礎 1.1 爬蟲原理 1.2 Python爬蟲常用庫 1.3 爬蟲實戰案例 1.4 注意事項 第二部分&#xff1a;爬蟲進階技巧 2.1 處理動態加載的內容 2.2 登錄認證 2.3 分布式爬取 2.4 反爬蟲策略 第三部分&#xff1a;爬蟲實戰項目 3.1 豆瓣…

【C語言】指針(二)

目錄 一、傳值調用和傳址調用 二、數組名的理解 三、通過指針訪問數組 四、一維數組傳參的本質 五、指針數組 六、指針數組模擬實現二維數組 一、傳值調用和傳址調用 指針可以用在哪里呢&#xff1f;我們看下面一段代碼&#xff1a; #include <stdio.h>void Swap(i…

基于Spring封裝一個websocket工具類使用事件發布進行解耦和管理

最近工作中&#xff0c;需要將原先的Http請求換成WebSocket&#xff0c;故此需要使用到WebSocket與前端交互。故此這邊需要研究一下WebSocket到底有何優點和不可替代性&#xff1a; WebSocket優點&#xff1a; WebSocket 協議提供了一種在客戶端和服務器之間進行全雙工通信的…

如何在MATALB中調用libMR

? 因為個人項目原因,我曾將參考OpenMax源碼GitHub - abhijitbendale/OSDN: Code and data for the research paper “Towards Open Set Deep Networks” A Bendale, T Boult, CVPR 2016將其轉換到MATLAB使用。 OpenMax 使用極值理論實現對開放集的篩選,在計算得分時需要用l…

異地組網群暉不能訪問怎么辦?

在日常使用群暉網絡儲存設備時&#xff0c;我們常常會遇到無法訪問的情況&#xff0c;特別是在異地組網時。這個問題很常見&#xff0c;但也很讓人困擾。本文將針對異地組網群暉無法訪問的問題進行詳細解答和分析。 異地組網的問題 在異地組網中&#xff0c;群暉設備無法訪問的…

Unity | Spine動畫動態加載

一、準備工作 Spine插件及基本知識可查看這篇文章&#xff1a;Unity | Spine動畫記錄-CSDN博客 二、Spine資源動態加載 1.官方說明 官方文檔指出不建議這種操作。但spine-unity API允許在運行時從SkeletonDataAsset或甚至直接從三個導出的資產實例化SkeletonAnimation和Skel…

被耽誤了的發明家

高三的某一天&#xff0c;數學焦老師在黑板上推公式&#xff0c;突然花屁股&#xff08;見另一篇博文《數學老師們》&#xff09;上出現了一個光圈。我心里一樂&#xff0c;有人在玩鏡子。本來大家對于焦老師的花屁股已經司空見慣了&#xff0c;可以不受干擾地聽課&#xff0c;…

HNU-算法設計與分析-作業3

第三次作業【動態規劃】 文章目錄 第三次作業【動態規劃】<1>算法實現題 3-1 獨立任務最優解問題<2>算法實現題 3-4 數字三角形問題<3>算法實現題 3-8 最小m段和問題<4>算法實現題 3-25 m處理器問題 <1>算法實現題 3-1 獨立任務最優解問題 ▲問…

postgresql安裝及性能測試

postgresql安裝及性能測試 1. Postgresql介紹 Postgresql是一款功能強大的開源對象關系型數據庫管理系統&#xff08;ORDBMS&#xff09;&#xff0c;以其穩定性、擴展性和標準的SQL支持而聞名。它支持復雜查詢、外鍵、觸發器、視圖、事務完整性、多版本并發控制&#xff08;MV…

Linux(七) 動靜態庫

目錄 一、動靜態庫的概念 二、靜態庫的打包與使用 2.1 靜態庫的打包 2.2 靜態庫的使用 三、動態庫的打包與使用 3.1 動態庫的打包 3.2 動態庫的使用 3.3 運行動態庫的四種方法 四、總makefile 一、動靜態庫的概念 靜態庫&#xff1a; Linux下&#xff0c;以.a為后綴的…

Python專題:十五、JSON數據格式

Python的數據處理&#xff1a;JOSN 計算機的主要工作&#xff1a;處理數據 最容易處理的數據就是結構化數據 非結構化數據&#xff1a;視頻&#xff0c;文件等 近些年的大數據、數據挖掘就是對互聯網中的各種非結構化的數據的分析和處理 半結構化數據 明確的結構屬性&…