C 使用alarm()和pause()频繁发送信号

C 使用alarm()和pause()频繁发送信号,c,signals,alarm,pause,C,Signals,Alarm,Pause,我的任务是: 编写一个程序,其中父进程恰好创建一个子进程。创建子进程后,父进程的行为如下:SIGUSR1每5秒向子进程发送一次信号。要实现此行为,父进程通常使用以下系统调用:alarm()和pause()。在发送信号SIGUSR1三次后,第四次将信号SIGUSR2发送给子系统。在此之后,父对象等待子对象完成 子对象的行为如下:它等待任何信号中断。如果接收到的信号为SIGUSR1,则会将消息打印到标准输出。如果接收到的信号是SIGUSR2,则信号结束。此外,在其执行的前5秒钟内,应阻止信号SIGU

我的任务是:

编写一个程序,其中父进程恰好创建一个子进程。创建子进程后,父进程的行为如下:
SIGUSR1
每5秒向子进程发送一次信号。要实现此行为,父进程通常使用以下系统调用:
alarm()
pause()
。在发送信号
SIGUSR1
三次后,第四次将信号
SIGUSR2
发送给子系统。在此之后,父对象等待子对象完成

子对象的行为如下:它等待任何信号中断。如果接收到的信号为
SIGUSR1
,则会将消息打印到标准输出。如果接收到的信号是
SIGUSR2
,则信号结束。此外,在其执行的前5秒钟内,应阻止信号
SIGUSR2
。要求学生在手册页中检查
报警()
暂停()
的行为

我的解决方案是这样的。我试图忽略父对象中的
报警()
,通过子对象中的报警,我将标志设置为true并更改unblock
SIGUSR2

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

bool flag = false;

void alert_ignore(int signum) {
    printf("catched alarm\n");
    return;
}

void alert_setting_flag(int signum) {
    flag = true;
    return;
}

void sigusr1_handler(int signum) {
    printf("Recieved SIGUSR1\n");
    return;
}

void sigusr2_handler(int signum) {
    printf("Recieved SIGUSR2\n");
    exit(0);
}


int main(int argc, char *argv[]) {

    sigset_t set;
    if (sigemptyset(&set) == -1) {
        perror("sigemptyset");
        exit(1);
    }
    if (sigaddset(&set, SIGUSR2) == -1) {
        perror("sigaddset");
        exit(1);
    }

    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
        perror("sigprocmask");
        exit(1);
    }

    struct sigaction alert_action;
    alert_action.sa_handler = alert_ignore;
    alert_action.sa_mask = set;
    if (sigaction(SIGALRM, &alert_action, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    struct sigaction sigusr1_action;
    sigusr1_action.sa_handler = sigusr1_handler;
    sigusr1_action.sa_mask = set;
    if (sigaction(SIGUSR1, &sigusr1_action, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    struct sigaction sigusr2_action;
    sigusr2_action.sa_handler = sigusr2_handler;
    if (sigaction(SIGUSR2, &sigusr2_action, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) {
        /* Child process duties */
        /* Setting alert handler to turn flag form false to true */
        alert_action.sa_handler = alert_setting_flag;
        if (sigaction(SIGALRM, &alert_action, NULL) == -1) {
            perror("sigaction");
            exit(1);
        }

        alarm(5);
        int count = 0;
        while (true) {
            pause();
            if (flag == true && !count) {
                if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1) {
                    perror("sigprocmask");
                    exit(1);
                }
                printf("SIGUSR2 unblocked\n");
                count++;
            }
        }
    }

    /* Parent Process duties */
    for (int i = 0; i < 3; ++i) {
        alarm(5);
        pause();
        kill(pid, SIGUSR1);
    }
    kill(pid, SIGUSR2);
    wait(NULL);

    return 0;
}
这种行为的原因是什么?(对我来说最重要的部分是,为什么没有按预期忽略SIGALRM,我知道存在一些问题,我没有以原子方式设置标志,但这不应该影响我的父进程,不是吗?

您有:

struct sigaction alert_action;
alert_action.sa_handler = alert_ignore;
alert_action.sa_mask = set;
if (sigaction(SIGALRM, &alert_action, NULL) == -1) {
这里,
alert\u action.sa\u flags
未初始化,也未分配给,因此它将是一个随机整数。可能会发生一些不好的事情。如果
SA_RESETHAND
标志打开,则在收到第一个信号后,信号操作将重置为默认值,下一个信号将终止进程。这就是为什么有时会看到shell打印出闹钟的原因

若要解决此问题,请初始化结构或为所有必需字段使用赋值语句。例如:

struct sigaction alert_action = {
        .sa_handler = alert_ignore,
        .sa_mask = set,
        .sa_flags = 0
};

您应该对所有
struct sigaction
变量执行此操作。

首先,您只能在信号处理程序中安全地调用异步信号安全函数。而且
printf()
不是一个异步信号安全函数。非常感谢。清楚的
struct sigaction alert_action = {
        .sa_handler = alert_ignore,
        .sa_mask = set,
        .sa_flags = 0
};