[英]How to cancel waitpid if child has no status change?
Disclaimer: Absolute newbie in C, i was mostly using Java before.免责声明:C 中的绝对新手,我以前主要使用 Java。
In many C beginner tutorials, waitpid
is used in process management examples to wait for its child processes to finish (or have a status change using options like WUNTRACED
).在许多 C 初学者教程中,在进程管理示例中使用
waitpid
来等待其子进程完成(或使用WUNTRACED
等选项进行状态更改)。 However, i couldn't find any information about how to continue if no such status change occurs, either by direct user input or programmatic (eg timeout).但是,如果没有发生此类状态更改,无论是通过直接用户输入还是编程(例如超时),我都找不到有关如何继续的任何信息。 So what is a good way to undo
waitpid
?那么撤消
waitpid
的好方法是什么? Something like SIGCONT
for stopped processes, but instead for processes delayed by waitpid
.像
SIGCONT
之类的东西用于停止的进程,而是用于被waitpid
延迟的进程。
Alternatively if the idea makes no sense, it would be interesting to know why.或者,如果这个想法没有意义,知道为什么会很有趣。
How about if I suggest using alarm()
?如果我建议使用
alarm()
怎么样? alarm()
delivers SIGALRM
after the countdown passes (See alarm()
man page for more details).倒计时结束后,
alarm()
传递SIGALRM
(有关详细信息,请参见alarm()
手册页)。 But from the signals
man page, SIGALRM
default disposition is to terminate the process.但从
signals
手册页来看, SIGALRM
默认处置是终止进程。 So, you need to register a signal handler for handling the SIGALRM
.因此,您需要注册一个信号处理程序来处理
SIGALRM
。 Code follows like this...代码如下...
#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;
}
OUTPUT OUTPUT
Child Timeout, waited for 5 sec
Note: You don't need to worry about SIGCHLD
because its default disposition is to ignore.注意:您不必担心
SIGCHLD
,因为它的默认处置是忽略。
For the completeness, it is guaranteed that SIGALRM
is not delivered to the child.为了完整起见,保证不会将
SIGALRM
传递给孩子。 This is from the man page of alarm()
这是来自
alarm()
的手册页
Alarms created by alarm() are preserved across execve(2) and are not inherited by children created via fork(2).
由 alarm() 创建的警报在 execve(2) 中保留,不会被通过 fork(2) 创建的子级继承。
I don't know why it didn't strike me at first.我不知道为什么一开始它没有打动我。 A simple approach would be to block
SIGCHLD
and call sigtimedwait()
which supports timeout option.一个简单的方法是阻止
SIGCHLD
并调用支持超时选项的sigtimedwait()
。 The code goes like this...代码是这样的......
#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;
}
OUTPUT OUTPUT
Timeout
The third argument to waitpid
takes a set of flags. waitpid
的第三个参数采用一组标志。 You want to include the WNOHANG
flag, which tells waitpid
to return immediately if no child process has exited.您想包含
WNOHANG
标志,它告诉waitpid
如果没有子进程退出则立即返回。
After adding this option, you would sit in a loop a sleep for some period of time and try again if nothing has exited.添加此选项后,您将在循环中休眠一段时间,如果没有任何内容退出,请重试。 Repeat until either a child has returned or until your timeout has passed.
重复直到孩子返回或直到您的超时时间过去。
Waiting for process to die on a typical Unix system is an absolute PITA.在典型的 Unix 系统上等待进程终止是绝对的 PITA。 The portable way would be to use various signals to interrupt
wait
function: SIGALARM
for timeout, SIGTERM
/ SIGINT
and others for "user input" event.可移植的方法是使用各种信号来中断
wait
function: SIGALARM
用于超时, SIGTERM
/ SIGINT
和其他用于“用户输入”事件。 This relies on a global state and thus might be impossible to do.这依赖于全局 state,因此可能无法做到。
The non-portable way would be to use pidfd_open
with poll
/ epoll
on Linux, kqueue
with a EVFILT_PROC
filter on BSDs.不可移植的方法是在 Linux 上使用
pidfd_open
和poll
/ epoll
,在 BSD 上使用带有EVFILT_PROC
过滤器的kqueue
。
Note that on Linux this allows waiting for a process to terminate, you will still have to retrieve status via waitid
with P_PIDFD
.请注意,在 Linux 上,这允许等待进程终止,您仍然必须通过带有
waitid
的P_PIDFD
检索状态。
If you still want to mix in "user events", add signalfd
to the list of descriptors on Linux or EVFILT_SIGNAL
filter of kqueue
on BSDs.如果您仍想混入“用户事件”,请将
signalfd
添加到 Linux 上的描述符列表或 BSD 上kqueue
的EVFILT_SIGNAL
过滤器列表中。
Another possible solution is to spawn a "process reaper" thread which is responsible for reaping of all processes and setting some event in a process object of your choice: futex
word, eventfd
etc. Waiting on such objects can be done with a timeout.另一种可能的解决方案是产生一个“进程收割机”线程,该线程负责收割所有进程并在您选择的进程 object 中设置一些事件:
futex
word、 eventfd
等。等待此类对象可以通过超时来完成。 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 .
Afaik Java 实现使用此策略。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.