簡體   English   中英

信號處理程序不處理信號

[英]Signal handler doesn't handle signal

我有這份作業,應該編寫一個生成孩子的C程序。 父級打開文件,並通過讀取每一行,打印行號和行內容然后倒帶該文件來永久循環。

子進程以隨機間隔將SIGUSR1信號發送給父進程。 為了創建這些間隔,我選擇讓孩子入睡。

當收到第一個信號時,父母會跳過打印語句。 收到另一個SIGUSR1信號時,它將再次開始打印。 這樣一直進行到發送了隨機數的信號為止。 然后,它將忽略五個信號。 忽略五個信號后,父進程將打印接收到的信號數,並在接收到另一個信號后退出。

我的問題是從子進程發送信號時程序崩潰。 父進程具有信號處理程序,但從未使用過。 我犯什么錯誤?

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

void checkArguments(int nbrOfArg);
static void sig_exit(int signo);
static void sig_handler(int signo);

int sigcount, n;

int main(int argc, char *argv[]){
    int interval, i, linecount = 1;
    char buffer[1024];
    FILE *f;
    sigcount = 0;
    signal(SIGUSR1, sig_handler);
    srand(time(NULL)); // generate random numbers   
    n = rand() % 20 + 10;
    if(fork() != 0){ //parent process
        printf("%d\n", getpid()); //debugging
        checkArguments(argc);
        f = fopen(argv[1], "r"); //open file
        if(f == NULL){
            printf("Error opening file\n");
            exit(EXIT_FAILURE);
        }
        if(sigcount < n){ //n is random number of signals sent that starts/stops printing
            signal(SIGUSR1, sig_handler); //set signal handler
            do {
                while(!feof(f)){// infinite printing loop
                    fgets(buffer, 1024, f);
                    if(sigcount % 2 == 0){ //stop printing at odd number of signals received
                        printf("%d %s", linecount, buffer);
                    }   
                    linecount++;
                }
                rewind(f);
                linecount = 1;  
            } while(linecount == 1); //infinite loop
        } else if(sigcount < (n + 6)) {
            signal(SIGUSR1, SIG_IGN);   // ignore signal
        } else {
            printf("%d signals received", sigcount);
            signal(SIGUSR1, sig_exit); // terminate parent process
        }   
    } else {
        for(i = 0; i < n; i++){
            interval = rand() % 10 + 1; //send signals at random interval
            sigcount++; //number of signals sent
            printf("Sending signal %d from %d to %d\n", sigcount, getpid(), getppid()); //debugging
            kill(getppid(), SIGUSR1); //send signal
            printf("Child sleeping for %d seconds\n", interval); //debugging
            sleep(interval); //let child sleep after sending signal
        }
    }
}

/** Checks so command line argument is valid **/
void checkArguments(int nbrOfArg){
    int k;
    if(nbrOfArg != 2){
        printf("Wrong number of arguments");
        exit(-1);
    }
}

void sig_handler(int signo){
    if(signo == SIGUSR1){
        printf("Receivied SIGUSR1 signal\n");   
    } else printf("Error: received undefined signal\n");

}

void sig_exit(int signo){
    if(signo == SIGUSR1){
        printf("Received SIGUSR1 signal\n");
        exit(SIGUSR1);  
    } else printf("Error: received undefined signal\n");
}

除了注釋中指出的要點外,還應該查看設置信號處理程序的位置,這是在調用fork()並啟動子進程之后,因此您所擁有的是父級與子級之間的競爭條件:

         fork()
      /         \
 Parent          Child
 CheckArguments  Send signal     
 Open file
 Create Handler

因此,在父級注冊其處理程序之前,孩子啟動並發送信號。 您可以通過在孩子發送第一個信號之前為其添加睡眠來檢查這是否是問題。

另外,信號的行為在Unix版本之間也有所不同,當我在計算機上嘗試您的代碼時,第一個信號被捕獲,但此后沒有。 這是因為在我的機器上,信號處理程序在運行后會被卸載,因此您需要將其作為信號處理程序的一部分重新啟用。

void sig_handler(int signo){
    if(signo == SIGUSR1)
    {
        printf("Receivied SIGUSR1 signal\n");
    } 
    else {
        printf("Error: received undefined signal\n");
    }
    signal(SIGUSR1, sig_handler);
}

我還將考慮更改為使用sigaction(而不是signal(),它可以使您更好地控制信號處理。

不同的系統使用signal()表現不同。 在運行macOS的Mac上(10.14.1 Mojave,但它也適用於其他版本),使用signal()的原始代碼可以正常工作-有很多陷阱,但是信號處理有效。 在虛擬機上運行的Ubuntu 18.04 LTS(托管在同一Mac上)時,使用代碼signal()因為(如指出的不正常工作的意見 ),當信號被捕獲的信號處理重置為默認值,前輸入信號處理程序。 這就建立了比賽條件。 這兩種不同的行為都符合標准C的要求-macOS提供了“可靠的信號”,而Linux沒有。

但是,一切並沒有丟失。 Linux和macOS都具有sigaction() ,它可以實現很好的控制—可以用來模擬任一組signal()行為。 另請參見sigaction()signal()什么區別?

還有其他一些問題需要解決。 您應該在分叉之前驗證參數並(請檢查是否可以)打開文件; 您應該報告錯誤。 子代應記錄其原始父代的PID,以便如果父代去世,則可以嘗試向其發送信號並收到通知,告知其失敗。 當原始父代去世時,父代PID切換到PID 1,即init過程,該過程基本上結束於忽略信號。

我已經解決了feof()的問題-沒有理由在循環的控制條件下使用feof() (請參閱while (!feof(file))總是錯誤的! )。 而是測試基本的I / O功能。 (Ubuntu上的C庫標記了fgets()函數,以便必須使用返回值。請注意編譯器的警告。)

下面的代碼減慢了主打印循環的速度,因此它每秒處理4行,而不是全傾斜運行。 macOS和Linux都具有nanosleep() ; Linux沒有使usleep()可用,盡管它具有更簡單(但功能較弱)的接口。

下面的代碼將sigcount使用的sigcount與孩子用來計數接收和發送的信號的i分開。 我還假定了C99或更高版本的支持,並將變量聲明移到了它們使用的地方。

按照編寫的方式,代碼永遠不會進入忽略SIGUSR1的狀態,更不用說觸發錯誤的狀態了。 子級(現在)發送了足夠的信號( n *= 2;發送了父級“期望”的兩倍信號),但是父級仍然卡在if (sigcount < n)的原始代碼中。 請注意,如果忽略這些信號,則無法計數。 代碼的那部分需要認真的修改。 收到適當數量的信號后,您應該退出文件讀取循環,然后簡單地計算接下來的五個信號(不要忽略它們),然后設置sig_exit處理程序。 下面的代碼未實現此功能。

我沒有嘗試通過在信號處理程序中調用printf()來解決問題。 它似乎並沒有在代碼中引起麻煩(我也不希望這樣),但是總的來說這很危險。 請參閱如何避免在信號處理程序中使用printf() 更多細節。

#define _XOPEN_SOURCE 700允許定義POSIX函數等,即使在編譯選項要求使用-std=c11 (禁用大多數擴展)的情況下也是如此。 程序源位於sig41.c ,因此該程序為sig41 該代碼可以使用(GCC 8.2.0)干凈地編譯:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
>     sig41.c -o sig41
$

除了一些其他小的更改(例如,在stderr上報告錯誤)之外,此代碼還可以在Ubuntu和macOS上運行:

#define _XOPEN_SOURCE 700
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

static void sig_exit(int signo);
static void sig_handler(int signo);

static int sigcount, n;

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s filename\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    FILE *f = fopen(argv[1], "r");
    if (f == NULL)
    {
        fprintf(stderr, "Error opening file %s for reading\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    struct sigaction sa = { 0 };
    sa.sa_handler = sig_handler;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGUSR1, &sa, 0);

    srand(time(NULL));
    n = rand() % 20 + 10;
    int pid = fork();
    if (pid < 0)
    {
        fprintf(stderr, "failed to fork!\n");
        exit(EXIT_FAILURE);
    }
    else if (pid != 0)
    {
        printf("%d\n", getpid());
        if (sigcount < n)
        {
            int linecount = 1;
            while (linecount == 1)
            {
                char buffer[1024];
                while (fgets(buffer, 1024, f))
                {
                    if (sigcount % 2 == 0)
                    {
                        printf("%d %s", linecount, buffer);
                    }
                    linecount++;
                    // nap time = quarter second
                    struct timespec nap = { .tv_sec = 0, .tv_nsec = 250000000 };
                    nanosleep(&nap, NULL);
                }
                rewind(f);
                linecount = 1;
            }
        }
        else if (sigcount < (n + 6))
        {
            printf("Going into SIG_IGN mode\n");
            sa.sa_handler = SIG_IGN;
            sigaction(SIGUSR1, &sa, 0);
        }
        else
        {
            printf("%d of %d signals received - sig_exit mode\n", sigcount, n);
            sa.sa_handler = sig_exit;
            sigaction(SIGUSR1, &sa, 0);
        }
    }
    else
    {
        fclose(f);
        int pid = getpid();
        int ppid = getppid();
        n *= 2;                     // Child needs to send more signals
        for (int i = 0; i < n; i++)
        {
            int interval = rand() % 10 + 1;
            printf("Sending signal %d of %d from %d to %d\n", i + 1, n, pid, ppid);
            if (kill(ppid, SIGUSR1) != 0)
            {
                fprintf(stderr, "Child failed to signal parent - exiting\n");
                exit(1);
            }
            printf("Child sleeping for %d seconds\n", interval);
            sleep(interval);
        }
    }
}

static void sig_handler(int signo)
{
    sigcount++;
    if (signo == SIGUSR1)
        printf("Received SIGUSR1 signal %d of %d\n", sigcount, n);
    else
        printf("Error: received undefined signal\n");
}

static void sig_exit(int signo)
{
    if (signo == SIGUSR1)
    {
        fprintf(stderr, "Received SIGUSR1 signal\n");
        exit(SIGUSR1);
    }
    else
        printf("Error: received undefined signal\n");
}

要做好這項工作,要做好一點困難。 我最終中斷了程序以停止它。

$ ./sig41 sig41.c
3247
1 #define _XOPEN_SOURCE 700
Sending signal 1 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 1 of 15
Sending signal 2 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 2 of 15
30     sa.sa_flags = 0;
31     sigemptyset(&sa.sa_mask);
…
56                     }
57                     linecount++;
Sending signal 3 of 30 from 3248 to 3247
Child sleeping for 1 seconds
Received SIGUSR1 signal 3 of 15
Sending signal 4 of 30 from 3248 to 3247
Child sleeping for 4 seconds
Received SIGUSR1 signal 4 of 15
62                 rewind(f);
63                 linecount = 1;
…
76             sigaction(SIGUSR1, &sa, 0);
77         }
Sending signal 5 of 30 from 3248 to 3247
Child sleeping for 2 seconds
Received SIGUSR1 signal 5 of 15
Sending signal 6 of 30 from 3248 to 3247
Child sleeping for 3 seconds
Received SIGUSR1 signal 6 of 15
86         {
87             int interval = rand() % 10 + 1;
…
96         }
97     }
Sending signal 7 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 7 of 15
Sending signal 8 of 30 from 3248 to 3247
Child sleeping for 10 seconds
Received SIGUSR1 signal 8 of 15
8 static void sig_exit(int signo);
9 static void sig_handler(int signo);
…
46         {
47             int linecount = 1;
Sending signal 9 of 30 from 3248 to 3247
Child sleeping for 5 seconds
Received SIGUSR1 signal 9 of 15
Sending signal 10 of 30 from 3248 to 3247
Child sleeping for 8 seconds
Received SIGUSR1 signal 10 of 15
68             printf("Going into SIG_IGN mode\n");
69             sa.sa_handler = SIG_IGN;
…
98 }
99 
Sending signal 11 of 30 from 3248 to 3247
Child sleeping for 9 seconds
Received SIGUSR1 signal 11 of 15
Sending signal 12 of 30 from 3248 to 3247
Child sleeping for 4 seconds
Received SIGUSR1 signal 12 of 15
18         exit(EXIT_FAILURE);
19     }
…
32     sigaction(SIGUSR1, &sa, 0);
33 
Sending signal 13 of 30 from 3248 to 3247
Child sleeping for 6 seconds
Received SIGUSR1 signal 13 of 15
Sending signal 14 of 30 from 3248 to 3247
Child sleeping for 6 seconds
Received SIGUSR1 signal 14 of 15
58                     // nap time = quarter second
59                     struct timespec nap = { .tv_sec = 0, .tv_nsec = 250000000 };
…
80     {
81         fclose(f);
Sending signal 15 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 15 of 15
Sending signal 16 of 30 from 3248 to 3247
Child sleeping for 8 seconds
Received SIGUSR1 signal 16 of 15
110 {
111     if (signo == SIGUSR1)
…
22     if (f == NULL)
23     {
Sending signal 17 of 30 from 3248 to 3247
Child sleeping for 1 seconds
Received SIGUSR1 signal 17 of 15
Sending signal 18 of 30 from 3248 to 3247
Child sleeping for 6 seconds
Received SIGUSR1 signal 18 of 15
28     struct sigaction sa = { 0 };
29     sa.sa_handler = sig_handler;
…
^C
$

如果您處理輸出,則可以看到輸出暫停了給定的行數-如果孩子睡眠1秒鍾,則省略4條輸出線。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM