POSIX 信号并不总是得到处理
POSIX Signals does not get always get handled
我正在尝试通过以下方式设计和实施模拟考试
POSIX 进程和进程间通信技术。模拟
工作方式如下:
名为 Teacher 的进程将从
从头到尾。
教师进程使用 fork/exec 系统调用 generate/load 10 个学生
进程。
每个学生进程:
一个。等待老师的信号开始考试。
b。一旦学生收到信号,它将开始考试。
c。学生进程将调用睡眠系统 API 随机睡眠
在 [10 – 30] 秒之间选择的数字,以模拟
不同进程完成考试的时间不同
d。一旦该进程的睡眠结束,它将触发一个信号,表明它
已完成考试
教师进程将继续跟踪学生进程的顺序
已经完成了。
所有Student进程结束后,Teacher进程会宣布
考试成绩如下,终止。
考试结束 -> 结果是:
ID=4128的同学第一名
ID=4122 学生第二名
ID=4126 的学生获得第三名
...
我在 c:
中制作了这段代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <sys/wait.h>
#include<signal.h>
static int child_recieved_sig = 0;
static int parent_recieved_sig = 0;
static int pids[10];
static int pidIndex = 0;
int random_time() {
// generate random time from 10-30 sec
srand(time(NULL) + getpid());
int rand_time = (rand() % 21) + 10; //form [0-20]+10
return rand_time;
}
//signal sent from the parent to the child to start the exam
void start_signal(int sig) {
if (sig == SIGUSR1) {
child_recieved_sig = 1;
}
}
//signal sent from the child to the parent indicating that the exam is over
void finish_signal(int sig, siginfo_t* info, void* context) {
if (sig == SIGUSR2) {
parent_recieved_sig++;
pids[pidIndex] = info->si_pid;
pidIndex++;
}
}
int main() {
struct sigaction ss = { 0 }; // fs = start signal
ss.sa_sigaction = start_signal;
ss.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &ss, NULL);
struct sigaction fs = { 0 }; // fs = finish signal
fs.sa_sigaction = finish_signal;
fs.sa_flags = SA_SIGINFO;
sigaction(SIGUSR2, &fs, NULL);
for (int i = 0;i < 10;i++) { //generate 10 children
pid_t pid = fork();
if (pid < 0)
perror("Forking has Failed!");
else if (pid == 0) { //child process
while (child_recieved_sig != 1); // wait for signal
printf("My ID is %d and I started my exam\n", getpid());
int time_interval = random_time();
printf("SleepTime: %d<-----------------------\n", time_interval);
sleep(time_interval);
kill(getppid(), SIGUSR2);
printf("Student %d finished exam\n", getpid());
exit(0);
} else {
kill(pid, SIGUSR1);
}
}
while (parent_recieved_sig < 10) usleep(50000);
printf("parent_received_sig: %d\n", parent_recieved_sig);
printf("The Exam is Over ==> Results are:\n");
printf("Student ID=%d Finished First\n", pids[0]);
printf("Student ID=%d Finished Second\n", pids[1]);
printf("Student ID=%d Finished Third\n", pids[2]);
printf("Student ID=%d Finished Forth\n", pids[3]);
printf("Student ID=%d Finished Fifth\n", pids[4]);
printf("Student ID=%d Finished Sixth\n", pids[5]);
printf("Student ID=%d Finished Seventh\n", pids[6]);
printf("Student ID=%d Finished Eighth\n", pids[7]);
printf("Student ID=%d Finished Nineth\n", pids[8]);
printf("Student ID=%d Finished Tenth\n", pids[9]);
}
问题是有时代码可以正常工作并按预期完成工作,而其他时候没有对代码进行任何修改它就会卡在 while look (while (parent_recieved_sig < 10) usleep(50000);
) 中,我一直在努力弄清楚原因,但我不能得出结论,我试过打印 parent_recieved_sig
的值,有时它没有达到 10,意味着某些信号没有被处理?
只有实时信号排队。其他的可以合并。有可能发送了两个而只收到了一个。
如另一个答案中所述,非实时信号不会排队,因此如果它们同时发送,您可能会错过其中一些信号(来自OS 观点).
下面是您的示例,为了用 SIGCHLD
的处理替换 SIGUSR2
的处理,稍作修改。
当子进程终止时,此信号会自动发送到其父进程。
有趣的部分是信号处理程序,只要成功,它就会以非阻塞方式 (WNOHANG
) 为任何子进程 (-1
) 在 waitpid()
上循环。
处理一个 SIGCHLD
可能导致检测到多个子进程同时终止。
EDIT1 如评论中所建议,更直接的解决方案是摆脱 SIGCHLD
的处理并修改 [=19 中的等待循环=] 像这样的功能
while (parent_recieved_sig < 10) {
pid_t r=wait(NULL);
if(r>0) {
parent_recieved_sig++;
pids[pidIndex] = r;
pidIndex++;
} else {
perror("wait()");
exit(1);
}
}
EDIT2 正如评论中所建议的,全局 int
s 应该是 volatile sig_atomic_t
s 以便被检测到(优化器不认为未更改) 在被信号处理程序修改时由主线程执行。
/**
gcc -std=c99 -o prog_c prog_c.c \
-pedantic -Wall -Wextra -Wconversion \
-Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#undef __STRICT_ANSI__
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
// ensure modifications in the signal handlers are detected
static volatile sig_atomic_t child_recieved_sig = 0;
static volatile sig_atomic_t parent_recieved_sig = 0;
static volatile sig_atomic_t pids[10];
static volatile sig_atomic_t pidIndex = 0;
int random_time(void) {
// generate random time from 10-30 sec
srand((unsigned int)time(NULL) + (unsigned int)getpid());
int rand_time = (rand() % 5) + 2; //form [0-20]+10
return rand_time;
}
//signal sent from the parent to the child to start the exam
void start_signal(int sig, siginfo_t* info, void* context) {
(void)info;
(void)context;
if (sig == SIGUSR1) {
child_recieved_sig = 1;
}
}
//signal sent from the child to the parent indicating that the exam is over
void finish_signal(int sig, siginfo_t* info, void* context) {
(void)info;
(void)context;
if (sig == SIGCHLD) {
for(;;) {
pid_t r=waitpid(-1, NULL, WNOHANG);
if(r<=0) break;
parent_recieved_sig++;
pids[pidIndex] = r;
pidIndex++;
}
}
}
int main(void) {
struct sigaction ss = { 0 }; // fs = start signal
ss.sa_sigaction = start_signal;
ss.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &ss, NULL);
struct sigaction fs = { 0 }; // fs = finish signal
fs.sa_sigaction = finish_signal;
fs.sa_flags = SA_SIGINFO;
sigaction(SIGCHLD, &fs, NULL);
for (int i = 0;i < 10;i++) { //generate 10 children
pid_t pid = fork();
if (pid < 0)
perror("Forking has Failed!");
else if (pid == 0) { //child process
while (child_recieved_sig != 1); // wait for signal
printf("My ID is %d and I started my exam\n", getpid());
int time_interval = random_time();
printf("SleepTime: %d<-----------------------\n", time_interval);
sleep((unsigned int)time_interval);
// kill(getppid(), SIGUSR2);
printf("Student %d finished exam\n", getpid());
exit(0);
} else {
kill(pid, SIGUSR1);
}
}
while (parent_recieved_sig < 10) usleep(50000);
printf("parent_received_sig: %d\n", parent_recieved_sig);
printf("The Exam is Over ==> Results are:\n");
printf("Student ID=%d Finished First\n", pids[0]);
printf("Student ID=%d Finished Second\n", pids[1]);
printf("Student ID=%d Finished Third\n", pids[2]);
printf("Student ID=%d Finished Forth\n", pids[3]);
printf("Student ID=%d Finished Fifth\n", pids[4]);
printf("Student ID=%d Finished Sixth\n", pids[5]);
printf("Student ID=%d Finished Seventh\n", pids[6]);
printf("Student ID=%d Finished Eighth\n", pids[7]);
printf("Student ID=%d Finished Nineth\n", pids[8]);
printf("Student ID=%d Finished Tenth\n", pids[9]);
}
正如@ZanLynx 在 中观察到的那样,常规信号不会排队,因此如果您依赖于从 children 接收信号来识别它们已经完成,那么您就有丢失信号的风险靠得很近。
此外,由于您是从 10 个不同进程的 21 种可能延迟中进行选择,因此您面临信号靠在一起到达的风险相当高,因此实际上很可能存在具有完全相同延迟的对(尽管这本身并不能保证信号丢失)。您可以通过使用更精细的延迟粒度来降低这种可能性,您可以使用 nanosleep()
而不是 sleep()
来实现。这不能确保您不会错过信号,但会降低这种可能性。
然而,更好的方法是使用更可靠的机制来识别 children 何时结束。正如评论中所建议的,最自然的替代方法是 wait()
函数。
我正在尝试通过以下方式设计和实施模拟考试 POSIX 进程和进程间通信技术。模拟 工作方式如下:
名为 Teacher 的进程将从 从头到尾。
教师进程使用 fork/exec 系统调用 generate/load 10 个学生 进程。
每个学生进程:
一个。等待老师的信号开始考试。
b。一旦学生收到信号,它将开始考试。
c。学生进程将调用睡眠系统 API 随机睡眠 在 [10 – 30] 秒之间选择的数字,以模拟 不同进程完成考试的时间不同
d。一旦该进程的睡眠结束,它将触发一个信号,表明它 已完成考试
教师进程将继续跟踪学生进程的顺序 已经完成了。
所有Student进程结束后,Teacher进程会宣布 考试成绩如下,终止。
考试结束 -> 结果是:
ID=4128的同学第一名
ID=4122 学生第二名
ID=4126 的学生获得第三名 ...
我在 c:
中制作了这段代码#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <sys/wait.h>
#include<signal.h>
static int child_recieved_sig = 0;
static int parent_recieved_sig = 0;
static int pids[10];
static int pidIndex = 0;
int random_time() {
// generate random time from 10-30 sec
srand(time(NULL) + getpid());
int rand_time = (rand() % 21) + 10; //form [0-20]+10
return rand_time;
}
//signal sent from the parent to the child to start the exam
void start_signal(int sig) {
if (sig == SIGUSR1) {
child_recieved_sig = 1;
}
}
//signal sent from the child to the parent indicating that the exam is over
void finish_signal(int sig, siginfo_t* info, void* context) {
if (sig == SIGUSR2) {
parent_recieved_sig++;
pids[pidIndex] = info->si_pid;
pidIndex++;
}
}
int main() {
struct sigaction ss = { 0 }; // fs = start signal
ss.sa_sigaction = start_signal;
ss.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &ss, NULL);
struct sigaction fs = { 0 }; // fs = finish signal
fs.sa_sigaction = finish_signal;
fs.sa_flags = SA_SIGINFO;
sigaction(SIGUSR2, &fs, NULL);
for (int i = 0;i < 10;i++) { //generate 10 children
pid_t pid = fork();
if (pid < 0)
perror("Forking has Failed!");
else if (pid == 0) { //child process
while (child_recieved_sig != 1); // wait for signal
printf("My ID is %d and I started my exam\n", getpid());
int time_interval = random_time();
printf("SleepTime: %d<-----------------------\n", time_interval);
sleep(time_interval);
kill(getppid(), SIGUSR2);
printf("Student %d finished exam\n", getpid());
exit(0);
} else {
kill(pid, SIGUSR1);
}
}
while (parent_recieved_sig < 10) usleep(50000);
printf("parent_received_sig: %d\n", parent_recieved_sig);
printf("The Exam is Over ==> Results are:\n");
printf("Student ID=%d Finished First\n", pids[0]);
printf("Student ID=%d Finished Second\n", pids[1]);
printf("Student ID=%d Finished Third\n", pids[2]);
printf("Student ID=%d Finished Forth\n", pids[3]);
printf("Student ID=%d Finished Fifth\n", pids[4]);
printf("Student ID=%d Finished Sixth\n", pids[5]);
printf("Student ID=%d Finished Seventh\n", pids[6]);
printf("Student ID=%d Finished Eighth\n", pids[7]);
printf("Student ID=%d Finished Nineth\n", pids[8]);
printf("Student ID=%d Finished Tenth\n", pids[9]);
}
问题是有时代码可以正常工作并按预期完成工作,而其他时候没有对代码进行任何修改它就会卡在 while look (while (parent_recieved_sig < 10) usleep(50000);
) 中,我一直在努力弄清楚原因,但我不能得出结论,我试过打印 parent_recieved_sig
的值,有时它没有达到 10,意味着某些信号没有被处理?
只有实时信号排队。其他的可以合并。有可能发送了两个而只收到了一个。
如另一个答案中所述,非实时信号不会排队,因此如果它们同时发送,您可能会错过其中一些信号(来自OS 观点).
下面是您的示例,为了用 SIGCHLD
的处理替换 SIGUSR2
的处理,稍作修改。
当子进程终止时,此信号会自动发送到其父进程。
有趣的部分是信号处理程序,只要成功,它就会以非阻塞方式 (WNOHANG
) 为任何子进程 (-1
) 在 waitpid()
上循环。
处理一个 SIGCHLD
可能导致检测到多个子进程同时终止。
EDIT1 如评论中所建议,更直接的解决方案是摆脱 SIGCHLD
的处理并修改 [=19 中的等待循环=] 像这样的功能
while (parent_recieved_sig < 10) {
pid_t r=wait(NULL);
if(r>0) {
parent_recieved_sig++;
pids[pidIndex] = r;
pidIndex++;
} else {
perror("wait()");
exit(1);
}
}
EDIT2 正如评论中所建议的,全局 int
s 应该是 volatile sig_atomic_t
s 以便被检测到(优化器不认为未更改) 在被信号处理程序修改时由主线程执行。
/**
gcc -std=c99 -o prog_c prog_c.c \
-pedantic -Wall -Wextra -Wconversion \
-Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#undef __STRICT_ANSI__
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
// ensure modifications in the signal handlers are detected
static volatile sig_atomic_t child_recieved_sig = 0;
static volatile sig_atomic_t parent_recieved_sig = 0;
static volatile sig_atomic_t pids[10];
static volatile sig_atomic_t pidIndex = 0;
int random_time(void) {
// generate random time from 10-30 sec
srand((unsigned int)time(NULL) + (unsigned int)getpid());
int rand_time = (rand() % 5) + 2; //form [0-20]+10
return rand_time;
}
//signal sent from the parent to the child to start the exam
void start_signal(int sig, siginfo_t* info, void* context) {
(void)info;
(void)context;
if (sig == SIGUSR1) {
child_recieved_sig = 1;
}
}
//signal sent from the child to the parent indicating that the exam is over
void finish_signal(int sig, siginfo_t* info, void* context) {
(void)info;
(void)context;
if (sig == SIGCHLD) {
for(;;) {
pid_t r=waitpid(-1, NULL, WNOHANG);
if(r<=0) break;
parent_recieved_sig++;
pids[pidIndex] = r;
pidIndex++;
}
}
}
int main(void) {
struct sigaction ss = { 0 }; // fs = start signal
ss.sa_sigaction = start_signal;
ss.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &ss, NULL);
struct sigaction fs = { 0 }; // fs = finish signal
fs.sa_sigaction = finish_signal;
fs.sa_flags = SA_SIGINFO;
sigaction(SIGCHLD, &fs, NULL);
for (int i = 0;i < 10;i++) { //generate 10 children
pid_t pid = fork();
if (pid < 0)
perror("Forking has Failed!");
else if (pid == 0) { //child process
while (child_recieved_sig != 1); // wait for signal
printf("My ID is %d and I started my exam\n", getpid());
int time_interval = random_time();
printf("SleepTime: %d<-----------------------\n", time_interval);
sleep((unsigned int)time_interval);
// kill(getppid(), SIGUSR2);
printf("Student %d finished exam\n", getpid());
exit(0);
} else {
kill(pid, SIGUSR1);
}
}
while (parent_recieved_sig < 10) usleep(50000);
printf("parent_received_sig: %d\n", parent_recieved_sig);
printf("The Exam is Over ==> Results are:\n");
printf("Student ID=%d Finished First\n", pids[0]);
printf("Student ID=%d Finished Second\n", pids[1]);
printf("Student ID=%d Finished Third\n", pids[2]);
printf("Student ID=%d Finished Forth\n", pids[3]);
printf("Student ID=%d Finished Fifth\n", pids[4]);
printf("Student ID=%d Finished Sixth\n", pids[5]);
printf("Student ID=%d Finished Seventh\n", pids[6]);
printf("Student ID=%d Finished Eighth\n", pids[7]);
printf("Student ID=%d Finished Nineth\n", pids[8]);
printf("Student ID=%d Finished Tenth\n", pids[9]);
}
正如@ZanLynx 在
此外,由于您是从 10 个不同进程的 21 种可能延迟中进行选择,因此您面临信号靠在一起到达的风险相当高,因此实际上很可能存在具有完全相同延迟的对(尽管这本身并不能保证信号丢失)。您可以通过使用更精细的延迟粒度来降低这种可能性,您可以使用 nanosleep()
而不是 sleep()
来实现。这不能确保您不会错过信号,但会降低这种可能性。
然而,更好的方法是使用更可靠的机制来识别 children 何时结束。正如评论中所建议的,最自然的替代方法是 wait()
函数。