我当前的程序正在创建子进程并让它们工作(CPU密集型工作)。 main()位于那里并等待子进程通过管道发送数据(使用select)。
我想要做的是当程序处理数据时我可以按CTRL + C来停止子进程工作并询问用户是否要退出或恢复工作。
如果用户想要退出,该程序将终止所有进程。如果用户想要恢复工作,它会告诉子进程恢复计算。
我已经有了代码,但它的工作效果不太好。
在main中我有signal(SIGINT, pausar);
来处理SIGINT(CTRL + C)。
这是pausar()函数:
void pausar(int signum){
signal(SIGINT, pausar);
int i;
// pid[] contains all the child processes
for(i = 0; i<CORES; i++)
{
kill(pid[i], SIGSTOP);
}
char option[2];
printf("\n Computacao pausada.\n'S' para sair ou 'C' para continuar: ");
scanf("%1s", option);
if (option[0] == 's' || option[0] == 'S') {
printf("A desligar...\n");
//if user wants to quit, kill all the child processes
for(i = 0; i<CORES; i++)
{
kill(pid[i], SIGKILL);
}
exit(0);
}
else
{
printf("[%d] A resumir computacao...\n",getpid());
kill(getpid(), SIGCONT);
//if user wants to resume work, send signal to continue
for(i = 0; i<CORES; i++)
{
kill(pid[i], SIGCONT);
printf("%d resumiu\n", pid[i]);
}
}
}
问题在于,有时我按下CTRL + C并且控制台中没有显示任何内容(但是进程因为我正在关注进程管理器而停止)。另一个问题是,在我输入'C'以恢复工作后,我在select()中出错,孩子们再也没有恢复工作。
答案 0 :(得分:4)
同时使用select()
和信号处理程序容易出现竞争条件 - 在select()
调用期间可能会发生信号,但在其他每行代码中也会发生。
如果您使用的是linux:使用signalfd()
创建一个事件套接字,并将此套接字添加到传递给select()
的读取集中。然后在代码中的固定点处理信号,您无需担心竞争条件。
答案 1 :(得分:3)
首先,对于您尝试做的事情,您的信号处理程序过于复杂。其次,在信号处理程序中调用signal()
不是一个好主意......它不是asynchronous signal-safe function。
您可以做的是:
signal()
设置信号处理函数,就像您已经完成的那样。sigprocmask()
阻止SIGINT信号。这样可以防止在调用pselect()
之前到达虚假信号。sig_atomic_t
pselect()
代替select()
。这将允许您更改过程信号掩码以允许SIGINT信号到达,并且它将以原子方式对信号进行操作。否则,你可以在调用select()
之前让你的SIGINT到达,然后你已经“丢失”了那个信号,即使它确实在处理程序中设置了标志。pselect()
调用返回时,检测是否已设置标志。sig_atomic_t
标志,并且由于捕获信号而从pselect
返回,则启动另一个实际执行所有子进程结束并提示用户的函数等等。由于信号到达的异步性,执行这些步骤将简化您的信号处理代码并减少竞争条件或其他意外结果的可能性。
如果您想了解有关pselect()
的更多信息,那么您需要nice article on that here。