系統編程-信號-總體概述和signal基本使用


 

信號章節 -- 信號章節總體概要

 

信號基本概念

信號是異步事件,發送信號的線程可以繼續向下執行而不阻塞。

 

信號無優先級。

1到31號信號是非實時信號,發送的信號可能會丟失,不支持信號排隊。

31號信號到64是實時信號, 發送的信號都會被接收, 支持信號排隊。

 

信號在Linux內核頭文件中的宏定義

 

信號的處理

由於進程啟動時,SIGUSR1和SIGUSR2被忽略,一般我們可以在有需要時,去捕獲這兩個信號,進而調用自己的處理函數。相應的,我們的程序其他地方去發送相應的信號。

 

signal函數原型 以及使用時所要包含的頭文件

和下面的是等價的:

 

實驗1  signal基本使用

實驗1.1

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

//定義信號處理函數
//signo: 進程捕獲到的信號
void sig_handler(int signo){
    printf("%d, %d occured \n", getpid(), signo);
}

int main(){

    #if 1  // 屏蔽這塊代碼,就是不捕獲這倆信號
    //向內核登記信號處理函數以及信號值  
    if(signal(SIGTSTP, sig_handler) == SIG_ERR){

        perror("signal error");
    }
    if(signal(SIGINT, sig_handler) == SIG_ERR){

        perror("signal sigint error");
    }
    #endif

    while(1){
        sleep(1);
        printf("- hello -\n");
    };

    return 0;
}

編譯運行

同時,根據這里的打印也可以看出,

SIGINT信號就是2號信號, 我們在鍵盤上按下CTRL+C就可以發送該信號了。

SIGTSTP信號就是20號信號,我們在鍵盤上按下CTRL+Z就可以發送該信號了。20號信號的備注就是 Keyboard stop, 即通過鍵盤發信號讓進程停止。

 

 

實驗1.2

如果屏蔽實驗1內捕獲這倆信號的代碼塊,運行效果如下

常用知識點補充:

19) SIGSTOP 20) SIGTSTP

19號信號和29號信號的相同點: 都可以使得進程暫停,並且收到SIGCONT信號后可以讓進程重新運行。

19號信號和29號信號的不同點:    SIGSTOP不可以捕獲(即使用信號處理函數)。

那么,我們來讓剛才停止的a.out繼續運行吧:

先查看a.out的pid

可見a.out的pid是8349

我們通過kill來發SIGCONT信號(18號信號)讓a.out繼續運行

可見,a.out又繼續運行起來了,

然而,需要注意的是,通過18號信號被繼續執行的進程:當終端內按下CTRL+C,則不能使得該進程終止了;且按下CTRL+Z,終端內也毫無跡象;但是可以通過kill -9被殺死。

根據實測,是這樣的,事實勝於雄辯。這個問題的原因以及背后隱藏的暫時我們還不知的相應知識點,可以留待以后探索,我們先暫且知道這么一回事就行了。

 

實驗2  SIG_DFL 和 SIG_IGN 使用

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

//定義信號處理函數
//signo: 進程捕獲到的信號
void sig_handler(int signo){
    printf("%d, %d occured \n", getpid(), signo);
}

int main(){
    printf("pid: %d \n", getpid());

    #if 1  // 屏蔽這塊代碼,就是不捕獲這倆信號
    //向內核登記信號處理函數以及信號值  
    if(signal(SIGTSTP, SIG_IGN) == SIG_ERR){

        perror("signal error");
    }
    if(signal(SIGINT, SIG_DFL) == SIG_ERR){

        perror("signal sigint error");
    }
    #endif

    while(1){
        sleep(1);
        printf("- hello -\n");
    };

    return 0;
}

此時,按下CTRL+Z對程序運行將毫無影響,而CTRL+C則采用默認方式,即結束進程。

 

 

實驗3 SIGUSR1 和 SIGUSR2 使用

注意,這兩個信號在進程啟動時默認是被忽略的。

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

//定義信號處理函數
//signo: 進程捕獲到的信號
void sig_handler(int signo){
    printf("%d, %d occured \n", getpid(), signo);
}

int main(){
    printf("pid: %d \n", getpid());

    #if 1  // 屏蔽這塊代碼,就是不捕獲這倆信號
    //向內核登記信號處理函數以及信號值  
    if(signal(SIGUSR1, sig_handler) == SIG_ERR){
        perror("signal error");
    }
    if(signal(SIGUSR2, sig_handler) == SIG_ERR){
        perror("signal sigint error");
    }
    #endif

    while(1){
        sleep(1);
        printf("- hello -\n");
    };

    return 0;
}

編譯運行,同時在另一個終端內發送信號 kill -SIGUSR1 10452  、 kill -SIGUSR2 10452

 

 

實驗4

知識點:SIGKILL 和 SIGSTOP不能被忽略,也不能被捕獲。

本實驗將嘗試捕獲SIGKILL和SIGSTOP,並以SIG_IGN的方式進程處理。

核心代碼展示:

編譯運行將返回SIG_ERR,如下圖所示

 

 

實驗5  SIGCHLD

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

//定義信號處理函數
//signo: 進程捕獲到的信號
void sig_handler(int signo){
    printf("%d, %d occured \n", getpid(), signo);
    wait(NULL);
}

int main(void)
{
    pid_t pid;

    if(signal(SIGCHLD, sig_handler) == SIG_ERR){
        perror("signal error");
    }

    pid = fork();
    if (pid < 0)
    {
        printf("fork error");
    }
    else if (pid == 0) /* first child : 子進程 */
    { 	
        sleep(2);
	printf("pid: child  =%ld\n", (long)getpid());
        exit(0);
    }

    // 使用信號的方式,父進程不必在此處阻塞調用wait,可以繼續向下執行自己的任務。
    while(1){
        sleep(1);
        printf("father can does his own things \n");
    }
}

編譯運行:

實驗中可見,父進程收到了子進程的17號信號,17號信號就是SIGCHLD信號(或寫作SIGCLD)

使用信號來回收子進程后,父進程不必在阻塞調用wait,可以繼續向下執行自己的任務。這個例子充分體現出了信號是一個異步事件。

在父進程還存活的期間,子進程退出將不會產生僵屍進程。

PS:父進程死后,肯定不會有其子進程還仍然是僵屍進程,因為子進程們會在其父進程死后被1號進程領養,進而被1號進程回收掉所占用的系統資源。

 

 

 

.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM