1. 程式人生 > >淺談訊號之block,pending,handler

淺談訊號之block,pending,handler

訊號的概念

為了理解訊號,先從我們最熟悉的場景說起:
   1. ⽤戶輸⼊命令,在Shell下啟動⼀個前臺程序。
  2. ⽤戶按下Ctrl-C,這個鍵盤輸⼊產⽣⼀個硬體中斷。
  3. 如果CPU當前正在執⾏這個程序的程式碼,則該程序的⽤戶空間程式碼暫停執⾏,CPU從⽤戶態 切換到核心態處理硬體中斷。
  4. 終端驅動程式將Ctrl-C解釋成⼀個SIGINT訊號,記在該程序的PCB中(也可以說傳送了⼀ 個SIGINT訊號給該程序)。
  5. 當某個時刻要從核心返回到該程序的⽤戶空間程式碼繼續執⾏之前,⾸先處理PCB中記錄的訊號,發現有⼀個SIGINT訊號待處理,⽽這個訊號的預設處理動作是終⽌程序,所以直接終⽌程序⽽不再返回它的⽤戶空間程式碼執⾏。

  需要注意的是:鍵盤上的組合鍵形成的訊號只能用於前臺程序

  目前來說,前臺程序隨時隨地都可以收到一個訊號,因為你在這個程序執行的任何時刻,你都可以出入“Ctrl-C”終止該程序。也就是說,訊號對於程序來說是非同步的。
  對於訊號的種類的和多少,可以再Linux中斷輸入“kill -l”顯示。

  
  這裡寫圖片描述

  
  每個訊號都是一個巨集定義,具體的巨集值為多少請百度。
  

訊號的產生

  產生訊號的方法有四種:
  1. 對於前臺程序來說,通過鍵盤的組合鍵就可以;
  2. 計算機的軟/硬體中斷或異常。
  硬體異常產⽣訊號,這些條件由硬體檢測到並通知核心,然後核心向當前程序傳送適當的訊號。例如當前程序執⾏了除以0的指令,CPU的運算單元會產⽣異常,核心將這個異常解釋 為SIGFPE訊號傳送給程序。再⽐如當前程序訪問了⾮法記憶體地址,,MMU會產⽣異常,核心 將這個異常解釋為SIGSEGV訊號傳送給程序。
  

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
int main()
{
    int i=0;
    int ret=10/i;
    return 0;
}
[[email protected] temp]$ 

這裡寫圖片描述

  3. signal函式,但嚴格來說signal並不是一個產生訊號的函式,而是一個改變訊號預設動作的函式,signal函式可以對於指定的訊號更改他的預設動作來達到自定義訊號動作的效果。
  

#include <stdio.h>
#include <signal.h> #include <sys/types.h> #include <stdlib.h> #include "mysleep.h" void Handler(int i) { printf("get a signal:%d \n",i); } int main() { int i=1; for(;i<32;i++){ signal(i,Handler); mysleep(1); } return 0; }

這裡寫圖片描述

  4. kill函式,kill函式可以對一個指定的程序傳送一個指定的訊號。

//kill函式
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
    if( kill( atoi(argv[1]), atoi(argv[2]) )<0 ){
        perror("kill");
        exit(1);
    }else{
        printf("send No.%d sig to %d proc success\n",atoi(argv[2]),atoi(argv[1]));
    }
    return 0;
}

  後臺執行一個死迴圈程序
  然後執行kill函式
  這裡寫圖片描述

訊號的處理

  而作業系統對發來訊號的處理操作有三種:
  1. 忽略此訊號。
  2. 執⾏該訊號的預設處理動作。
  3. 提供⼀個訊號處理函式,要求核心在處理該訊號時切換到⽤戶態執⾏這個處理函式,這種⽅式稱為捕捉(Catch)⼀個訊號。我們上面的第三條其實就是捕捉函式。

軟體如何產⽣訊號

  
  其實,庫裡已經為我們提供了函式,我們以alarm舉例。
  

       #include <unistd.h>

       unsigned int alarm(unsigned int seconds);

  這個函式的作用是在seconds秒後給本程序傳送一個SIGALRM訊號,而該訊號的預設動作為終止該程序。
  返回值:
  這個函式的返回值是0或者是以前設定的鬧鐘時間還餘下 的秒數。打個⽐⽅,某⼈要⼩睡⼀覺,設定鬧鐘為30分鐘之後響,20分鐘後被⼈吵醒了,還想多睡 ⼀會⼉,於是重新設定鬧鐘為15分鐘之後響,“以前設定的鬧鐘時間還餘下的時間”就是10分鐘。如果seconds值為0,表⽰取消以前設定的鬧鐘,函式的返回值仍然是以前設定的鬧鐘時間還餘下的秒數。
  

#include <stdio.h>
#include <sys/types.h>

int main()
{
    int seconds=5;
    printf("pid:%d\n",getpid());
    int ret=alarm(seconds);
    while(1);
    printf("pid:%d\n",getpid());
    return 0;
}

  5秒鐘之後因為鬧鐘訊號來臨,終止該程序。
這裡寫圖片描述

訊號的結構

  訊號並不是很簡單的程序給作業系統發訊號或作業系統給程序發訊號,然後呼叫系統呼叫解決問題那麼簡單的。
  實際執⾏訊號的處理動作稱為訊號遞達(Delivery),訊號從產⽣到遞達之間的狀態,稱為訊號未決(Pending)。程序可以選擇阻塞(Block )某個訊號。被阻塞的訊號產⽣時將保持在未決狀態,直到程序解除對此訊號的阻塞,才執⾏遞達的動作。注意,阻塞和忽略是不同的,只要訊號被阻塞就不會遞達,⽽忽略是在遞達之後 可選的⼀種處理動作。
  訊號的產生是有操縱系統發出的,發給程序。每個程序的PCB中都有三個“訊號表”,分別為block、pending、handler。
  需要注意的是:這三個表都是使用點陣圖實現的。而且作業系統中,很多都是以點陣圖實現的,思考一下為什麼呢?
  其中:block時訊號遮蔽字,意味著只要這個點陣圖中對應位有效後,對應的訊號就被遮蔽,無法正常遞達。
  pending表的代表的意思是,訊號我已經收到,但是還沒有遞達到正確位置。
  handler表代表的意思是每個訊號對應的處理方式。假如我們捕捉訊號,自定義訊號處理動作,則對應的handler表就會發生變化。
  訊號在計算機中可以用下圖簡單說明:
  這裡寫圖片描述

  需要注意的是,即使pending中已有這個訊號,但是block中已經遮蔽該訊號,那該訊號以無法正常遞達。
  

兩種訊號的遞達

  如果在程序解除對某訊號的阻塞之前這種訊號產⽣過多次,將如何處理?POSIX.1允許系統遞送該訊號⼀次或多次。Linux是這樣實現的:常規訊號在遞達之前產⽣多次只計⼀次,⽽實時訊號在遞達之前產⽣多次可以依次放在⼀個佇列⾥。本章不討論實時訊號。從上圖來看,每個訊號只有⼀ 個bit的未決標誌,⾮0即1,不記錄該訊號產⽣了多少次,阻塞標誌也是這樣表⽰的。因此,未決和阻塞標誌可以⽤相同的資料型別sigset_t來儲存,sigset_t稱為訊號集,這個型別可以表⽰每個訊號的“有效”或“⽆效”狀態,在阻塞訊號集中“有效”和“⽆效”的含義是該訊號是否被阻塞,⽽在未決訊號集中“有效”和“⽆效”的含義是該訊號是否處於未決狀態。下⼀節將詳細介紹訊號集的各種操作。 阻塞訊號集也叫做當前程序的訊號遮蔽字(Signal Mask),這⾥的“遮蔽”應該理解為阻塞⽽不是忽略。

幾個操作訊號集的函式

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

  返回值:若成功則為0,若出錯則為-1
  how引數的含義
  這裡寫圖片描述
  

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo); 

  函式sigemptyset初始化set所指向的訊號集,使其中所有訊號的對應bit清零,表⽰該訊號集不包含任何有效訊號。

  函式sigfillset初始化set所指向的訊號集,使其中所有訊號的對應bit置位,表⽰該訊號集的有效訊號包括系統⽀持的所有訊號。注意,在使⽤sigset_t型別的變數之前,⼀定要調 ⽤sigemptyset或sigfillset做初始化,使訊號集處於確定的狀態。初始化sigset_t變數之後就可以 在調⽤sigaddset和sigdelset在該訊號集中新增或刪除某種有效訊號。

  這四個函式都是成功返回0,出錯返回-1。sigismember是⼀個布林函式,⽤於判斷⼀個訊號集的有效訊號中是否包含某種 訊號,若包含則返回1,不包含則返回0,出錯返回-1。

  
  使用這幾個函式來試試對訊號的瞭解
  

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

void printsigset(sigset_t* sig)
{
    int i=1;
    for(;i<32;i++){
        if( sigismember(sig, i) ){//check No."i" signal
            printf("%d ",1);
        }else{
            printf("%d ",0);
        }
    }
    printf("\n");
}

int main()
{
    sigset_t a,b;
    sigemptyset(&a);//init
    sigemptyset(&b);
    sigaddset(&a,SIGINT);//add SIGINT to a
    if( sigprocmask(SIG_BLOCK,&a,NULL)<0 ){//block SIGINT signal ,mean CTRL-C can't end this peocess
        perror("sigprocmask");
        exit(1);
    }
    while(1){
        sigpending(&b);//get b'pending table
        printsigset(&b);//print now pending
        sleep(1);
    }
    return 0;
}

這裡寫圖片描述
 可以發現,原來沒有一個訊號位有效,但是我使用組合鍵Ctrl+C後,2號訊號就變為有效了,並且之前使用sigprocmask函式遮蔽了2號訊號,也就是SIGINT,所以Ctrl+C沒有終止程式。
 

訊號的捕捉

  如果訊號的處理動作是⽤戶⾃定義函式,在訊號遞達時就調⽤這個函式,這稱為捕捉訊號。由於訊號處理函式的程式碼是在⽤戶空間的,處理過程⽐較複雜,舉例如下:
1. ⽤戶程式註冊了SIGQUIT訊號的處理函式sighandler。
2. 當前正在執⾏main函式,這時發⽣中斷或異常切換到核心態。
3. 在中斷處理完畢後要返回⽤戶態的main函式之前檢查到有訊號SIGQUIT遞達。
4. 核心決定返回⽤戶態後不是恢復main函式的上下⽂繼續執⾏,⽽是執⾏sighandler函式,sighandler和main函式使⽤不同的堆疊空間,它們之間不存在調⽤和被調⽤的關係,是 兩個獨⽴的控制流程。
5. sighandler函式返回後⾃動執⾏特殊的系統調⽤sigreturn再次進⼊核心態。
6. 如果沒有新的訊號要遞達,這次再返回⽤戶態就是恢復main函式的上下⽂繼續執⾏了。
  可以用下圖表示:
  這裡寫圖片描述
  訊號的捕捉的程式碼,上面已經實現了。

相關推薦

訊號blockpendinghandler

訊號的概念 為了理解訊號,先從我們最熟悉的場景說起:    1. ⽤戶輸⼊命令,在Shell下啟動⼀個前臺程序。   2. ⽤戶按下Ctrl-C,這個鍵盤輸⼊產⽣⼀個硬體中斷。   3. 如果CPU當前正在執⾏這個程序的程式碼,則該程序的⽤戶空間程式

學習路(五):三種語句結構vim編輯器快捷鍵及使用方法find命令使用

vim編輯器循環;forwhileuntil for 變量 in 列表; do 循環體 done e.g for I in ‘seq 1 $FILE‘ ; doecho "Hello,‘head -n $I

go介面、執行緒、通道純屬個人看法

淺談go介面、通道、執行緒 golang 接 口 Go 是靜態型別的。每一個變數有一個靜態的型別,也就是說,有一個已知型別並且在編譯時就確定下來了 type MyInt int var i int var j MyInt 那麼 i 的型別為 int 而 j 的型別為 MyInt。即使

人工智慧機器學習機器學習監督學習

淺談機器學習與深度學習的區別 在人類歷史發展的這個階段,我們所談的AI(artificial intelligence)主要指的是弱人工智慧(narrow AI),也就是機器可以實現幫助人類實現一些任務,比如小區入口的業主人臉識別;另外,還有一個強人工智慧(Ge

javascript事件、函式、方法、物件 各代表何含義通俗解釋及其之間的關聯與區別

簡單來說: 在javascript中,所有的事件都是通過函式來執行的,函式本身即是動作(針對事件來說),也是方法(針對物件來說)!物件是指的誰觸發了事件,繫結事件的主謀。 【事件】事件就如神經開關,刺

C++冒泡排序、希爾排序、快速排序、插入排序、堆排序、基數排序性能對比分析(好戲在後面有圖有真相)

棧溢出 分享圖片 隨機數 函數 大根堆 oschina 共同學習 時間復雜度 還原 由於沒考慮到一些情況,對以上一些算法做了改進和對比!以及昨晚把希爾排序寫錯而誤以為其效率高過快速排序的糗事,今天一一做了更正和說明,如果你絕得本隨筆不是很妥可以嘗試看看這http://www

Nginx服務器的安裝升級、配置、LNMP平臺搭建、nginx+fastcgi、nginx高級技術-地址重寫及優化

perl 新的 大文件 文件的 add 並發連接數 文件配置 redirect ntp Nginx服務器:是俄羅斯人編寫的十分輕量級的HTTP服務器,是一個高性能的HTTP和反向代理服務器,同時也是一個IMAP/POP3/SMTP代理服務器 一、安裝Nginx軟件: 準備工

遠程登錄時ssh的加密原理

回車 直接 phrase 允許 輸入密碼 tar continue 兩種 left 登錄方式主要有兩種: 1、基於用戶密碼的登錄方式: 加密原理: 當服務器知道用戶請求登錄時,服務器會把自己的公鑰發給用戶,ssh會將服務器的公鑰存放在客戶端的~/.ssh/

tomcat優化(內存並發緩存安全網絡系統等)

mps config 兩種 問題 adding back get ces hit 一.Tomcat內存優化Tomcat內存優化主要是對 tomcat 啟動參數優化,我們可以在 tomcat 的啟動腳本 catalina.sh 中設置 java_OPTS 參數JAVA_OPT

極限工坊淘小咖:實體餐飲業的變革線上擁抱線下小程序的新零售

利用 網上 積分 新希望 都是 微信小程序 新的 附近 第三方服務 隨著互聯網的極速發展,人們的日常生活節奏逐漸加快,實體商家的經營的商鋪營業額已經到達率了瓶頸期,一些經常關註互聯網的商家早已經察覺小程序這是個新的風口,而互聯網的發展,也已經讓實體商家不得不做起線上+線下的

String是不可變的那麼String s = "aaa"為什麼同樣可以執行 s = "bbb"

什麼是不可變物件? 眾所周知, 在Java中, String類是不可變的。那麼到底什麼是不可變的物件呢? 可以這樣認為:如果一個物件,在它建立完成之後,不能再改變它的狀態,那麼這個物件就是不可變的。不能改變狀態的意思是,不能改變物件內的成員變數,包括基本資料型別的值不能改變

程式猿簡歷的寫法你會如何寫你的簡歷呢。

引言   簡歷的重要性相信就不需要LZ來灌輸這個思想了,一份好的簡歷和一份差的簡歷,可以直接左右HR最終選擇的結果。LZ在公司雖然現在還不參與面試,但是偶爾閒的時候也會看看投遞的簡歷,看完以後也會給出

python自動化測試資料驅動寫一個真正通用的驅動類

現如今python越來越流行,這種指令碼語言讓自動化測試變的簡潔高效;當然不論是用java還是python或者其他框架時,都有一個不能迴避的問題-----那就是資料問題 資料的靈活性不僅可以讓case覆蓋度更大,還可以避免出現因為需求變更導致的測試指令碼“傷筋動骨”式的改造

es的原理、機制 IK分詞原理

1、分散式的架構es都有哪些機制? 1、主備 primary shard 的副本 replica shard primary shard不能和自己的replica shard放在同一個節點上、 2、容錯 使用選舉機制 master node宕機,選舉mast

分散式叢集管理的原理看看叢集究竟是做什麼的

本文始發於個人公眾號:**TechFlow**,原創不易,求個關注 今天是分散式專題的第11篇文章,我們一起來聊聊分散式叢集資源管理。 在開始文章之前,我們先來問一個問題,為什麼是國際上是亞馬遜,國內是阿里這兩家公司雲端計算搞得最好呢?這兩家公司之間有一個巨大的共同點,就是它們都是電商公司。電商公司的特

soaRESTful

let net ado soap 業務 淺談 網絡資源 ado.net 面向服務 今晚打算花點時間整理一下面向服務的架構oap。1傳統中小型項目架構一般是這樣的:(java)html+servlet+jdbc.和(.net)html+handler+ado.net都是在一臺

MYSQL日誌文件系統

mysql日誌文件系統 同大多數關系型數據庫一樣,日誌文件是MySQL數據庫的重要組成部分。MySQL有幾種不同的日誌文件,通常包括錯誤日誌文件,二進制日誌,通用日誌,慢查詢日誌,等等。這些日誌可以幫助我們定位mysqld內部發生的事件,數據庫性能故障,記錄數據的變更歷史,用戶恢復數據庫等等 MySQL

jqueryon()綁定事件和off()解除綁定事件

span syntax num this code value 自己 冒泡 屬性 off()函數用於移除元素上綁定的一個或多個事件的事件處理函數。 off()函數主要用於解除由on()函數綁定的事件處理函數。 該函數屬於jQuery對象(實例)。 語法 jQuery

架構路:單點登錄 SSO

用戶體驗 們的 建設 驗證機制 一個 簡單的 用戶登錄 集中 不同 前言:SSO 單點登錄   “半吊子”的全棧工程師又來了,技術類的文章才發表了兩篇,本來想先將主攻的幾個系列都開個頭(Nodejs、Java、前端、架構、全棧等等),無奈博客起步太晚,寫博文的時間又沒有很多

【JavaScript系列】JavaScript函數(一)

php 面向過程 .com func fun 面向對象編程 quest ice bbf 在編程語言中,無論是面向過程的C,兼備面過程和對象的c++,還是面向對象的編程語言,如java,.net,php等,函數均扮演著重要的角色。當然,在面向對象編程語言JavaScript中