当前位置:  开发笔记 > 编程语言 > 正文

Waitpid相当于超时?

如何解决《Waitpid相当于超时?》经验,为你挑选了4个好方法。

想象一下,我有一个启动多个子进程的进程.父母需要知道孩子什么时候退出.

我可以使用waitpid,但是如果/当父需要退出时,我无法告诉阻塞的线程waitpid优雅地退出并加入它.让事情自己清理是件好事,但这可能不是什么大不了的事.

我可以用waitpidWNOHANG,然后睡了一段任意时间,以防止忙等待.然而,我只能知道一个孩子是否经常退出.在我的情况下,我知道孩子何时立即离开可能不是超级关键,但我想尽快知道...

我可以使用信号处理程序SIGCHLD,并在信号处理程序中执行当子项退出时要执行的任何操作,或者将消息发送到其他线程以执行某些操作.但是使用信号处理程序会稍微混淆代码流.

我真正想做的是使用waitpid一些超时,比如5秒.由于退出进程不是一个时间关键的操作,我可以懒惰地发出线程信号退出,同时仍然在waitpid其余时间阻塞它,随时准备做出反应.在linux中有这样的调用吗?在替代方案中,哪一个最好?


编辑:

基于回复的另一种方法是SIGCHLDpthread\ 来阻塞所有线程_sigmask().然后在一个线程中,sigtimedwait()一边寻找一边打电话SIGCHLD.这意味着我可以超时调用并检查线程是否应该退出,如果没有,则保持阻塞状态等待信号.一旦a SIGCHLD被传递到这个线程,我们可以立即对它做出反应,并且在等待线程的行中,不使用信号处理程序.



1> geocar..:

不要混合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);
}

然后,你的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;
}

您可以看到selfpipe_waitpid()如何控制超时,甚至可以与其他select()基于IO的IO 混合使用.


如果两个孩子死亡,您不一定会收到两个SIGCHLD通知.如果有太多SIGCHLD进入(大致为PIPE_BUF),则会使管道无阻塞.

2> mpartel..:

分叉一个中间子,它分叉真正的孩子和一个超时过程并等待所有(两个)孩子.当一个人退出时,它会杀死另一个并退出.

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);

出乎意料的简单:)

如果您确定程序中没有其他模块可以自行生成子进程,您甚至可以省略中级子进程.



3> osexp2003..:

这是个有趣的问题.我发现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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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;
}



4> Steve Baker..:

该功能可以通过信号中断,因此您可以在调用waitpid()之前设置定时器,并在定时器信号被提升时以EINTR退出.编辑:它应该像调用waitpid()之前调用alarm(5)一样简单.


实际上不要这样做.如果waitpid()接收子进程但是SIGALRM在内核返回之前触发,则可能会丢失子进程.许多unix在这里也有bug,即使在理想情况下也不能正确使用EINTR.
请注意阅读上述评论的任何人:使用pthread_sigmask而不是sigprocmask
推荐阅读
mobiledu2402851323
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有