简体   繁体   English

如果孩子没有状态变化,如何取消waitpid?

[英]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 ,因为它的默认处置是忽略。

EDIT编辑

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) 创建的子级继承。

EDIT 2编辑 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_openpoll / 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 上,这允许等待进程终止,您仍然必须通过带有waitidP_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 上kqueueEVFILT_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM