繁体   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