欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Linux進程信號的用法及說明

 更新時間:2025年07月28日 16:54:16   作者:QQ_437664314  
Linux信號是進程間異步通知機制,分傳統(tǒng)(1-31)與實時(34-64)信號,處理方式包括默認、忽略和自定義,通過阻塞信號可控制其遞達,系統(tǒng)調用如sigprocmask和sigpending用于管理信號狀態(tài)與處理

信號產生

生活中的信號類比(交通信號燈、警報),當產生這些信號時,我們會立馬想到對應的動作。

在Linux中,信號是事件發(fā)生對進程的通知機制亦稱軟件中斷,由操作系統(tǒng)內核、進程本身或者其他進程向目標進程異步事件發(fā)送機制(即收到某種信號,并不會立馬去執(zhí)行)。通知進程發(fā)生某種預定義的時間,要求進程作出相應響應。

信號與硬件中斷相似之處在于會打斷程序執(zhí)行流程。

常見信號

硬件異常

  • 如內存越界(SIGSEGV
  • 除零錯誤(SIGFPE
  • 由內核自動生成

系統(tǒng)調用:

  • kill():向指定進程發(fā)送信號
  • raise():進程向自身發(fā)送信號
  • alarm():設置定時器,超時后發(fā)送 SIGALRM

終端輸入:

  • Ctrl+C:SIGINT(終止進程)
  • Ctrl+Z:SIGTSTP(暫停進程)
  • Ctrl+\:

軟件條件:

  • 子進程退出時,父進程收到 SIGCHLD
  • 定時器到期(如 alarm())觸發(fā)信號

核心轉儲 :

信號的分類

信號分為兩大類。

(編號1-31)為傳統(tǒng)信號信號,內核向進程通知且遞送一次,(編號34-64)為實時信號使信號按序遞送。

信號處理方式

  • 默認動作: 部分是終止自己,暫停等
  • 忽略動作: 是一種信號處理的方式,只不過動作就是什么都不干
  • 自定義動作: 使用signal方法修改信號的處理動作。(即用戶程序員編寫的函數:將默認動作轉化為自定義動作)

改變信號處理方式

signal,sigaction

信號阻塞

概念悉知

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

信號產生后會稍后遞達給某進程,信號在產生和到達期間會一直處于pending等待狀態(tài)。

信號在內核中的表示

每個進程都有一個信號屏蔽字亦稱阻塞信號集,一個 sigset_t 類型的位圖,每一位對應一個信號(如 SIGINT、SIGQUIT 等)。用來標記哪些信號當前被阻塞。被阻塞的信號若已產生,會進入未決狀態(tài),直到阻塞被解除才會觸發(fā)處理。位為1生效,0不生效。

block與pending之間聯(lián)系

pending集是信號產生后的暫存區(qū),block是控制信號是否能從pending暫存區(qū)遞交給進程的開關

  • block:決定哪些信號會被阻塞,近而將其放入未決信號集
  • pending:記錄已產生但未被處理的信號(因被阻塞或正在處理其他信號)
  • 通過sigpending()査詢掛起的信號集,通過sigprocmask()控制阻塞狀態(tài)。

其中block位圖用于表示進程是否阻塞。

  • pending位圖用于是否有信號寫入(信號是否產生,信號產生時,內核在進程控制塊中設置該信號的pending位圖,直到信號抵達才消失)。
  • handler函數指針數組用于進程執(zhí)行何種動作(其中存放的是動作函數指針,每個信號的編號就是其數組下標)。

關鍵系統(tǒng)調用

信號集操作函數

sigset_t 本質上是一個位圖,每一位代表一個信號。當某一位被設置為 1 時,表示對應的信號被包含在信號集中;為 0 則表示不包含。使用者只能調用以下函數來操作sigset_t變量,不用關注內部數據。

在使用sigset_t類型的變量之前,一定要調用sigemptyset()函數初始化一個未包含任何成員的信號集或者sigfillset()函數則初始化一個信號集,使其包含所有信號(包括所有實時信號)。

#include <signal.h>
// 清空信號集,置0
int sigemptyset(sigset_t *set);  
// 填充所有信號, 置有效狀態(tài)1?  
int sigfillset(sigset_t *set);     

信號集初始化后,可以分別使用 sigaddset()sigdelset()函數向一個集合中添加或者移除單個信號。使用sigismember()測試信號是否是信號集set的成員。

#include <signal.h>
// 添加單個信號
int sigaddset(sigset_t *set, int signo); 
 // 刪除單個信號
int sigdelset(sigset_t *set, int signo);
// 判斷信號是否存在
int sigismember(const sigset_t *set, int signo); 

信號掩碼(阻塞信號傳遞)

內核會為每個進程維護一個信號掩碼(一組信號),阻塞其針對該進程的傳遞。如果將被阻塞的信號發(fā)送給某進程,那么對該信號的傳遞將延后,直至從進程信號掩碼中移除該信號,從而解除阻塞為止。阻塞信號集內01只能由其觸發(fā)。

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

參數 :

how:指定了sigprocmask()函數想給信號掩碼帶來的變化。

  • SIG_BLOCK:set包含了我們希望添加到當前信號屏蔽字的信號(邏輯或)
  • SIG_UNBLOCK:set包含了我們希望從當前信號屏蔽字中解除阻塞的信號(與非)
  • SIG_SETMASK:設置當前信號屏蔽字為set所指向的值(直接賦值)

set

  • 要操作的信號集,(為NULL則忽略)

oldset

  • 保存舊的信號掩碼(可為NULL)

返回值:成功返回 0,失敗返回 -1(設置 errno

  • 阻塞:信號暫存到未決信號集直到解除阻塞
  • 忽略:直接丟棄

操作示例:

void handler(int no){
    cout << "execcute user-defined actions" << " ";
}

int main(){

    //更改2號信號執(zhí)行動作
    signal(2, handler);

    //創(chuàng)建兩個信號集
    sigset_t set, old_set; 

    //清空信號集
    sigemptyset(&set);
    sigaddset(&set, SIGINT);//將2號信號添加進set集01000...000
    
    //將set信號集中指定信號添加到當前阻塞信號集中,同時將原來的信號集保存到old_set中
    sigprocmask(SIG_BLOCK, &set, &old_set);//阻塞set中包含的信號集

    cout << "SIG_INT is blocked, ctrl+c has failed" << endl;
    sleep(5);//5秒內ctrl+c不會有反應

    //使用old_set替換當前所有阻塞信號集
    sigprocmask(SIG_SETMASK, &old_set, NULL);//恢復之前阻塞信號集
    cout << "SIG_INT has been resloved" << endl;
    sleep(5);
    return 0;
}

阻塞期間發(fā)送2號信號?

信號不會立馬處理,而是將該信號加入pending信號集(即pending位圖SIG_INT位從0-->1)

信號阻塞接觸后?

pending信號集內該信號立即處理(處理方式鑒于默認動作忽略(丟棄)自定義動作),該位由1-->0

sigpending獲取未決信號集

如果某進程接受了一個該進程正在阻塞的信號,那么會將該信號填加到進程的等待信號集中。當之后解除了對該信號的鎖定時,會隨之將信號傳遞給此進程。

#include <signal.h>
int sigpending(sigset_t *set);

系統(tǒng)調用返回后,用戶空間的set變量包含了當前的未決信號集,可以使用sigismember()檢查特定信號是否在未決信號集中。 

作用: 返回處于等待狀態(tài)的信號集,并將其置于 set指向的sigset_t 結構中。

操作是否進入未決?后續(xù)動作
信號被阻塞,且期間被發(fā)送該信號記錄在未決信號集中,直到阻塞解除后被處理
信號被阻塞,期間未被發(fā)送不記錄該信號,解除阻塞后若收到信號則直接處理
信號未被阻塞,但被發(fā)送信號直接調用對應函數即可

signal() - 設置信號處理

#include <signal.h>

void (*signal(int sig, void (*func)(int)))(int);

返回函數指針的函數:

代碼示例:

void handler(int signo){
    cout << "execcute user-defined actions" << " ";
}
...
    //更改2號信號執(zhí)行動作
    signal(2, handler);

聲明該函數類型是一個函數指針類型,signal是函數名(如:int (*p)(int,int),p是函數名,有兩個int類型參數,返回值是一個int類型)。signal返回的是一個函數指針,該函數指向的函數參數是一個int類型,返回值是void。所以外層是指明這是一個函數指針類型。里面才是使用的函數本體。

  • signal 是一個函數
  • 它接受一個整數和一個函數指針作為參數
  • 它返回一個與第二個參數類型相同的函數指針

返回函數指針的數組: 對?

sigaction() - 設置信號處理

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

signum : 目標信號

act 新的信號處理方式

  • sa_handler:信號處理函數指針(或 SIG_IGN、SIG_DFL)。
  • sa_mask:處理該信號時額外阻塞的信號集。
  • sa_flags:控制選項(如 SA_RESTARTSA_NODEFER
  • NULL

oldact

  • 保存舊的處理配置(可為NULL)

返回值:成功返回 0,失敗返回 -1(設置 errno

struct sigaction {
    void     (*sa_handler)(int);          // 簡單處理函數
    void     (*sa_sigaction)(int, siginfo_t *, void *); // 高級處理函數
    sigset_t sa_mask;                     // 處理期間屏蔽的信號集
    int      sa_flags;                    // 控制標志
    void     (*sa_restorer)(void);        // 內部使用(已廢棄)
};

現(xiàn)在我們使用sigprocmask函數和sigpending函數完成一個函數。思路是

  1. 對2號信號寫入信號集
  2. 將該信號集使用sigprocmask函數將2號信號block住
  3. 我們鍵盤輸入ctrl+c,產生2號信號,發(fā)送至該進程。
  4. 使用sigpending函數輸出pending位圖,因為我們將2號進程block住了,該信號并不能抵達。我們再向其發(fā)送2號信號,信號被block住了并不會影響信號的寫入。因此我們再發(fā)送2號進程之前pending位圖應該全為0,發(fā)送2號進程之后會看見pending位圖發(fā)送由01的變化。

執(zhí)行流程:

  1. 前 5 秒內,SIGINT(Ctrl+C)被阻塞,信號進入未決狀態(tài)但不觸發(fā)處理。
  2. 5 秒后解除阻塞,若之前有未決的 SIGINT,會立即觸發(fā) handle 函數。
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <signal.h>
using namespace std;
static void handler(int signo){
    cout << signo << " 號信號確實遞達了" << endl;
    //最終不退出進程
}
 void DisplayPending(const sigset_t pending){
    // 打印 pending 表
    int i = 1;
    while (i < 32)
    {
        if (sigismember(&pending, i))  cout << "1";
        else cout << "0";
        i++;
    } cout << endl;
}
int main(){
    // 更改 2 號信號的執(zhí)行動作
    signal(2, handler);
    // 創(chuàng)建信號集
    sigset_t set, oset;
    // 信號集清空  0
    sigemptyset(&set);
    sigemptyset(&oset);
    
    sigaddset(&set, 2);	//將2號信號寫入set
    // 設置當前進程的屏蔽信號集
    sigprocmask(SIG_BLOCK, &set, &oset);//0給進程
    // 死循環(huán)
    int n = 0;
    while (true){
        if (n == 5){
            // 采用 SIG_SETMASK 的方式,覆蓋進程的 block 表
            sigprocmask(SIG_SETMASK, &oset, NULL); // 不接收進程的 block 表
        }
        // 獲取進程的 未決信號集
        sigset_t pending;
        sigemptyset(&pending);
        int ret = sigpending(&pending);
        assert(ret == 0);
        (void)ret; // 避免 release 模式中出錯
        DisplayPending(pending);
        n++;
        sleep(1);
    }
    return 0;
}

信號處理

進程地址空間

為了進程可以通過訪問虛擬地址來間接訪問物理資源,我們需要建立虛擬地址空間與物理內存的映射關系。用戶區(qū)和內核區(qū)都需要通過內核級頁表或者用戶級頁表進行映射使用。

系統(tǒng)調用:

系統(tǒng)調用是受控的內核入口,借助于這一機制,進程可以請求內核以自己的名義去執(zhí)行某些動作。以應用程序編程接口(API)的形式,內核提供有一系列服務供程序訪問。

信號處理過程

用戶態(tài)的時候執(zhí)行用戶代碼,在中斷/異常/系統(tǒng)調用的時候切換到內核態(tài),査看進程的三張表。(信號捕捉過程)當需要執(zhí)行自定義動作的時候,切回用戶態(tài)執(zhí)行自定義動作,自定動作完成之后,再切到內核態(tài)執(zhí)行sys_sigretur()函數,最后再切回用戶態(tài)中用戶代碼的下一行繼續(xù)執(zhí)行。

當執(zhí)行默認動作或者忽略動作的時候,直接可以在內核中完成,完成之后切回用戶態(tài)中的用戶代碼的下一行,繼續(xù)執(zhí)行。

  • sigaction()
  • volatile關鍵字
  • SIGCHLD

總結

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • Linux下誤刪messages文件的找回方法

    Linux下誤刪messages文件的找回方法

    今天小編就為大家分享一篇關于Linux下誤刪messages文件的找回方法,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • 詳解Linux搭建DNS服務器

    詳解Linux搭建DNS服務器

    這篇文章主要介紹了Linux搭建DNS服務器,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • 詳解Linux文件操作知識點

    詳解Linux文件操作知識點

    在本篇文章中我們給大家詳細分享了關于Linux文件操作的相關知識點內容,有興趣的朋友們可以學習下。
    2018-09-09
  • Linux實現(xiàn)搭建ssh并允許使用root遠程

    Linux實現(xiàn)搭建ssh并允許使用root遠程

    這篇文章主要介紹了Linux實現(xiàn)搭建ssh并允許使用root遠程方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • centos yum更新及刪除多余啟動項

    centos yum更新及刪除多余啟動項

    在CentOS更新后,并不會自動刪除舊內核。所以在啟動選項中會有多個內核選項,可以手動使用以下命令刪除多余的內核:
    2018-04-04
  • Linux之信號的保存方式

    Linux之信號的保存方式

    這篇文章主要介紹了Linux之信號的保存方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • Apache訪問機制配置小結

    Apache訪問機制配置小結

    本文主要介紹了Apache訪問機制配置小結,包括如何設置訪問控制、認證和授權,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-07-07
  • Linux gcc命令的具體使用

    Linux gcc命令的具體使用

    這篇文章主要介紹了Linux gcc命令的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-02-02
  • CentOS 6.5 環(huán)境實現(xiàn)本地局域網搭建YUM的方法【基于FTP】

    CentOS 6.5 環(huán)境實現(xiàn)本地局域網搭建YUM的方法【基于FTP】

    這篇文章主要介紹了CentOS 6.5 環(huán)境實現(xiàn)本地局域網搭建YUM的方法,結合實例形式分析了CentOS基于FTP本地局域網搭建YUM的具體步驟、相關命令與操作技巧,需要的朋友可以參考下
    2018-04-04
  • 詳解CentOS7下PostgreSQL 11的安裝和配置教程

    詳解CentOS7下PostgreSQL 11的安裝和配置教程

    這篇文章主要介紹了CentOS7下PostgreSQL 11的安裝和配置教程,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-10-10

最新評論