我用叉子叉了一个孩子,我试着让他们同步,这样他们就能打印
child 0
parent 0
child 1
parent 1
不过,我必须使用
sigsuspend
,这是我目前的代码,我只得到parent suspend
。没有孩子的踪迹。int c=0, receivedP=0, receivedC=0;
sigset_t setParent, setChild;
void handler(int s){
if(s==SIGUSR1){
receivedC=1;
printf("parent --sig1--> child\n");
c++;
}
else{
receivedP=1;
printf("child --sig2--> parent\n");
}
}
void child(){
sigfillset(&setChild);
sigdelset(&setChild,SIGUSR1);
sigdelset(&setChild,SIGINT); //this makes me able to terminate the program at any time
while(1){
if(receivedC==0){
printf("child suspend\n");
sigsuspend(&setChild);
}
receivedC=0;
printf("child %d\n",c);
kill(getppid(),SIGUSR2);
}
}
void parent(pid_t pf){
sigfillset(&setParent);
sigdelset(&setParent,SIGUSR2);
sigdelset(&setParent,SIGINT); //this makes me able to terminate the program at any time
kill(pf,SIGUSR1);
while(1){
if(receivedP==0){
printf("parent suspend\n");
sigsuspend(&setParent);
}
receivedP=0;
printf("parent %d\n",c);
kill(pf,SIGUSR1);
}
}
int main(){
signal(SIGUSR1,handler);
signal(SIGUSR2,handler);
pid_t p;
p= fork();
if(!p)child();
else parent(p);
return 0;
}
有人知道是什么原因吗?
最佳答案
我认为你是违反了信号的一个典型问题。
while(1){
if(receivedP==0){
printf("parent suspend\n");
sigsuspend(&setParent);
}
receivedP=0;
printf("parent %d\n",c);
kill(pf,SIGUSR1);
}
想象一下,如果来自孩子的信号在
if(receivedP==0)
和sigsuspend(&setParent)
的指令之间到达,会发生什么。处理程序将执行,并将receivedP
设置为1,但主循环不会再次检查它;它将进入sigsuspend
并且永远不会出来。为了安全地使用
sigsuspend
,您需要在程序不调用sigsuspend
时始终阻止您关心的信号。你可以用sigprocmask
来完成。还必须确保信号在处理程序执行期间被阻塞,这要求您使用sigaction
而不是signal
(但无论如何,您都应该这样做,因为signal
严重未指定,系统到系统的变化将咬到您的屁股)。一旦您确保信号只能在
sigsuspend
期间传递,就不再需要receivedP
和receivedC
变量;您知道信号已经发生,或者sigsuspend
不会返回。(如果您的程序在每个进程中等待的信号不止一个,那么这是不正确的,但在这一点上,事情会变得更加复杂;在它出现之前不要担心它。)事实上,一旦确定了这一点,就不需要在信号处理程序中做任何事情。计数器变量可以是
parent
和child
的局部变量。在一个信号处理程序中尽可能少的操作总是最好的;C标准的字母允许您几乎什么都不做,而不会冒未定义行为的风险,POSIX只会将其打开一点。(练习:将此程序更改为使用sigwaitinfo
以便它根本不需要处理程序函数。)你程序的修改对我来说是可靠的。我还纠正了一些其他样式问题和小错误:注意
parent
和child
中的循环以不同的顺序执行操作,注意main
中的错误检查,注意我只阻塞了SIGUSR1
和SIGUSR2
,因为应该允许其他几个信号终止进程(SIGTERM
,SIGHUP
,SIGQUIT
,SIGSEGV
,,…),而您不希望维护一个列表。足以阻止程序已为其安装处理程序的信号。#include <signal.h>
#include <stdio.h>
#include <unistd.h>
static void handler(int unused)
{
}
static void child(sigset_t *ss)
{
unsigned int c = 0;
pid_t parent_pid = getppid();
sigdelset(ss, SIGUSR1);
for (;;) {
sigsuspend(ss);
printf("child %u\n", c++);
kill(parent_pid, SIGUSR2);
}
}
static void parent(sigset_t *ss, pid_t child_pid)
{
unsigned int c = 0;
sigdelset(ss, SIGUSR2);
for (;;) {
printf("parent %u\n", c++);
kill(child_pid, SIGUSR1);
sigsuspend(ss);
}
}
int main(void)
{
// Ensure line-buffered stdout.
if (setvbuf(stdout, 0, _IOLBF, 0)) {
perror("setvbuf");
return 1;
}
// This signal mask is in effect at all times _except_ when sleeping
// in sigsuspend(). Note that _only_ the signals used for IPC are
// blocked. After forking, each process will modify it appropriately
// for its own use of sigsuspend(); this does not affect the kernel-side
// copy made by sigprocmask().
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGUSR1);
sigaddset(&ss, SIGUSR2);
if (sigprocmask(SIG_BLOCK, &ss, 0)) {
perror("sigprocmask");
return 1;
}
// Always use sigaction(), not signal(); signal() is underspecified.
// The mask here is the signal mask to use _while the handler is
// executing_; it should also block both IPC signals.
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_mask = ss;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &sa, 0) || sigaction(SIGUSR2, &sa, 0)) {
perror("sigaction");
return 1;
}
pid_t child_pid = fork();
if (child_pid < 0) {
perror("fork");
return 1;
}
if (child_pid == 0)
child(&ss);
else
parent(&ss, child_pid);
// we never get here but the compiler might not know that
return 0;
}
我建议您一直阅读GNU C Library手册中的section on signal handling;它包含了一些关于安全使用信号的其他有用建议。