天天看點

信号:signal() 、 pause() 、 alarm()一、信号二、程序間的各個信号詳解三、操作信号三、經典信号

  • 一、信号
    • 1. 什麼是信号?
    • 2. 哪些情況會引發信号?
    • 3.系統到底有哪些信号呢?
    • 4.信号處理的三種方式
    • 5.可靠信号&&不可靠信号
    • 6.實時信号&&非實時信号
    • 7.背景程序&&前台程序
  • 二、程序間的各個信号詳解
  • 三、操作信号
    • 1.注冊信号
      • (1)作用
      • (2)函數功能分析——signal()函數
      • (3)執行個體驗證
      • (4)另一個可以擷取并處理信号的函數——pause()
    • 2.發送信号
      • (1)指令形式
      • (2)函數形式
      • (3)執行個體驗證
      • (4)補充另外兩個發送信号的函數
  • 三、經典信号
    • 1.SIGALRM信号
      • (1) alarm()函數文法
      • (2)執行個體驗證
      • (3)alarm函數的逾時處理機制
      • (4)alarm的定時處理
      • 4.1)用于定時的函數:setitiimer()

一、信号

1. 什麼是信号?

1.信号是linux系統為了響應某些狀況而産生的事件。程序收到信号應該采取相應的操作。

2.信号是異步事件,當信号到達,儲存目前程序的執行環境,轉去執行信号處理函數,

當信号處理函數執行函數完畢,恢複現場,繼續執行

2. 哪些情況會引發信号?

1)鍵盤事件 ctrl+c ctrl+\

2) 非法記憶體

3)硬體故障

4)環境切換

3.系統到底有哪些信号呢?

——用kill -l 檢視

信号:signal() 、 pause() 、 alarm()一、信号二、程式間的各個信号詳解三、操作信号三、經典信号
1)關閉終端      2)ctrl +c           3)ctrl\ ——以call的形式推出         4)非法指令     
5)陷入核心      6)abort程序終止      7)總線錯誤                        8)浮點數溢出  
9)殺死程序      10) 使用者自己定義      11)段錯誤                        12)由使用者自己定義  
13)管道破裂     14)鬧鐘              15)缺少某個程序                   17)子程序死亡     
18)繼續         19)暫停              23)緊急資料                      29)用來做異步IO    
32)            33)拿去做多線程
           

4.信号處理的三種方式

1.預設處理方式:man 7 signal

2.忽略:信号來了,不進行處理,裝作沒看見(忽視、丢掉)

——-

SIGKILL SIGSTOP 這兩個信号是不能被忽略的

3.捕獲并處理:信号來了,執行我們自己寫的代碼

——-

SIGKILL SIGSTOP 這兩個信号是不能被捕獲的

5.可靠信号&&不可靠信号

-

不可靠信号:(1-31号信号)

——原本的Unix拿下來的信号都是不可靠信号(不支援排隊)

linux的信号繼承自早期的Unix信号,Unix信号的缺陷:

1.信号處理函數執行完畢,信号恢複成預設的處理方式(linux已經改進)

2.(當同時來多個信号時)會出現信号丢失,信号不排隊

-

可靠信号:(34-64号信号)

不會出現信号丢失,支援排隊,信号處理函數執行完畢,不會恢複成預設的處理方式。

6.實時信号&&非實時信号

-

實時信号:

可靠信号,沒有快慢一說

-

非實時信号:

不可靠信号

7.背景程序&&前台程序

背景程序:

-

jobs

檢視有哪些背景程序

-

fg%id

将背景程序調到前台程序(id是作業号,不是程序号)

-

ctrl+z

将前台程序轉到背景程序

-

ctrl+c

隻能發送背景程序

前台程序:

./a.out &——直接以背景方式啟動

fg%1(1——作業編号,即第一個程序)

二、程序間的各個信号詳解

) SIGHUP
本信号在使用者終端連接配接(正常或非正常)結束時發出, 通常是在終端的控制程序結束時, 通知同一session内的各個作業, 這時它們與控制終端不再關聯。  
登入Linux時,系統會配置設定給登入使用者一個終端(Session)。在這個終端運作的所有程式,包括前台程序組和背景程序組,一般都屬于這個 Session。當使用者退出Linux登入時,前台程序組和背景有對終端輸出的程序将會收到SIGHUP信号。這個信号的預設操作為終止程序,是以前台程序組和背景有終端輸出的程序就會中止。不過可以捕獲這個信号,比如wget能捕獲SIGHUP信号,并忽略它,這樣就算退出了Linux登入,wget也能繼續下載下傳。

此外,對于與終端脫離關系的守護程序,這個信号用于通知它重新讀取配置檔案。


) SIGINT
程式終止(interrupt)信号, 在使用者鍵入INTR字元(通常是Ctrl-C)時發出,用于通知前台程序組終止程序。

) SIGQUIT
和SIGINT類似, 但由QUIT字元(通常是Ctrl-\)來控制. 程序在因收到SIGQUIT退出時會産生core檔案, 在這個意義上類似于一個程式錯誤信号。

) SIGILL
執行了非法指令. 通常是因為可執行檔案本身出現錯誤, 或者試圖執行資料段. 堆棧溢出時也有可能産生這個信号。

) SIGTRAP
由斷點指令或其它trap指令産生. 由debugger使用。

) SIGABRT
調用abort函數生成的信号。

) SIGBUS
非法位址, 包括記憶體位址對齊(alignment)出錯。比如通路一個四個字長的整數, 但其位址不是的倍數。它與SIGSEGV的差別在于後者是由于對合法存儲位址的非法通路觸發的(如通路不屬于自己存儲空間或隻讀存儲空間)。

) SIGFPE
在發生緻命的算術運算錯誤時發出. 不僅包括浮點運算錯誤, 還包括溢出及除數為等其它所有的算術的錯誤。

) SIGKILL
用來立即結束程式的運作. 本信号不能被阻塞、處理和忽略。如果管理者發現某個程序終止不了,可嘗試發送這個信号。

) SIGUSR1
留給使用者使用

) SIGSEGV
試圖通路未配置設定給自己的記憶體, 或試圖往沒有寫權限的記憶體位址寫資料.

) SIGUSR2
留給使用者使用

) SIGPIPE
管道破裂。這個信号通常在程序間通信産生,比如采用FIFO(管道)通信的兩個程序,讀管道沒打開或者意外終止就往管道寫,寫程序會收到SIGPIPE信号。此外用Socket通信的兩個程序,寫程序在寫Socket的時候,讀程序已經終止。

) SIGALRM
時鐘定時信号, 計算的是實際的時間或時鐘時間. alarm函數使用該信号.

) SIGTERM
程式結束(terminate)信号, 與SIGKILL不同的是該信号可以被阻塞和處理。通常用來要求程式自己正常退出,shell指令kill預設産生這個信号。如果程序終止不了,我們才會嘗試SIGKILL。

) SIGCHLD
子程序結束時, 父程序會收到這個信号。
如果父程序沒有處理這個信号,也沒有等待(wait)子程序,子程序雖然終止,但是還會在核心程序表中占有表項,這時的子程序稱為僵屍程序。這種情況我們應該避免(父程序或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子程序,或者父程序先終止,這時子程序的終止自動由init程序來接管)。

) SIGCONT
讓一個停止(stopped)的程序繼續執行. 本信号不能被阻塞. 可以用一個handler來讓程式在由stopped狀态變為繼續執行時完成特定的工作. 例如, 重新顯示提示符

) SIGSTOP
停止(stopped)程序的執行. 注意它和terminate以及interrupt的差別:該程序還未結束, 隻是暫停執行. 本信号不能被阻塞, 處理或忽略.

) SIGTSTP
停止程序的運作, 但該信号可以被處理和忽略. 使用者鍵入SUSP字元時(通常是Ctrl-Z)發出這個信号

) SIGTTIN
當背景作業要從使用者終端讀資料時, 該作業中的所有程序會收到SIGTTIN信号. 預設時這些程序會停止執行.

) SIGTTOU
類似于SIGTTIN, 但在寫終端(或修改終端模式)時收到.

) SIGURG
有”緊急”資料或out-of-band資料到達socket時産生.

) SIGXCPU
超過CPU時間資源限制. 這個限制可以由getrlimit/setrlimit來讀取/改變。

) SIGXFSZ
當程序企圖擴大檔案以至于超過檔案大小資源限制。

) SIGVTALRM
虛拟時鐘信号. 類似于SIGALRM, 但是計算的是該程序占用的CPU時間.

) SIGPROF
類似于SIGALRM/SIGVTALRM, 但包括該程序用的CPU時間以及系統調用的時間.

) SIGWINCH
視窗大小改變時發出.

) SIGIO
檔案描述符準備就緒, 可以開始進行輸入/輸出操作.

) SIGPWR
Power failure

) SIGSYS
非法的系統調用。
在以上列出的信号中,程式不可捕獲、阻塞或忽略的信号有:SIGKILL,SIGSTOP

不能恢複至預設動作的信号有:SIGILL,SIGTRAP
預設會導緻程序流産的信号有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
預設會導緻程序退出的信号有:SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
預設會導緻程序停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
預設程序忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH
           

三、操作信号

1.注冊信号

(1)作用

注冊信号實際上是對信号進行三種基本處理操作,用于告訴目前程序接受到信号後應該進行什麼處理操作。

(2)函數功能分析——signal()函數

typedef void(*sighandler_t)(int);
sighandler_t signal(int signum, //要注冊的信号
                    sighandler_t handler);//信号執行函數

//第一個參數 signum:說明所要處理的信号類型,它可以取除了SIGKILL和SIGSTOP外的任何一種信号。
//第二個參數 handler:描述了與信号關聯的動作,它可以取以下三種值:
      SIG_IGN:表示忽略該信号。 #define SIG_IGN ((sighandler_t)1)
      SIG_DFL:表示恢複對信号的系統預設處理。不寫此處理函數預設也是執行系統預設操作。#define SIG_IGN ((sighandler_t)0)
      sighandler_t類型的函數指針:執行自己寫的代碼。

//傳回錯誤:  SIG_ERR
 SIG_ERR    #define SIG_IGN((sighandler_t)-1)
//傳回值:
  成功傳回原本的信号處理函數指針;
  失敗傳回SIGERR; SIGERR的宏為 #define SIG_IGN ((sighandler_t)-1)
           

(3)執行個體驗證

//測試SIG_DFL
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>

int main()
{
    int i=;
    signal(SIGINT,SIG_DFL);//SIG_INT是ctlr c發起的
    //SIG_DFL表示采取預設處理方式處理接受到的信号
    //把這句代碼去掉,執行結果一樣

    for(i=;i<;++i)
    {
        printf("下午好:%d\n",i);
        sleep();
    }
    return ;
}
           
信号:signal() 、 pause() 、 alarm()一、信号二、程式間的各個信号詳解三、操作信号三、經典信号
//測試SIG_IGN
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>


int main()
{
    int i=;
    signal(SIGINT,SIG_IGN);//SIGINT 是ctrl c發起的信号
                           //SIG_IGN表示忽略接受到的信号
    for(i=;i<;++i)
    {
        printf("我最瘦:%d\n",i);
        sleep();
    }
    return ;
}
           
信号:signal() 、 pause() 、 alarm()一、信号二、程式間的各個信号詳解三、操作信号三、經典信号
//測試sighandler_t
#include<unistd.h>
#include<signal.h>

void handler(int n)//自定義一個函數
{
    printf("算了吧,還不減肥呀!\n");
    exit();
}

int main()
{

    int i=;
    signal(SIGINT,handler);
    while()
    {
        printf("想吃巧克力蛋糕!!!\n");
        sleep();
    }
    return ;
}
           
信号:signal() 、 pause() 、 alarm()一、信号二、程式間的各個信号詳解三、操作信号三、經典信号

(4)另一個可以擷取并處理信号的函數——pause()

  1. 函數文法:
    信号:signal() 、 pause() 、 alarm()一、信号二、程式間的各個信号詳解三、操作信号三、經典信号
  2. 函數功能:暫停程序,将目前程序設為就緒态,讓出CPU,直到收到任意一個信号終止,并且處理完該信号之後,直接執行pause()下面的語句。
  3. 參數解釋:無需傳參
  4. 執行個體驗證:
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>


//該執行個體可以了解為實作了sleep()函數的功能
int main()
{
    int ret=alarm();//設定一個定時器
    pause();//捕捉定時器信号
    //目前程序暫停,直到捕獲到任意一個信号,處理完後直接執行printf語句

    printf("I have been waken up\n",ret);
}
           
信号:signal() 、 pause() 、 alarm()一、信号二、程式間的各個信号詳解三、操作信号三、經典信号
這個函數中的printf是不會執行的,因為定時器發送的SIGARAM的預設處理是終止程式,是以程式列印之前程式已經結束了,與sleep不同的是sleep是不會退出的。

2.發送信号

(1)指令形式

-

kill -信号值 pid

(2)函數形式

//頭檔案
#include <sys/types.h>
#include <signal.h>

int kill(int pid, int signum);

//第一個參數
    pid>:發送給pid程序,pid是信号欲送往的程序的辨別。
    pid=:調用者所在程序組的任何一個程序,信号将送往所有與調用kill()的那個程序屬同一個使用組的程序。
    pid=-:信号将送往所有調用程序有權給其發送信号的程序,除了程序(init)。
    pid<-:|pid|程序組的所有程序,信号将送往所有調用程序有權給其發送信号的程序,除了程序(init)。

//第二個參數
    signum:準備發送的信号代碼,-`假如其值為,則沒有任何信号發出`,但是系統會執行錯誤檢查,通常會-`利用sig值為來檢驗某個程序是否仍在執行。`

//傳回值:
    成功執行時,傳回。
    失敗傳回-,errno 被設為以下的某個值
     EINVAL:指定的信号碼無效(參數 sig 不合法)
     EPERM;權限不夠無法傳送信号給指定程序 
     ESRCH:參數 pid 所指定的程序或程序組不存在 
           

(3)執行個體驗證

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

void handler(int n)//自定義函數,用于驗證能接收到信号
{
    printf("在的呀,我收到信号啦\n");
}

int main()
{
    int i=;
    signal(SIGUSR1,handler);//父程序擷取信号SIGUSR1
    //SIGUSR1:用于自定義信号,常用于發送和接收
    pid_t pid=fork();
    if(pid==)//子程序
    {
        sleep();
        kill(getppid(),SIGUSR1);//将信号SIGUSR1發送給父程序
        exit();
    }

    else
    {
        while()
        {
            printf("叮咚叮咚你在嘛\n");
            sleep();
        }
    }
    return ;
}
           
信号:signal() 、 pause() 、 alarm()一、信号二、程式間的各個信号詳解三、操作信号三、經典信号

(4)補充另外兩個發送信号的函數

1.給自己發送信号:-

int raise (int signum)

傳回值:成功傳回0,失敗傳回-1

2.給程序組發送信号:-

int killpg(int gid,int signum)

//注:gid是目前程序組的id

//傳回值-1,并把error值設為EINTR

三、經典信号

1.SIGALRM信号

(1) alarm()函數文法

信号:signal() 、 pause() 、 alarm()一、信号二、程式間的各個信号詳解三、操作信号三、經典信号

alarm(0) 清除鬧鐘

alarm(n) 等n秒後向該程序發送信号——報逾時

(2)執行個體驗證

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

void handler(int s){
    printf("逾時\n");
    exit();
}   

int main(void)
{
    char buf[]={};
    printf("輸入名字:");

    signal(SIGALRM,handler);//捕獲到SIGALRM信号執行後執行handler函數

    alarm();//如果3seconds後,SIGALRM信号沒有被清除,則擷取SIG信号
    scanf("%s",buf);
    alarm();//清除SIGALRM信号
    printf("名字為:%s\n",buf);

    for( ; ;)//用于驗證alarm(0)确實清除了SIGALRM信号
    {
        printf("叮咚~\n");
        sleep();
    }
}

           
信号:signal() 、 pause() 、 alarm()一、信号二、程式間的各個信号詳解三、操作信号三、經典信号

(3)alarm函數的逾時處理機制

——以簡單的考試計數程式驗證

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

int Right =;//做對的題數
int Wrong =;//做錯的題數

void handler(int s)//用于驗證逾時
{
    printf("time is over\n");
    printf("Right=%d,Wrong=%d\n",Right,Wrong);
    exit();
}

int main(void)
{
    int i=;
    signal(SIGALRM,handler);//擷取SIGALRM信号,執行handler函數

    alarm();//設定倒計時為20
    srand(getpid());//随機數種子

    for(i=;i<;i++)//出10道10以内的加法題
    {
        int left=rand()%;
        int right=rand()%;
        printf("%d+%d=",left,right);
        int ret;
        scanf("%d",&ret);
        if(left + right == ret)
        {
            Right++;
        }

        else
        {
            Wrong++;
        }
    }
    printf("做完了\n");
    printf("Right=%d,Wrong=%d\n",Right,Wrong);
}

           
信号:signal() 、 pause() 、 alarm()一、信号二、程式間的各個信号詳解三、操作信号三、經典信号

(4)alarm的定時處理

4.1)用于定時的函數:setitiimer()

1. 函數原型:

#include<sys/time.h>
 int setitimer(int which,
              const struct itimerval* new_value,
              struct itimerval* old_value);
           

2.函數功能解釋:

設定一個定時器,從第n秒開始啟動定時器,每隔m秒發送SIGALRM信号(這裡的 n 和 m 都是自己設定的)

3.傳回值:

失敗:傳回-1 ; 成功:傳回0

4.參數解釋:

which:

一般寫成

ITIMER_REAL

,即真實的桌面時間

new_value:

一個結構體指針,結構體内部存儲着“啟動時間”和“間隔時間”,稍後詳解

old_value:

同樣的結構體指針,一般寫成

NULL

5.對struct itimerval* new_value 的詳解

**結構體内部的資訊

struct itimerval
{
struct timeval it_interval;//以後每一次執行的間隔時間
struct timeval it_value;//第一次執行的時間
};

struct timeval//時間的精确度
{
long tv_sec;//時間的秒部分
long tv_usec;//時間的微秒部分
}

**執行個體說明該結構體的作用
struct itimerval it;
//上句代碼的作用是定義一個該結構體變量it,it包括兩部分資訊:啟動時間和間隔時間

it.it_value.tv.sec=;
it.it_value.tv.usec=;
//上兩句代碼的作用是将it的啟動時間設為:真實的桌面時間+0秒+1微秒

it.it_interval.tv.sec=;
it.it_interval.tv.usec=;
//上兩句代碼的作用是将it的間隔時間設定為1秒0微秒
           

6.執行個體分析:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/time.h>

    printf("收到SIGALRM信号了\n");
}
{

    struct itimerval it;
    //上句代碼的作用是定義一個該結構體變量it,it包括兩部分資訊
:啟動時間 和 間隔時間
    it.it_value.tv_sec       = ;
    it.it_value.tv_usec      = ;
    //上兩句代碼的作用是将it的啟動時間設為:真實時間+0秒1微秒

    it.it_interval.tv_sec    = ;
    it.it_interval.tv_usec   = ;
    //上兩句代碼的作用是将it的間隔時間設為:1秒0微秒
    setitimer(ITIMER_REAL,&it,NULL);
    //上面的定時器内部資訊設定完成後,通過setitimer函數啟動該>定時器


    while()//無限循環,用于驗證定時器确實是每隔一段時間就發出
一個SIGALAM信号
    ;

}
           
信号:signal() 、 pause() 、 alarm()一、信号二、程式間的各個信号詳解三、操作信号三、經典信号

繼續閱讀