使用 c 安排警报

Schedule alarm using c

我们的任务是开发一个创建 child 进程的应用程序。然后 parent 进程等待 3 秒(我们不允许在 parent 进程中使用 sleep )并发送一个 SIGUSR2 信号,然后发送一个 SIGUSR1 信号.之后,parent 定期(3 秒)向 child 发送 SIGUSR1 信号,直到它终止。在 child 终止时它打印 Parent done 并退出。我们应该使用 alarm()pause().

来实现这个行为

child 进程应该阻塞信号 SIGUSR2 13 秒。收到 SIGUSR1 后,它应该打印 Received SIGUSR1。收到 SIGUSR2 后,它会打印 Child done 并退出。我的实现方法如下:

请注意SIGUSR2信号只发送一次,直到现在我还没有实现它,每次都与[=13=一起发送]信号。

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

static pid_t chld;

void init_signal(struct sigaction* action, void (*handler)(int));
void init_sigset(sigset_t* set, int signum);
void bind_signal(struct sigaction* action, int n, ...);
void par_signal(int signum);
void chld_signal(int signum);

int main(){
    chld = fork();
    struct sigaction action;

    if (chld == -1){
        fprintf(stderr, "Failed to create child process.\n");
        return EXIT_FAILURE;
    } else if (chld == 0){
        init_signal(&action, &chld_signal);
        bind_signal(&action, 3, SIGUSR1, SIGUSR2, SIGALRM);        

        sigset_t sig;
        init_sigset(&sig, SIGUSR2);

        sigprocmask(SIG_BLOCK, &sig, NULL);
        alarm(13);
        pause();
    }
    init_signal(&action, &par_signal);
    bind_signal(&action, 2, SIGALRM, SIGCHLD);

    alarm(3);
    pause();
}

void init_signal(struct sigaction* action, void (*handler)(int)){
    action->sa_flags = 0;
    action->sa_handler = handler;

    sigemptyset(&action->sa_mask);
}

void init_sigset(sigset_t* set, int signum){
    sigemptyset(set);
    sigaddset(set, signum);
}

void bind_signal(struct sigaction* action, int n, ...){
    va_list args;
    va_start(args, n);

    for (int i = 0; i < n; i++){
        int signum = va_arg(args, int);
        sigaction(signum, action, NULL);
    }
    va_end(args);
}

void par_signal(int signum){
    if (signum == SIGALRM){
        kill(chld, SIGUSR2);
        kill(chld, SIGUSR1);
        alarm(3);
        pause();
    } else if (signum == SIGCHLD){
        printf("Parent done\n");
        exit(EXIT_SUCCESS);
    }
}

void chld_signal(int signum){
    if (signum == SIGALRM){
        sigset_t sig;
        init_sigset(&sig, SIGUSR2);

        sigprocmask(SIG_UNBLOCK, &sig, NULL);
        pause();
    } else if (signum == SIGUSR1){
        printf("Received SIGUSR1\n");
        pause();
    } else if (signum == SIGUSR2){
        printf("Child done\n");
        exit(EXIT_SUCCESS);
    }
}

除了 child 仅接收到 parent 进程的第一个信号这一事实之外,它的工作原理也是如此。我认为这与我分别使用 pause alarm 有关。你知道我做错了什么吗?

可以找到练习任务表的 link here(任务 3)。

在信号处理程序中调用 pause 是有问题的,因为在这种情况下,刚刚触发信号处理程序的信号会被阻止,直到您从信号处理程序中 return。这意味着该进程将不再对该信号做出反应。信号处理程序只能由其他信号触发。

请参阅下面的系统调用跟踪和解释。为了清楚起见,我用 @PARENT@ 替换了 parent 的 PID,用 #CHILD## 替换了 child 的 PID。

$ strace -f -r -e trace=signal,process ./proc
     0.000000 execve("./proc", ["./proc"], 0x7ffc146ba360 /* 74 vars */) = 0
     0.001428 arch_prctl(0x3001 /* ARCH_??? */, 0x7ffd3c958fc0) = -1 EINVAL (Invalid argument)
     0.002302 arch_prctl(ARCH_SET_FS, 0x7f264e40a540) = 0
# clone is fork()
     0.001086 clone(strace: Process #CHILD## attached
child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f264e40a810) = #CHILD##
# parent establishes SIGALRM handler
[pid @PARENT@]      0.000791 rt_sigaction(SIGALRM, {sa_handler=0x564709a5859c, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f264e25e470},  <unfinished ...>
# child establishes SIGUSR1 handler
[pid #CHILD##]      0.000087 rt_sigaction(SIGUSR1, {sa_handler=0x564709a58605, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f264e25e470},  <unfinished ...>
[pid @PARENT@]      0.000132 <... rt_sigaction resumed> NULL, 8) = 0
[pid #CHILD##]      0.000056 <... rt_sigaction resumed> NULL, 8) = 0
# child establishes SIGUSR2 handler
[pid #CHILD##]      0.000072 rt_sigaction(SIGUSR2, {sa_handler=0x564709a58605, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f264e25e470}, NULL, 8) = 0
# child establishes SIGALRM handler
[pid #CHILD##]      0.000141 rt_sigaction(SIGALRM, {sa_handler=0x564709a58605, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f264e25e470}, NULL, 8) = 0
# parent establishes SIGCHLD handler
[pid @PARENT@]      0.000389 rt_sigaction(SIGCHLD, {sa_handler=0x564709a5859c, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f264e25e470},  <unfinished ...>
# child blocks SIGUSR2
[pid #CHILD##]      0.000132 rt_sigprocmask(SIG_BLOCK, [USR2], NULL, 8) = 0
# child waits for signal
[pid #CHILD##]      0.000204 pause( <unfinished ...>
[pid @PARENT@]      0.000837 <... rt_sigaction resumed> NULL, 8) = 0
# parent waits for signal
[pid @PARENT@]      0.000133 pause()       = ? ERESTARTNOHAND (To be restarted if no handler)
# parent receives SIGALRM
[pid @PARENT@]      3.000317 --- SIGALRM {si_signo=SIGALRM, si_code=SI_KERNEL} ---
# parent sends SIGUSR2 and SIGUSR1 to child
[pid @PARENT@]      0.000106 kill(#CHILD##, SIGUSR2) = 0
[pid @PARENT@]      0.000116 kill(#CHILD##, SIGUSR1) = 0
[pid #CHILD##]      0.000144 <... pause resumed> ) = ? ERESTARTNOHAND (To be restarted if no handler)
# child receives SIGUSR1 (SIGUSR2 is blocked)
[pid #CHILD##]      0.000166 --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=@PARENT@, si_uid=1000} ---
# parent waits for signal again. As this "pause" was called from a signal handler triggered by SIGALRM, this signal is now blocked.
[pid @PARENT@]      0.000227 pause( <unfinished ...>
# child waits for signal. As this "pause" was called from a signal handler triggered by SIGUSR1, this signal is now blocked.
[pid #CHILD##]      0.000566 pause()       = ? ERESTARTNOHAND (To be restarted if no handler)
# child receives SIGALRM
[pid #CHILD##]      9.997742 --- SIGALRM {si_signo=SIGALRM, si_code=SI_KERNEL} ---
# child unblocks SIGUSR2
[pid #CHILD##]      0.000102 rt_sigprocmask(SIG_UNBLOCK, [USR2], NULL, 8) = 0
# child now receives SIGUSR2 which was already sent by the parent
[pid #CHILD##]      0.000122 --- SIGUSR2 {si_signo=SIGUSR2, si_code=SI_USER, si_pid=@PARENT@, si_uid=1000} ---
# child exits
[pid #CHILD##]      0.000183 exit_group(0) = ?
[pid #CHILD##]      0.000204 +++ exited with 0 +++
     0.000081 <... pause resumed> )     = ? ERESTARTNOHAND (To be restarted if no handler)
# parent receives SIGCHLD from child
     0.000056 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=#CHILD##, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
Parent done
# parent exits
     0.000563 exit_group(0)             = ?
     0.000301 +++ exited with 0 +++

我建议从信号处理程序设置标志并在主函数中进行决策和等待。像这样

static volatile sig_atomic_t childSigUsr1 = 0;
static volatile sig_atomic_t childSigUsr2 = 0;
static volatile sig_atomic_t childSigAlrm = 0;

void chld_signal(int signum) {
    if (signum == SIGALRM) {
        childSigAlrm = 1;
    } else if (signum == SIGUSR1) {
        childSigUsr1 = 1;
    } else if (signum == SIGUSR2) {
        childSigUsr2 = 1;
    }
}

int main() {
    chld = fork();
    struct sigaction action;

    if (chld == -1) {
        fprintf(stderr, "Failed to create child process.\n");
        return EXIT_FAILURE;
    } else if (chld == 0) {
        init_signal(&action, &chld_signal);
        bind_signal(&action, 3, SIGUSR1, SIGUSR2, SIGALRM);        

        sigset_t sig;
        init_sigset(&sig, SIGUSR2);

        sigprocmask(SIG_BLOCK, &sig, NULL);
        alarm(13);
        while(1) {
            pause();
            if(childSigUsr1) {
                childSigUsr1 = 0;
                printf("Received SIGUSR1\n");
            }
            if(childSigUsr2) {
                childSigUsr2 = 0;
                printf("Child done\n");
                exit(EXIT_SUCCESS);
            }
            if(childSigAlrm) {
                sigset_t sig;
                init_sigset(&sig, SIGUSR2);

                sigprocmask(SIG_UNBLOCK, &sig, NULL);
            }
        }
    }
    init_signal(&action, &par_signal);
    bind_signal(&action, 2, SIGALRM, SIGCHLD);

    while(1) {
        alarm(3);
        pause();
        /* handle and reset signal flags similar to child */
    }
}