Linux基礎篇——信號

Linux下的信號列表
這裏寫圖片描述
列表中,編號爲1 ~ 31的信號爲傳統UNIX支持的信號,是不可靠信號(非實時的),編號爲32 ~ 63的信號是後來擴充的,稱做可靠信號(實時信號)。不可靠信號和可靠信號的區別在於前者不支持排隊,可能會造成信號丟失,而後者不會。

信號的產生方式有四種:
1、硬件中斷
2、命令行(指令)
3、函數調用
4、軟件條件(程序異常)
信號的處理動作有三種:
1、忽略此信號
2、執行該信號的默認處理動作(一般爲結束該進程)
3、執行一個信號處理函數,要求內核在處理此信號時切換到用戶態執行(稱爲捕捉一個信號)

自定義信號處理函數
代碼如下:

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

執行情況如下:
這裏寫圖片描述

產生信號詳解

1、通過終端按鍵產生信號
core文件
在SIGINT的默認處理動作是要終止進程,SIGQUIT的默認動作是終止進程並Core Dump,當⼀一個進程要異常終⽌止時,可以選擇把進程的⽤用戶空間內存數據全部 保存到磁盤上,⽂文件名通常是core,這叫做Core Dump。進程異常終⽌止通常是因爲有Bug,⽐比如⾮非法內存訪問導致段錯誤,事後可以⽤用調試器檢查core⽂文件以查清錯誤原因,這叫做Post-mortem Debug(事後調試)。⼀一個進程允許產⽣生多⼤大的core⽂文件取決於進程的Resource Limit(這個信息保存 在PCB中)。默認是不允許產⽣生core⽂文件的,因爲core⽂文件中可能包含⽤用戶密碼等敏感信息,不安全。在開發調試階段可以⽤用ulimit命令變這個限制,允許產⽣生core⽂文件。
core文件大小默認是0,所以不設置的話不會生成core文件
這裏寫圖片描述
core文件最大可以爲1024Kb,我們通過ulimit -c 1024命令設置core文件大小
在死循環程序中我們按下ctrl+’c’或ctrl+’\’,終止該進程,此時生成core文件,
這裏寫圖片描述
2、通過系統函數向進程發信號
(1) kill命令是調用kill函數實現的,kill函數可以給一個指定的進程發特定的信號。
kill -信號標誌 進程標號
函數原型

#include<signal.h>
 int kill(pid_t pid,int signo);
 int raise(int signo);

raise函數可以給當前進程發送指定的信號
(2) abort函數使當前進程接收到信號異常終止
函數原型 void abort(void);//和exit 函數一樣,abort函數總是會成功的,所以沒有返回值
3、由軟件條件產生信號

#include<unistd.h>
unsigned in alarm(insigned int seconds);

函數原型如上,調用alarm函數可以設定一個鬧鐘,也就是告訴內核在seconds秒後向進程發送一個SIGALRM信號

  1 #include<stdio.h>
  2 #include<signal.h>
  3 #include<unistd.h>
  4 
  5 int main()
  6 {
  7     int count=0;
  8     alarm(1);
  9     for(;1;count++)
 10     {
 11         printf("count = %d\n",count);
 12     }
 13     return 0;
 14 }

執行結果爲1s中計算機計數次數
這裏寫圖片描述

阻塞信號以及相關函數

幾個概念:
實際執行信號的處理動作稱爲信號遞達(Delivery)。
信號從產生到遞達之間的狀態,稱爲信號未決(Pending)。
進程可以選擇阻塞(Block )某個信號。被阻塞的信號產⽣生時將保持在未決狀態,直到進程解除對此信號的阻塞,才 執行遞達的動作。
,阻塞和忽略是不同的,只要信號被阻塞就不會遞達,而忽略是在遞達之後 可選的一種處理動作。
信號在內核中的表示示意圖
這裏寫圖片描述
每個信號都有兩個標誌位分別表⽰示阻塞(block)和未決(pending),還有⼀一個函數指針表⽰示處理
動作。信號產⽣生時,內核在進程控制塊中設置該信號的未決標誌,直到信號遞達才清除該標
志。在上圖的例⼦子中,
1. SIGHUP信號未阻塞也未產生過,當它遞達時執⾏行默認處理動作。
2. SIGINT信號產生過,但正在被阻塞,所以暫時不能遞達。雖然它的處理動作是忽略,但在沒 有解除阻塞之前不能忽略這個信號,因爲進程仍有機會改變處理動作之後再解除阻塞。
3. SIGQUIT信號未產生過,⼀一旦產生SIGQUIT信號將被阻塞,它的處理動作是用戶自定義函數sighandler。

信號集操作函數

sigset_t類型對於每種信號用一個bit表⽰示“有效”或“無效”狀態,至於這個類型內部如何存儲這 些bit則依賴於系統實現,從使用者的角度是不必關心的,使用者只能調用以下函數來操 作sigset_t變量。
幾個函數原型:

#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 sigismenber(cosnt 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。
sigprocmask
int sigprocmask(int how,const sigset_t *set,sigset_t *oset)
調用函數sigprocmask可以讀取或更改進程的信號屏蔽字(阻塞信號集)
返回值:若成功則爲0,若出錯則爲-1
如果oset是非空指針,則讀取進程的當前信號屏蔽字通過oset參數傳出。如果set是非空指針,則 更改進程的信號屏蔽字,參數how指⽰示如何更改。如果oset和set都是非空指針,則先將原來的信號 屏蔽字備份到oset裏,然後根據set和how參數更改信號屏蔽字。假設當前的信號屏蔽字爲mask,下表說明了how參數的可選值。
這裏寫圖片描述
如果調用sigprocmask解除了對當前若干個未決信號的阻塞,則sigprocmask返回前,⾄至少將其中 一個信號遞達。

sigpending
int sigpending(sigset_t *set)
sigpending**讀取當前進程的未決信號集**,通過set參數傳出。調用成功則返回0,出錯則返回-1。
下面通過程序來演示函數的功能

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

void printsigset(sigset_t *set)
{
    int i=1;
    for(;i<32;i++)
    {
        if(sigismember(set,i))
        {
            putchar('1');
        }
        else
        {
            putchar('0');
        }
    }
    printf("\n");
}


int main()
{
    sigset_t s,p;
    sigemptyset(&s);
    sigemptyset(&p);
    sigaddset(&s,SIGINT);
    sigprocmask(SIG_BLOCK,&s,&p);
    while(1)
    {
        sigpending(&p);
        printsigset(&p);
        sleep(1);
    }
    return 0;
}

執行結果如下:
這裏寫圖片描述
我們可以發現,在沒有按下ctrl+‘c’時,當前進程中未決信號集一直爲0,在按下後,第二位變成了1。因爲在按下ctrl+’c’時信號被阻塞,一直處於未決狀態。
我們將代碼修改如下:
修改while循環

       while(1)
    {
        sigpending(&p);
        printsigset(&p);
        ifcount&4==0)
        {
            sigprocmask(SIG_UNBLOCK,&s,NULL);
        }
        count++;
        sleep(1);
    } 

運行結果如下:
這裏寫圖片描述
當ctrl+‘c’按下且if語句執行後,進程退出,因爲此時信號已經爲未決狀態且未阻塞,所以執行默認動作,終止進程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章