想象一下,我有一个启动多个子进程的进程.父母需要知道孩子什么时候退出.
我可以使用waitpid,但是如果/当父需要退出时,我无法告诉阻塞的线程waitpid优雅地退出并加入它.让事情自己清理是件好事,但这可能不是什么大不了的事.
我可以用waitpid用WNOHANG,然后睡了一段任意时间,以防止忙等待.然而,我只能知道一个孩子是否经常退出.在我的情况下,我知道孩子何时立即离开可能不是超级关键,但我想尽快知道...
我可以使用信号处理程序SIGCHLD,并在信号处理程序中执行当子项退出时要执行的任何操作,或者将消息发送到其他线程以执行某些操作.但是使用信号处理程序会稍微混淆代码流.
我真正想做的是使用waitpid一些超时,比如5秒.由于退出进程不是一个时间关键的操作,我可以懒惰地发出线程信号退出,同时仍然在waitpid其余时间阻塞它,随时准备做出反应.在linux中有这样的调用吗?在替代方案中,哪一个最好?
编辑:
基于回复的另一种方法是SIGCHLD用pthread\ 来阻塞所有线程_sigmask().然后在一个线程中,sigtimedwait()一边寻找一边打电话SIGCHLD.这意味着我可以超时调用并检查线程是否应该退出,如果没有,则保持阻塞状态等待信号.一旦a SIGCHLD被传递到这个线程,我们可以立即对它做出反应,并且在等待线程的行中,不使用信号处理程序.
geo*_*car 42
不要混合alarm()使用wait().您可以通过这种方式丢失错误信息.
使用自管技巧.这会将任何信号转换为有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);
}
Run Code Online (Sandbox Code Playgroud)
然后,你的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;
}
Run Code Online (Sandbox Code Playgroud)
您可以看到selfpipe_waitpid()如何控制超时,甚至可以与其他select()基于IO的IO 混合使用.
mpa*_*tel 29
分叉一个中间子,它分叉真正的孩子和一个超时过程并等待所有(两个)孩子.当一个人退出时,它会杀死另一个并退出.
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);
Run Code Online (Sandbox Code Playgroud)
出乎意料的简单:)
如果您确定程序中没有其他模块可以自行生成子进程,您甚至可以省略中级子进程.
ose*_*003 16
这是个有趣的问题.我发现sigtimedwait可以做到.
编辑2016/08/29:感谢Mark Edington的建议.我在Ubuntu 16.04上测试了你的例子,它按预期工作.
注意:这仅适用于子进程.遗憾的是,在Linux/Unix中似乎没有与Window的WaitForSingleObject(unrelated_process_handle,timeout)相同的方式来获得超时内无关进程终止的通知.
好的,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;
}
Run Code Online (Sandbox Code Playgroud)
chy*_*hys 13
如果您的程序仅在当代 Linux 内核(5.3 或更高版本)上运行,则首选方法是使用pidfd_open( https://lwn.net/Articles/789023/ https://man7.org/linux/man-pages/man2/ pidfd_open.2.html)。
该系统调用返回一个代表进程的文件描述符,然后您可以select或it,就像等待其他类型的文件描述符一样。pollepoll
例如,
int fd = pidfd_open(pid, 0);
struct pollfd pfd = {fd, POLLIN, 0};
poll(&pfd, 1, 1000) == 1;
Run Code Online (Sandbox Code Playgroud)
该功能可以通过信号中断,因此您可以在调用waitpid()之前设置定时器,并在定时器信号被提升时以EINTR退出.编辑:它应该像调用waitpid()之前调用alarm(5)一样简单.
| 归档时间: |
|
| 查看次数: |
26962 次 |
| 最近记录: |