C++ Waitpid 等同于超时?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/282176/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Waitpid equivalent with timeout?
提问by Greg Rogers
Imagine I have a process that starts several child processes. The parent needs to know when a child exits.
想象一下,我有一个启动多个子进程的进程。父母需要知道孩子何时退出。
I can use waitpid
, but then if/when the parent needs to exit I have no way of telling the thread that is blocked in waitpid
to exit gracefully and join it. It's nice to have things clean up themselves, but it may not be that big of a deal.
我可以使用waitpid
,但是如果/当父级需要退出时,我无法告诉被阻塞的线程waitpid
正常退出并加入它。让事情自行清理是件好事,但这可能没什么大不了的。
I can use waitpid
with WNOHANG
, and then sleep for some arbitrary time to prevent a busy wait. However then I can only know if a child has exited every so often. In my case it may not be super critical that I know when a child exits right away, but I'd like to know ASAP...
我可以使用waitpid
with WNOHANG
,然后睡眠任意时间以防止忙等待。但是,我只能知道孩子是否经常退出。在我的情况下,我知道孩子何时离开可能不是非常重要,但我想尽快知道......
I can use a signal handler for SIGCHLD
, and in the signal handler do whatever I was going to do when a child exits, or send a message to a different thread to do some action. But using a signal handler obfuscates the flow of the code a little bit.
我可以为 使用信号处理程序SIGCHLD
,并在信号处理程序中执行当孩子退出时我将要执行的任何操作,或者将消息发送到不同的线程以执行某些操作。但是使用信号处理程序会稍微混淆代码流。
What I'd really like to do is use waitpid
on some timeout, say 5 sec. Since exiting the process isn't a time critical operation, I can lazily signal the thread to exit, while still having it blocked in waitpid
the rest of the time, always ready to react. Is there such a call in linux? Of the alternatives, which one is best?
我真正想做的是waitpid
在一些超时时使用,比如 5 秒。由于退出进程不是一个时间关键的操作,我可以懒惰地向线程发出退出信号,同时在waitpid
其余时间仍然阻塞它,随时准备做出反应。linux中有这样的调用吗?在备选方案中,哪一个最好?
EDIT:
编辑:
Another method based on the replies would be to block SIGCHLD
in all threads with pthread
\ _sigmask()
. Then in one thread, keep calling sigtimedwait()
while looking for SIGCHLD
. This means that I can time out on that call and check whether the thread should exit, and if not, remain blocked waiting for the signal. Once a SIGCHLD
is delivered to this thread, we can react to it immediately, and in line of the wait thread, without using a signal handler.
另一种基于回复的方法是SIGCHLD
使用pthread
\阻止所有线程_sigmask()
。然后在一个线程中,sigtimedwait()
在寻找SIGCHLD
. 这意味着我可以在该调用超时并检查线程是否应该退出,如果不是,则保持阻塞等待信号。一旦 aSIGCHLD
被传递到这个线程,我们就可以立即对它做出反应,并且在等待线程的队列中,而无需使用信号处理程序。
采纳答案by Steve Baker
The function can be interrupted with a signal, so you could set a timer before calling waitpid() and it will exit with an EINTR when the timer signal is raised. Edit: It should be as simple as calling alarm(5) before calling waitpid().
该函数可以被信号中断,因此您可以在调用 waitpid() 之前设置一个计时器,并且在引发计时器信号时它将以 EINTR 退出。编辑:它应该像在调用 waitpid() 之前调用 alarm(5) 一样简单。
回答by geocar
Don't mix alarm()
with wait()
. You can lose error information that way.
不要alarm()
与wait()
. 您可能会以这种方式丢失错误信息。
Use the self-pipe trick. This turns any signal into a select()
able event:
使用自管道技巧。这会将任何信号变成一个select()
能够发生的事件:
int selfpipe[2];
void selfpipe_sigh(int n)
{
int save_errno = errno;
(void)write(selfpipe[1], "",1);
errno = save_errno;
}
void selfpipe_setup(void)
{
static struct sigaction act;
if (pipe(selfpipe) == -1) { abort(); }
fcntl(selfpipe[0],F_SETFL,fcntl(selfpipe[0],F_GETFL)|O_NONBLOCK);
fcntl(selfpipe[1],F_SETFL,fcntl(selfpipe[1],F_GETFL)|O_NONBLOCK);
memset(&act, 0, sizeof(act));
act.sa_handler = selfpipe_sigh;
sigaction(SIGCHLD, &act, NULL);
}
Then, your waitpid-like function looks like this:
然后,您的类似 waitpid 的函数如下所示:
int selfpipe_waitpid(void)
{
static char dummy[4096];
fd_set rfds;
struct timeval tv;
int died = 0, st;
tv.tv_sec = 5;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(selfpipe[0], &rfds);
if (select(selfpipe[0]+1, &rfds, NULL, NULL, &tv) > 0) {
while (read(selfpipe[0],dummy,sizeof(dummy)) > 0);
while (waitpid(-1, &st, WNOHANG) != -1) died++;
}
return died;
}
You can see in selfpipe_waitpid()
how you can control the timeout and even mix with other select()
-based IO.
您可以看到selfpipe_waitpid()
如何控制超时甚至与其他select()
基于 IO 的混合。
回答by mpartel
Fork an intermediate child, which forks the real child and a timeout process and waits for all (both) of its children. When one exits, it'll kill the other one and exit.
分叉一个中间孩子,它分叉真正的孩子和一个超时过程并等待它的所有(两个)孩子。当一个退出时,它会杀死另一个并退出。
pid_t intermediate_pid = fork();
if (intermediate_pid == 0) {
pid_t worker_pid = fork();
if (worker_pid == 0) {
do_work();
_exit(0);
}
pid_t timeout_pid = fork();
if (timeout_pid == 0) {
sleep(timeout_time);
_exit(0);
}
pid_t exited_pid = wait(NULL);
if (exited_pid == worker_pid) {
kill(timeout_pid, SIGKILL);
} else {
kill(worker_pid, SIGKILL); // Or something less violent if you prefer
}
wait(NULL); // Collect the other process
_exit(0); // Or some more informative status
}
waitpid(intermediate_pid, 0, 0);
Surprisingly simple :)
出乎意料的简单:)
You can even leave out the intermediate child if you're sure no other module in the program is spwaning child processes of its own.
如果您确定程序中没有其他模块正在生成自己的子进程,您甚至可以省略中间子进程。
回答by osexp2003
This is an interesting question. I found sigtimedwaitcan do it.
这是个有趣的问题。我发现sigtimedwait可以做到。
EDIT 2016/08/29: Thanks for Mark Edington's suggestion. I'v tested your example on Ubuntu 16.04, it works as expected.
编辑 2016/08/29:感谢 Mark Edington 的建议。我已经在 Ubuntu 16.04 上测试了你的例子,它按预期工作。
Note: this only works for child processes. It's a pity that seems no equivalent way of Window's WaitForSingleObject(unrelated_process_handle, timeout) in Linux/Unix to get notified of unrelated process's termination within timeout.
注意:这仅适用于子进程。遗憾的是,Linux/Unix 中的 Window 的 WaitForSingleObject(unrelated_process_handle, timeout) 似乎没有等效的方式来获得有关超时内无关进程终止的通知。
OK, Mark Edington's sample code is here:
好的,Mark Edington 的示例代码在这里:
/* The program creates a child process and waits for it to finish. If a timeout
* elapses the child is killed. Waiting is done using sigtimedwait(). Race
* condition is avoided by blocking the SIGCHLD signal before fork().
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
static pid_t fork_child (void)
{
int p = fork ();
if (p == -1) {
perror ("fork");
exit (1);
}
if (p == 0) {
puts ("child: sleeping...");
sleep (10);
puts ("child: exiting");
exit (0);
}
return p;
}
int main (int argc, char *argv[])
{
sigset_t mask;
sigset_t orig_mask;
struct timespec timeout;
pid_t pid;
sigemptyset (&mask);
sigaddset (&mask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
perror ("sigprocmask");
return 1;
}
pid = fork_child ();
timeout.tv_sec = 5;
timeout.tv_nsec = 0;
do {
if (sigtimedwait(&mask, NULL, &timeout) < 0) {
if (errno == EINTR) {
/* Interrupted by a signal other than SIGCHLD. */
continue;
}
else if (errno == EAGAIN) {
printf ("Timeout, killing child\n");
kill (pid, SIGKILL);
}
else {
perror ("sigtimedwait");
return 1;
}
}
break;
} while (1);
if (waitpid(pid, NULL, 0) < 0) {
perror ("waitpid");
return 1;
}
return 0;
}
回答by ony
I thought that select
will return EINTR
when SIGCHLD
signaled by on of the child.
I belive this should work:
我认为当孩子发出信号时select
会返回。我相信这应该有效:EINTR
SIGCHLD
while(1)
{
int retval = select(0, NULL, NULL, NULL, &tv, &mask);
if (retval == -1 && errno == EINTR) // some signal
{
pid_t pid = (waitpid(-1, &st, WNOHANG) == 0);
if (pid != 0) // some child signaled
}
else if (retval == 0)
{
// timeout
break;
}
else // error
}
Note: you can use pselect
to override current sigmask
and avoid interrupts from unneeded signals.
注意:您可以使用pselect
覆盖电流sigmask
并避免来自不需要的信号的中断。
回答by Aktau
Due to circumstances I absolutely needed this to run in the main thread and it was not very simple to use the self-pipe trick or eventfd because my epoll loop was running in another thread. So I came up with this by scrounging together other stack overflow handlers. Note that in general it's much safer to do this in other ways but this is simple. If anyone cares to comment about how it's really really bad then I'm all ears.
由于某些情况,我绝对需要它在主线程中运行,并且使用自管道技巧或 eventfd 并不是很简单,因为我的 epoll 循环正在另一个线程中运行。所以我通过寻找其他堆栈溢出处理程序来提出这个问题。请注意,通常以其他方式执行此操作要安全得多,但这很简单。如果有人想评论它真的很糟糕,那么我全神贯注。
NOTE: It is absolutely necessary to block signals handling in any thread save for the one you want to run this in. I do this by default as I believe it messy to handle signals in random threads.
注意:绝对有必要在任何线程中阻止信号处理,除了要在其中运行的线程。我默认这样做,因为我认为在随机线程中处理信号很麻烦。
static void ctlWaitPidTimeout(pid_t child, useconds_t usec, int *timedOut) {
int rc = -1;
static pthread_mutex_t alarmMutex = PTHREAD_MUTEX_INITIALIZER;
TRACE("ctlWaitPidTimeout: waiting on %lu\n", (unsigned long) child);
/**
* paranoid, in case this was called twice in a row by different
* threads, which could quickly turn very messy.
*/
pthread_mutex_lock(&alarmMutex);
/* set the alarm handler */
struct sigaction alarmSigaction;
struct sigaction oldSigaction;
sigemptyset(&alarmSigaction.sa_mask);
alarmSigaction.sa_flags = 0;
alarmSigaction.sa_handler = ctlAlarmSignalHandler;
sigaction(SIGALRM, &alarmSigaction, &oldSigaction);
/* set alarm, because no alarm is fired when the first argument is 0, 1 is used instead */
ualarm((usec == 0) ? 1 : usec, 0);
/* wait for the child we just killed */
rc = waitpid(child, NULL, 0);
/* if errno == EINTR, the alarm went off, set timedOut to true */
*timedOut = (rc == -1 && errno == EINTR);
/* in case we did not time out, unset the current alarm so it doesn't bother us later */
ualarm(0, 0);
/* restore old signal action */
sigaction(SIGALRM, &oldSigaction, NULL);
pthread_mutex_unlock(&alarmMutex);
TRACE("ctlWaitPidTimeout: timeout wait done, rc = %d, error = '%s'\n", rc, (rc == -1) ? strerror(errno) : "none");
}
static void ctlAlarmSignalHandler(int s) {
TRACE("ctlAlarmSignalHandler: alarm occured, %d\n", s);
}
EDIT: I've since transitioned to using a solution that integrates well with my existing epoll()-based eventloop, using timerfd. I don't really lose any platform-independence since I was using epoll anyway, and I gain extra sleep because I know the unholy combination of multi-threading and UNIX signals won't hurt my program again.
编辑:我已经过渡到使用与我现有的基于 epoll() 的事件循环很好地集成的解决方案,使用 timerfd。我并没有真正失去任何平台独立性,因为我一直在使用 epoll,而且我获得了额外的睡眠,因为我知道多线程和 UNIX 信号的邪恶组合不会再次伤害我的程序。
回答by Chris Dodd
If you're going to use signals anyways (as per Steve's suggestion), you can just send the signal manually when you want to exit. This will cause waitpid to return EINTR and the thread can then exit. No need for a periodic alarm/restart.
如果您无论如何要使用信号(根据史蒂夫的建议),您可以在想要退出时手动发送信号。这将导致 waitpid 返回 EINTR,然后线程可以退出。无需定期报警/重启。
回答by Krunch
I can use a signal handler for SIGCHLD, and in the signal handler do whatever I was going to do when a child exits, or send a message to a different thread to do some action. But using a signal handler obfuscates the flow of the code a little bit.
我可以为 SIGCHLD 使用信号处理程序,并在信号处理程序中执行当孩子退出时我将要执行的任何操作,或者将消息发送到不同的线程以执行某些操作。但是使用信号处理程序会稍微混淆代码流。
In order to avoid race conditions you should avoid doing anything more complex than changing a volatile flag in a signal handler.
为了避免竞争条件,您应该避免做任何比更改信号处理程序中的 volatile 标志更复杂的事情。
I think the best option in your case is to send a signal to the parent. waitpid() will then set errno to EINTR and return. At this point you check for waitpid return value and errno, notice you have been sent a signal and take appropriate action.
我认为在你的情况下最好的选择是向父母发送信号。waitpid() 然后将 errno 设置为 EINTR 并返回。此时,您检查 waitpid 返回值和 errno,注意您已收到信号并采取适当的行动。
回答by Pip
Instead of calling waitpid() directly, you could call sigtimedwait() with SIGCHLD (which would be sended to the parent process after child exited) and wait it be delived to the current thread, just as the function name suggested, a timeout parameter is supported.
您可以不直接调用waitpid(),而是使用SIGCHLD 调用sigtimedwait()(它会在子进程退出后发送到父进程)并等待它被传递到当前线程,正如函数名所建议的那样,超时参数是支持的。
please check the following code snippet for detail
请检查以下代码片段以获取详细信息
static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) {
sigset_t child_mask, old_mask;
sigemptyset(&child_mask);
sigaddset(&child_mask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
printf("*** sigprocmask failed: %s\n", strerror(errno));
return false;
}
timespec ts;
ts.tv_sec = MSEC_TO_SEC(timeout_ms);
ts.tv_nsec = (timeout_ms % 1000) * 1000000;
int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts));
int saved_errno = errno;
// Set the signals back the way they were.
if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
printf("*** sigprocmask failed: %s\n", strerror(errno));
if (ret == 0) {
return false;
}
}
if (ret == -1) {
errno = saved_errno;
if (errno == EAGAIN) {
errno = ETIMEDOUT;
} else {
printf("*** sigtimedwait failed: %s\n", strerror(errno));
}
return false;
}
pid_t child_pid = waitpid(pid, status, WNOHANG);
if (child_pid != pid) {
if (child_pid != -1) {
printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
} else {
printf("*** waitpid failed: %s\n", strerror(errno));
}
return false;
}
return true;
}
参考:https: //android.googlesource.com/platform/frameworks/native/+/master/cmds/dumpstate/DumpstateUtil.cpp#46