信號之中斷的系統調用、可重入函數、SIGCLD語義以及可靠信號術語和語義

本文來自個人博客:https://dunkwan.cn

中斷的系統調用

早期UNIX系統的一個特性是:如果進程在執行一個低速系統調用而阻塞期間捕捉到一個信號,則該系統調用就被中斷不再繼續執行。該系統調用返回出錯,其errno設置爲EINTR。這樣處理是因爲一個信號發生了,進程捕捉到它,這意味着已經發生了某種事情,所以是個好機會應當喚醒阻塞的系統調用。

這裏系統調用應當與函數進行區分,當捕捉到某個信號時,被中斷的是內核中執行的系統調用。

爲了支持這種特性,將系統調用分爲兩類:低速系統調用和其他系統調用。低速系統調用是可能會使進程永遠阻塞的一類系統調用,包括:

  • 如果某些類型文件的數據不存在,則讀操作可能會使調用者永遠阻塞;
  • 如果這些數據不能被相同的類型文件立即接受,則寫操作可能會使調用者永遠阻塞;
  • 在這種條件發生之前打開某些類型文件,可能會發生阻塞;
  • pause函數和wait函數;
  • 某些ioctl操作;
  • 某些進程間通信函數。

下面是幾種實現所提供的與信號有關的函數及它們的語義。

可重入函數

在信號處理程序中保證調用安全的函數,這些函數是可重入函數並被稱之爲異步信號安全的。以下是一些異步信號安全的函數。

測試示例:

#include "../../include/apue.h"
#include <pwd.h>

static void my_alarm(int signo)
{
    struct passwd *rootptr;

    printf("in signal handler\n");
    if((rootptr = getpwnam("root")) == NULL)
        err_sys("getpwnam(root) error");
    alarm(1);
}

int main(void)
{
    struct passwd *ptr;

    signal(SIGALRM, my_alarm);
    alarm(1);
    for(;;){
        if((ptr = getpwnam("dunk")) == NULL)
            err_sys("getpwnam error");
        if(strcmp(ptr->pw_name, "dunk") != 0)
            printf("return value corrupted!, pw_name = %s\n", ptr->pw_name);
    }

}

在運行該程序時,結果具有隨機性。從這個示例中可以看出,如果在信號處理程序中調用一個非可重入函數,則其結果是不可知的。

SIGCLD語義

SIGCLDSIGCHLD這兩個信號很容易被混淆。SIGCLDsystem V的一個信號名,其語義與名爲SIGCHLD的BSD信號不同。POSIX.1採用BSD的SIGCHLD信號。

對於SIGCLD的早期處理方式是:

  1. 如果進程明確地將該信號的配置設置爲SIG_IGN,則調用進程的子進程將不產生殭屍進程。
  2. 如果將SIGCLD的配置設置爲捕捉,則內核立即檢查是否有子進程準備好被等待,如果是這樣,則調用SIGCLD處理程序。

測試示例:

#include "../../include/apue.h"
#include <sys/wait.h>

static void sig_cld(int);

int main(void)
{
    pid_t pid;

    if(signal(SIGCHLD, sig_cld) == SIG_ERR)
        perror("signal error");

    if((pid = fork()) < 0){
        perror("fork error");
    }else if(pid == 0){
        sleep(2);
        _exit(0);
    }

    pause();
    return 0;
}

static void sig_cld(int signo)
{
    pid_t pid;
    int status;

    printf("SIGCHLD received\n");

    if(signal(SIGCHLD, sig_cld) == SIG_ERR)
        perror("signal error");

    if((pid = wait(&status)) < 0)
        perror("wait error");

    printf("pid = %d\n", pid);
}

結果如下:

可靠信號術語和語義

  1. 造成信號的事件:硬件異常(除數爲0)、軟件條件(alarm定時器超時)、終端產生的信號或調用kill函數。
  2. 當一個信號產生時,內核通常在進程表中以某種形式設置一個標誌,這種行爲稱之爲向進程遞送了一個信號。在信號產生和遞送之間的時間間隔內,稱信號是未決的(pending)。
  3. POSIX.1允許系統遞送該信號一次或多次,如果信號被遞送多次,則稱信號進行了排隊。
  4. 每個進程都有一個信號屏蔽字,它規定了當前要阻塞遞送到該進程的信號集。
  5. 信號編號可能會超過一個整型所包含的二進制位數,因此POSIX.1定義了一個新數據類型sigset_t,它可容納一個信號集
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章