使用 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 */
}
}
我们的任务是开发一个创建 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 */
}
}