我必须解决两个进程之间的同步问题,我必须用信号来解决。
两个进程应该执行一定量的工作,然后发出另一个进程的信号。这将无限期地发生。
我的代码会发生什么:
我向父进程发送一个信号,该进程向子进程发送一个信号,该进程再次向父进程发送一个信号,该进程被停止并且不再执行。
理想情况下,父母和孩子应该无限期地互相发出信号。
int main () {
parent_pid = getpid();
pid_t pid = fork();
if (pid < 0) {
syserr_quit("fork error");
} else if (pid == 0) {
signal(SIGUSR2, child_work);
while (1) {}
} else {
child_pid = pid;
signal(SIGUSR1, parent_work);
while (1) {}
}
}
void child_work (int signo) {
sleep(1); // fake child work
if (kill(parent_pid, SIGUSR1) < 0) syserr_quit("kill error");
// wait for parent signal
pause();
}
void parent_work (int signo) {
sleep(1); // fake parent work
if (kill(child_pid, SIGUSR2) < 0) syserr_quit("kill error");
// wait for child signal
pause();
}
无法弄清楚出了什么问题,因为它只是一小段代码而且似乎一切都在我身上。
如果有人想在他们的机器上将其旋转,这里的a full demo可以在复制/粘贴后运行。
根据一些评论,我使用sigaction
来避免竞争条件,并在演示中停止使用sleep
。
Docs声明,为了避免在执行处理程序期间阻止信号接收,必须设置SA_NODEFER
标志。
代码仍然存在故障,只有这一次是停止接收信号的孩子。
代码段与上面相同,但signal
被替换除外 - 两次 - 与
struct sigaction action;
action.sa_handler = parent/child_work;
action.sa_flags = SA_NODEFER;
sigaction(signo, &action, NULL);
完整代码
要启动信令,请从控制台检查并发送信号(即kill -SIGUSR1 <parentid>
)
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void parent_work(int signo);
void child_work(int signo);
void syserr_quit(char *msg);
pid_t parent_pid;
pid_t child_pid;
int main () {
parent_pid = getpid();
printf("parent pid: %d\n", parent_pid);
pid_t pid = fork();
if (pid < 0) {
syserr_quit("fork error");
} else if (pid == 0) {
struct sigaction action;
action.sa_handler = child_work;
action.sa_flags = SA_NODEFER;
sigaction(SIGUSR2, &action, NULL);
while (1) {}
} else {
child_pid = pid;
printf("child pid: %d\n", pid);
struct sigaction action;
action.sa_handler = parent_work;
action.sa_flags = SA_NODEFER;
sigaction(SIGUSR1, &action, NULL);
while (1) {}
}
}
void child_work (int signo) {
for (int i = 0; i < 100000000; ++i); // fake child work
// signal parent that a write is now possible
printf("send signal to %d\n", parent_pid); fflush(stdout);
if (kill(parent_pid, SIGUSR1) < 0) syserr_quit("kill error");
printf("signal sent\n"); fflush(stdout);
// wait for parent signal
pause();
}
void parent_work (int signo) {
for (int i = 0; i < 100000000; ++i); // fake child work
// signal child that a read is possible
printf("send signal to %d\n", child_pid); fflush(stdout);
if (kill(child_pid, SIGUSR2) < 0) syserr_quit("kill error");
printf("signal sent\n"); fflush(stdout);
// wait for child signal
pause();
}
void syserr_quit(char *msg) {
perror(msg);
exit(EXIT_FAILURE);
}
好吧,伙计们看来更新的代码不起作用只是因为我没有正确初始化sigaction
掩码。必须使用sigemptyset
完成,您可以找出原因right here。
the answer中的更多详情。
答案 0 :(得分:0)
当您调用SIGUSR1
信号处理程序parent_work
时,SIGUSR1
信号将映射到SIG_DFL
(默认信号处理)或忽略父进程(参见。 signal
):
当一个信号出现,并且func指向一个函数时,它是实现定义的是否相当于:
signal(sig, SIG_DFL);
被执行或者实现阻止一些实现定义的信号集(至少包括sig)发生,直到当前信号处理完成。
当子进程再次为父进程生成SIGUSR1
信号时,由于该原因,它不会再次调用parent_work
信号处理程序。
因此,在信号处理程序中,如果您希望后续信号也由信号处理程序处理,则需要再次重新注册信号:
void parent_work(int signo) {
/* your handler code */
signal(signo, parent_work);
}
请注意,这仍然为比赛条件留出空间 - 即。如果第二个SIGUSR1
在信号处理程序重新注册之前到达。但实际上,这是你设计所固有的。
当然有更好的选择和一般建议。其中一些已在评论中提及过,所以如果您有兴趣我会参考这些。
答案 1 :(得分:0)
似乎更新的代码不起作用只是因为我没有正确初始化sigaction
掩码。必须使用sigemptyset
完成,您可以找出原因right here。
为执行处理程序时希望能够捕获的信号正确初始化sigaction
:
struct sigaction sa;
sigemptyset(&sa.sa_mask);
action.sa = handler;
action.sa = SA_NODEFER;
sigaction(signo, &sa, NULL);
最终代码
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void parent_work(int signo);
void child_work(int signo);
void syserr_quit(char *msg);
pid_t parent_pid;
pid_t child_pid;
int main () {
parent_pid = getpid();
printf("parent pid: %d\n", parent_pid);
pid_t pid = fork();
if (pid < 0) {
syserr_quit("fork error");
} else if (pid == 0) {
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = child_work;
sa.sa_flags = SA_NODEFER;
sigaction(SIGUSR2, &sa, NULL);
while (1) {}
} else {
child_pid = pid;
printf("child pid: %d\n", pid);
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = parent_work;
sa.sa_flags = SA_NODEFER;
sigaction(SIGUSR1, &sa, NULL);
while (1) {}
}
}
void child_work (int signo) {
for (int i = 0; i < 100000000; ++i); // fake child work
// signal parent that a write is now possible
printf("send signal to %d\n", parent_pid); fflush(stdout);
if (kill(parent_pid, SIGUSR1) < 0) syserr_quit("kill error");
printf("signal sent\n"); fflush(stdout);
// wait for parent signal
pause();
}
void parent_work (int signo) {
for (int i = 0; i < 100000000; ++i); // fake child work
// signal child that a read is possible
printf("send signal to %d\n", child_pid); fflush(stdout);
if (kill(child_pid, SIGUSR2) < 0) syserr_quit("kill error");
printf("signal sent\n"); fflush(stdout);
// wait for child signal
pause();
}
void syserr_quit(char *msg) {
perror(msg);
exit(EXIT_FAILURE);
}