如果child没有状态变化,如何取消waitpid?
How to cancel waitpid if child has no status change?
免责声明:C 的绝对新手,我以前主要使用 Java。
在许多 C 初学者教程中,waitpid
在进程管理示例中用于等待其 child 进程完成(或使用 WUNTRACED
等选项更改状态)。但是,如果没有通过直接用户输入或程序化(例如超时)发生此类状态更改,我找不到有关如何继续的任何信息。那么撤消waitpid
有什么好方法呢?类似于 SIGCONT
表示停止的进程,而不是延迟 waitpid
的进程。
或者,如果这个想法没有意义,那么知道为什么会很有趣。
waitpid
的第三个参数采用一组标志。您想要包含 WNOHANG
标志,如果没有 child 进程退出,它会立即通知 waitpid
到 return。
添加此选项后,您将在循环中休眠一段时间,如果没有退出,则重试。重复直到 child 已 returned 或直到您的超时已过。
如果我建议使用 alarm()
怎么样? alarm()
在倒计时结束后交付 SIGALRM
(有关详细信息,请参阅 alarm()
手册页)。但是从 signals
手册页来看,SIGALRM
默认处理是终止进程。因此,您需要注册一个信号处理程序来处理 SIGALRM
。代码如下...
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void sigalrm(int signo)
{
return; // Do nothing !
}
int main()
{
struct sigaction act, oldact;
act.sa_handler = sigalrm; // Set the signal handler
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_INTERRUPT // If interrupt defined set it to prevent the auto restart of sys-call
act.sa_flags |= SA_INTERRUPT;
#endif
sigaction(SIGALRM, &act, &oldact);
pid_t fk_return = fork();
if (fk_return == 0) { // Child never returns
for( ; ; );
}
unsigned int wait_sec = 5;
alarm(wait_sec); // Request for SIGALRM
time_t start = time(NULL);
waitpid(-1, NULL, 0);
int tmp_errno = errno; // save the errno state, it may be modified in between function calls.
time_t end = time(NULL);
alarm(0); // Clear a pending alarm
sigaction(SIGALRM, &oldact, NULL);
if (tmp_errno == EINTR) {
printf("Child Timeout, waited for %d sec\n", end - start);
kill(fk_return, SIGINT);
exit(1);
}
else if (tmp_errno != 0) // Some other fatal error
exit(1);
/* Proceed further */
return 0;
}
输出
Child Timeout, waited for 5 sec
注意:你不需要担心SIGCHLD
,因为它的默认配置是忽略。
编辑
为了完整起见,保证SIGALRM
没有投递到child。这是来自 alarm()
的手册页
Alarms created by alarm() are preserved across execve(2) and are not inherited by children created via fork(2).
编辑 2
我不知道为什么它一开始没有打动我。一种简单的方法是阻止 SIGCHLD
并调用支持超时选项的 sigtimedwait()
。代码是这样的...
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
sigset_t sigmask;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &sigmask, NULL);
pid_t fk_return = fork();
if (fk_return == 0) { // Child never returns
for( ; ; );
}
if (sigtimedwait(&sigmask, NULL, &((struct timespec){5, 0})) < 0) {
if (errno == EAGAIN) {
printf("Timeout\n");
kill(fk_return, SIGINT);
exit(1);
}
}
waitpid(fk_return, NULL, 0); // Child should have terminated by now.
/* Proceed further */
return 0;
}
输出
Timeout
在典型的 Unix 系统上等待进程死亡是绝对的 PITA。可移植的方法是使用各种信号来中断 wait
函数:SIGALARM
用于超时,SIGTERM
/SIGINT
和其他用于 "user input" 事件。这依赖于全局状态,因此可能无法做到。
不可移植的方法是在 BSD 上使用 pidfd_open
with poll
/epoll
on Linux, kqueue
和 EVFILT_PROC
过滤器。
请注意,在 Linux 这允许等待进程终止,您仍然必须通过 waitid
和 P_PIDFD
.
检索状态
如果您仍想混入 "user events",请将 signalfd
添加到 Linux 上的描述符列表或 BSD 上 kqueue
的 EVFILT_SIGNAL
过滤器.
另一种可能的解决方案是生成一个 "process reaper" 线程,该线程负责收集所有进程并在您选择的进程对象中设置一些事件:futex
word, eventfd
etc. Waiting on such objects can be done with a timeout. This requires everyone to agree to use the same interface for process spawning which might or might not be reasonable. Afaik Java implementations use this strategy.
免责声明:C 的绝对新手,我以前主要使用 Java。
在许多 C 初学者教程中,waitpid
在进程管理示例中用于等待其 child 进程完成(或使用 WUNTRACED
等选项更改状态)。但是,如果没有通过直接用户输入或程序化(例如超时)发生此类状态更改,我找不到有关如何继续的任何信息。那么撤消waitpid
有什么好方法呢?类似于 SIGCONT
表示停止的进程,而不是延迟 waitpid
的进程。
或者,如果这个想法没有意义,那么知道为什么会很有趣。
waitpid
的第三个参数采用一组标志。您想要包含 WNOHANG
标志,如果没有 child 进程退出,它会立即通知 waitpid
到 return。
添加此选项后,您将在循环中休眠一段时间,如果没有退出,则重试。重复直到 child 已 returned 或直到您的超时已过。
如果我建议使用 alarm()
怎么样? alarm()
在倒计时结束后交付 SIGALRM
(有关详细信息,请参阅 alarm()
手册页)。但是从 signals
手册页来看,SIGALRM
默认处理是终止进程。因此,您需要注册一个信号处理程序来处理 SIGALRM
。代码如下...
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void sigalrm(int signo)
{
return; // Do nothing !
}
int main()
{
struct sigaction act, oldact;
act.sa_handler = sigalrm; // Set the signal handler
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_INTERRUPT // If interrupt defined set it to prevent the auto restart of sys-call
act.sa_flags |= SA_INTERRUPT;
#endif
sigaction(SIGALRM, &act, &oldact);
pid_t fk_return = fork();
if (fk_return == 0) { // Child never returns
for( ; ; );
}
unsigned int wait_sec = 5;
alarm(wait_sec); // Request for SIGALRM
time_t start = time(NULL);
waitpid(-1, NULL, 0);
int tmp_errno = errno; // save the errno state, it may be modified in between function calls.
time_t end = time(NULL);
alarm(0); // Clear a pending alarm
sigaction(SIGALRM, &oldact, NULL);
if (tmp_errno == EINTR) {
printf("Child Timeout, waited for %d sec\n", end - start);
kill(fk_return, SIGINT);
exit(1);
}
else if (tmp_errno != 0) // Some other fatal error
exit(1);
/* Proceed further */
return 0;
}
输出
Child Timeout, waited for 5 sec
注意:你不需要担心SIGCHLD
,因为它的默认配置是忽略。
编辑
为了完整起见,保证SIGALRM
没有投递到child。这是来自 alarm()
Alarms created by alarm() are preserved across execve(2) and are not inherited by children created via fork(2).
编辑 2
我不知道为什么它一开始没有打动我。一种简单的方法是阻止 SIGCHLD
并调用支持超时选项的 sigtimedwait()
。代码是这样的...
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
sigset_t sigmask;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &sigmask, NULL);
pid_t fk_return = fork();
if (fk_return == 0) { // Child never returns
for( ; ; );
}
if (sigtimedwait(&sigmask, NULL, &((struct timespec){5, 0})) < 0) {
if (errno == EAGAIN) {
printf("Timeout\n");
kill(fk_return, SIGINT);
exit(1);
}
}
waitpid(fk_return, NULL, 0); // Child should have terminated by now.
/* Proceed further */
return 0;
}
输出
Timeout
在典型的 Unix 系统上等待进程死亡是绝对的 PITA。可移植的方法是使用各种信号来中断 wait
函数:SIGALARM
用于超时,SIGTERM
/SIGINT
和其他用于 "user input" 事件。这依赖于全局状态,因此可能无法做到。
不可移植的方法是在 BSD 上使用 pidfd_open
with poll
/epoll
on Linux, kqueue
和 EVFILT_PROC
过滤器。
请注意,在 Linux 这允许等待进程终止,您仍然必须通过 waitid
和 P_PIDFD
.
如果您仍想混入 "user events",请将 signalfd
添加到 Linux 上的描述符列表或 BSD 上 kqueue
的 EVFILT_SIGNAL
过滤器.
另一种可能的解决方案是生成一个 "process reaper" 线程,该线程负责收集所有进程并在您选择的进程对象中设置一些事件:futex
word, eventfd
etc. Waiting on such objects can be done with a timeout. This requires everyone to agree to use the same interface for process spawning which might or might not be reasonable. Afaik Java implementations use this strategy.