Linux---信號

信號對於我們來說已經很熟悉了,走在路上紅燈亮起,這時我們就停下腳步,綠燈亮起,又繼續前行;當水壺哨聲響起的時候,這時我們就過去關掉電源……
那麼我們爲什麼會對那種現象進行識別,並做出一系列的反應呢?
這是因爲大腦能記住生活中的這種信號進行識別並做出動作。那麼進程也是如此。

信號的產生:
1.用戶在終端按下某些鍵時,終端驅動程序會發送信號給前臺進程;

終止程序:在鍵盤按下ctrl+\ 組合鍵產生信號SIGQUIT,SIGQUIT默認動作是終止程序並Core Dump(核心轉儲).
Core Dump:當一個進程在異常終止的時候,操作系統會將內存中進程的有效數據拷貝到磁盤上,文件名通常是core。
查詢core文件大小命令:ulimit -a
修改core 文件大小命令:ulimit -c 大小
Core Dump:可以定位程序錯誤
這裏寫圖片描述

2.硬件異常產生信號(如:除數爲0,無效的內存引用等),硬件檢測到並通知內核,內核會向當前進程發送信號;

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

int main(int argc,char* argv)
{
    if(argc!=2){
        printf("Usage:%s,symbol",argv[0]);
        exit(1);
    }
    return 0;
}

這裏寫圖片描述

3.用戶調用kill命令將信號發送給其他進程,進程調用kill函數將信號發送給另一個進程或進程組;
這裏寫圖片描述
函數:
int kill(pid_t pid,int signo):給指定進程髮指定信號

int main(int argc,char* argv[])
{
        if(argc!=3){
                printf("Error:%s,signo,pid\n",argv[0]);
                exit(1);
        }
        kill(atoi(argv[2]),atoi(argv[1]));
        return 0;
}

這裏寫圖片描述
int raise(int signo):給當前進程發送特定信號

void abort(void):使當前進程接收到信號異常終止

4.當檢測到某些軟件條件已經產生,將其通知有關進程時產生信號。

int count=0;
void handler(int signo)
{
        printf("signo:%d count:%d\n",signo,count);
        exit(1);
}
int main(int argc,char* argv[])
{
        signal(14,handler);
        alarm(1);//信號沒產生前的一秒,將數據累加多少次
        while(1){
                printf("count=%d\n",count);
                count++;
        }
        return 0;
}

這裏寫圖片描述

信號在內核中的存在:
這裏寫圖片描述
遞達(Delivery):實際執行信號的處理動作;
阻塞(Block):被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行遞達的動作;
未決(Pending):信號從產生到遞達之間的狀態。

信號集:
每個信號只有一個bit的未決標誌或阻塞標誌,不記錄該信號產生多少次,因此可以用相同的數據類型sigset_t來存儲,sigset_t稱爲信號集。

使用者只能調用以下函數對sigset_t變量進行操作,不應該對它的內部數據進行解釋。

int sigemptyset(sigset_t* set);
初始化set所指向的信號集,使其中所有信號的對應bit位清零,表示該信號集不包含任何有效信號;
int sigfillset(sigset_t* set);
初始化set所指向的信號集,使其中所有信號的對應bit置位,表示該信號集的有效信號包含系統支持的所有信號;
int sigaddset(sigset* set,int signo);
將一個信號signo添加到set所指向的信號集中;
int sigdelset(sigset_t* set,int signo);
將一個信號signo從set所指向的信號集中刪除;
以上四個函數都是成功返回0,失敗返回-1.
int sigismember(sigset_t* set,int signo);
判斷一個信號signo是否在set所指向的信號集中。
sigismember函數是一個bool函數,包含則返回1,不包含則返回0,出錯返回-1.

讀取或更改進程的信號屏蔽字函數(阻塞信號集):
int sigprocmask(int how,const sigset_t *set,sigset_t* oset);
參數:how:如何更改當前信號屏蔽字;
這裏寫圖片描述
讀取進程的當前信號屏蔽字由oset參數傳出,若set和oset都爲非空,則將原來的信號屏蔽字備份到oset裏。

讀取當前進程的未決信號集:
int sigpending(sigset_t* set);
函數返回信號集,其中的各個信號對於調用進程是阻塞的而不能遞達,因此也一定是當前未決的。信號集由set參數返回。
成功返回0,失敗返回-1.

void Print(sigset_t* set)
{
        int i=0;
        for(;i<32;++i){
        //判斷信號是否在信號集中,在的話就將對應比特位置1
                if(sigismember(set,i)){
                        putchar('1');
                }
                else{
                        putchar('0');
                }
        }
        printf("\n");
}

int main()
{
        sigset_t sig,psig;
        //對信號集進行初始化
        sigemptyset(&sig);
        //將信號SIGINT添加到信號集中
        sigaddset(&sig,SIGINT);
        //設置進程的信號屏蔽字
        sigprocmask(SIG_BLOCK,&sig,NULL);
        while(1){
            //獲取當前進程的未決信號集
                sigpending(&psig);
                Print(&psig);
                sleep(1);
        }
        return 0;
}

這裏寫圖片描述
捕捉信號:
什麼時候對信號進行捕捉是合適的時機?
這裏寫圖片描述
從上圖可知:捕捉信號的最佳時機在進程從內核態返回到用戶態的時候,這個時候會進行一次信號的檢測和處理,如果信號的處理動作是用戶自定義函數,信號遞達時調用這個處理函數,這個過程稱爲信號的捕捉。

如何去捕捉信號?

讀取或修改與指定信號相關聯的處理動作函數:

int sigaction(int signo,const struct sigaction* act,struct sigaction* oact);

結構體:struct sigaction
{
void(*)(int) sa_handle;
sigset_t sa_mask;
int sa_flags;
}

act:若非空,則根據act修改該信號的處理動作;
oact:若非空,則根據oact傳出原來的信號處理動作。
返回值:成功返回0,失敗返回-1.

使調用進程掛起直到有信號遞達函數:

int pause(void)

如果信號處理動作是終止進程,進程終止,函數不返回;
如果信號處理動作是忽略,進程還是處於掛起狀態,函數不返回;
如果信號處理動作是捕捉,則調用信號處理函數,成功pause返回-1,出錯error設置爲EINTR。

思考:調用pause()函數之後,信號遞達,設置的信號處理函數啥都不做,那麼信號處理函數有沒有必要存在?

有必要。
調用pause()函數之後,進程處於掛起狀態,當信號遞達的時候信號自動添加到信號屏蔽字中(當該信號再次到來的時候會被阻塞到當前處理結束),信號處理函數調用完畢之後信號解除屏蔽,pause()函數返回,進程繼續執行;如果沒有信號處理函數,pause()函數將使進程一直處於掛起狀態。

競態條件:由於程序執行的時序問題導致的錯誤。

int sigsuspend(const sigset_t* sigmask )
通過指定的sigmask來臨時解除對某些信號的屏蔽。沒有成功返回值,調用信號處理函數返回-1,error設置爲EINTR。

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

void handler(int signo)
{
        printf("get a signo:%d\n",signo);
        exit(1);
}

void mysleep(int seconds)
{
        struct sigaction new, old;
        sigset_t newmask,oldmask,midmask;
        unsigned int count=0;

        new.sa_handler=handler;
        sigemptyset(&new.sa_mask);
        new.sa_flags=0;
        sigaction(SIGALRM,&new,&old);

        sigemptyset(&newmask);
        sigaddset(&newmask,SIGALRM);
        sigprocmask(SIG_BLOCK,&newmask,&oldmask);

        alarm(seconds);
        midmask=oldmask;
        sigdelset(&midmask,SIGALRM);
        sigsuspend(&midmask);

        count=alarm(0);
        sigaction(SIGALRM,&old,NULL);
        sigprocmask(SIG_SETMASK,&oldmask,NULL);
}

int main()
{
        while(1){
                printf("I like eating icecream!\n");
                mysleep(1);
        }
        return 0;
}
發佈了83 篇原創文章 · 獲贊 43 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章