使用 alarm() 和 pause() 频繁发送信号
Sending signal frequently with alarm() and pause()
我的任务是:
编写一个程序,其中 parent 进程恰好创建 1 child。创建child后,parent进程的行为如下:它发送信号SIGUSR1
给child 5秒。为实现此行为,parent 进程最常使用以下系统调用:alarm()
和 pause()
。发送信号SIGUSR1
三次后,第四次发送信号SIGUSR2
给child。此后,parent 等待 child 完成。
child 的行为如下:它一直等待直到被任何信号中断。如果接收到的信号是 SIGUSR1
,它会向标准输出打印一条消息。如果收到的信号是 SIGUSR2
那么它就结束了。此外,在其执行的前 5 秒内,信号 SIGUSR2
应该被阻塞。学生应检查手册页中 alarm()
和 pause()
的行为。
我的解决方案如下所示。我试图忽略 parent 中的 alarm()
并通过 child 中的警报我将标志设置为 true 并更改解锁 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;
}
这有效,但只是有时我会遇到很多随机行为,如下所示,我不知道为什么。
Case(0) desired behavior
catched alarm // after 5 seconds
SIGUSR2 unblocked
Recieved SIGUSR1
catched alarm // after 10 seconds
Recieved SIGUSR1
catched alarm // after 15 seconds
Recieved SIGUSR1
Recieved SIGUSR2
Case 1 (a lot):
catched alarm // after 5 seconds
Recieved SIGUSR1
SIGUSR2 unblocked
Alarm clock // after 10 seconds
Case 2 (very rare) needed to terminate it:
catched alarm // after 5 seconds
catched alarm // after 10 seconds
catched alarm // after 15 seconds
^Z
Case 3 (double printing SIGUSR2 unblocked):
catched alarm // after 5 seconds
SIGUSR2 unblocked
SIGUSR2 unblocked
Recieved SIGUSR1
catched alarm // after 10 seconds
Recieved SIGUSR1
catched alarm // after 15 seconds
Recieved SIGUSR1
Recieved SIGUSR2
Case 4:
catched alarm // after 5 seconds
Alarm clock
这种行为的原因是什么? (对我来说最重要的部分是,为什么 SIGALRM 没有按要求被忽略,我知道我没有以原子方式设置标志是有问题的,但这不应该影响我的 parent 过程,不?)
你有:
struct sigaction alert_action;
alert_action.sa_handler = alert_ignore;
alert_action.sa_mask = set;
if (sigaction(SIGALRM, &alert_action, NULL) == -1) {
这里,alert_action.sa_flags
没有被初始化也没有赋值,所以它将是一个随机整数。可能会发生许多坏事。如果 SA_RESETHAND
标志打开,那么在收到第一个 SIGALRM 后信号动作将被重置为默认值,下一个 SIGALRM 将杀死进程。这就是为什么您有时会看到 shell 打印出 Alarm clock
.
要解决此问题,请初始化结构或为所有必要字段设置赋值语句。例如:
struct sigaction alert_action = {
.sa_handler = alert_ignore,
.sa_mask = set,
.sa_flags = 0
};
您应该对所有 struct sigaction
变量执行此操作。
我的任务是:
编写一个程序,其中 parent 进程恰好创建 1 child。创建child后,parent进程的行为如下:它发送信号SIGUSR1
给child 5秒。为实现此行为,parent 进程最常使用以下系统调用:alarm()
和 pause()
。发送信号SIGUSR1
三次后,第四次发送信号SIGUSR2
给child。此后,parent 等待 child 完成。
child 的行为如下:它一直等待直到被任何信号中断。如果接收到的信号是 SIGUSR1
,它会向标准输出打印一条消息。如果收到的信号是 SIGUSR2
那么它就结束了。此外,在其执行的前 5 秒内,信号 SIGUSR2
应该被阻塞。学生应检查手册页中 alarm()
和 pause()
的行为。
我的解决方案如下所示。我试图忽略 parent 中的 alarm()
并通过 child 中的警报我将标志设置为 true 并更改解锁 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;
}
这有效,但只是有时我会遇到很多随机行为,如下所示,我不知道为什么。
Case(0) desired behavior
catched alarm // after 5 seconds
SIGUSR2 unblocked
Recieved SIGUSR1
catched alarm // after 10 seconds
Recieved SIGUSR1
catched alarm // after 15 seconds
Recieved SIGUSR1
Recieved SIGUSR2
Case 1 (a lot):
catched alarm // after 5 seconds
Recieved SIGUSR1
SIGUSR2 unblocked
Alarm clock // after 10 seconds
Case 2 (very rare) needed to terminate it:
catched alarm // after 5 seconds
catched alarm // after 10 seconds
catched alarm // after 15 seconds
^Z
Case 3 (double printing SIGUSR2 unblocked):
catched alarm // after 5 seconds
SIGUSR2 unblocked
SIGUSR2 unblocked
Recieved SIGUSR1
catched alarm // after 10 seconds
Recieved SIGUSR1
catched alarm // after 15 seconds
Recieved SIGUSR1
Recieved SIGUSR2
Case 4:
catched alarm // after 5 seconds
Alarm clock
这种行为的原因是什么? (对我来说最重要的部分是,为什么 SIGALRM 没有按要求被忽略,我知道我没有以原子方式设置标志是有问题的,但这不应该影响我的 parent 过程,不?)
你有:
struct sigaction alert_action;
alert_action.sa_handler = alert_ignore;
alert_action.sa_mask = set;
if (sigaction(SIGALRM, &alert_action, NULL) == -1) {
这里,alert_action.sa_flags
没有被初始化也没有赋值,所以它将是一个随机整数。可能会发生许多坏事。如果 SA_RESETHAND
标志打开,那么在收到第一个 SIGALRM 后信号动作将被重置为默认值,下一个 SIGALRM 将杀死进程。这就是为什么您有时会看到 shell 打印出 Alarm clock
.
要解决此问题,请初始化结构或为所有必要字段设置赋值语句。例如:
struct sigaction alert_action = {
.sa_handler = alert_ignore,
.sa_mask = set,
.sa_flags = 0
};
您应该对所有 struct sigaction
变量执行此操作。