【问题标题】:How to terminate waitpid on user input?如何终止用户输入的waitpid?
【发布时间】:2019-07-07 06:45:17
【问题描述】:

我正在使用 C 语言开发基于 ncurses 的文件管理器。问题是某些子进程可能需要一些时间才能完成,并且在此之前由于waitpid 而它仍然卡住。

我不能使用WNOHANG 标志,因为下一个代码块取决于子进程的输出。

void getArchivePreview(char *filepath, int maxy, int maxx)
{
    pid_t pid;
    int fd;
    int null_fd;

    // Reallocate `temp_dir` and store path to preview file
    char *preview_path = NULL;
    allocSize = snprintf(NULL, 0, "%s/preview", cache_path);
    preview_path = malloc(allocSize+1);
    if(preview_path == NULL)
    {
        endwin();
        printf("%s\n", "Couldn't allocate memory!");
        exit(1);
    }
    snprintf(preview_path, allocSize+1, "%s/preview", cache_path);

    // Create a child process to run "atool -lq filepath > ~/.cache/cfiles/preview"
    pid = fork();
    if( pid == 0 )
    {
        remove(preview_path);
        fd = open(preview_path, O_CREAT | O_WRONLY, 0755);
        null_fd = open("/dev/null", O_WRONLY);
        // Redirect stdout
        dup2(fd, 1);
        // Redirect errors to /dev/null
        dup2(null_fd, 2);
        execlp("atool", "atool", "-lq", filepath, (char *)0);
        exit(1);
    }
    else
    {
        int status;
        waitpid(pid, &status, 0);
        getTextPreview(preview_path, maxy, maxx);
        free(preview_path);
    }
}

在这种情况下,如果用户决定转到其他文件,我想继续执行程序的其余部分。我可以通过什么方式更改程序的架构?

【问题讨论】:

  • 也许考虑为SIGCHLD 使用sigaction 处理程序?这样,您可以等待子退出(由信号处理程序指示)或输入。
  • 怎么能同时等待他们两个呢?如果我理解正确,它将等待孩子或等待输入
  • @Hasturkun,如果我理解正确你的意思,处理SIGCHLD 并使用sigactionSIGUSR1,使用raise 在用户输入上发出SIGUSR1 的信号。
  • @JohnLeaf:如果您使用的是select(或poll),您可以使用socketpaireventfdsignalfd(前两个由信号处理程序)等待输入和子终止。存在其他类似的选项。 (有些取决于您的程序是否是多线程的)
  • 做两个分叉?

标签: c fork


【解决方案1】:

如果我正确理解了这个问题,那么您想在孩子完成或任何用户输入时取消阻止父母。

正如this 评论中所建议的,您可以处理SIGCHLD 和另外一个信号说SIGUSR1SIGUSR1 将在您获得用户输入时引发。以下是同时处理 SIGCHLD 和 'SIGUSR1' 的示例。如果使用输入任何数字,那么它会将SIGUSR1 提高到父级和父级kill 子级。其他孩子将在退出时提出SIGCHLD

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

int raised_signal = -1; 

static void cb_sig(int signal)
{
        if (signal == SIGUSR1)
                raised_signal = SIGUSR1;
        else if (signal == SIGCHLD)
                raised_signal = SIGCHLD;
}

int main()
{
        int pid;
        int i, a;
        struct sigaction act;

        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        act.sa_handler = cb_sig;

        if (sigaction(SIGUSR1, &act, NULL) == -1) 
                printf("unable to handle siguser1\n");
        if (sigaction(SIGCHLD, &act, NULL) == -1) 
                printf("unable to handle sigchild\n");

        pid = fork();
        if (pid == 0) {
                /* child */
                for (i = 0; i < 10; i++) {
                        sleep(1);
                        printf("child is working\n");
                }
                exit(1);
        } else {
                /* parent */
                if (-1 ==  scanf("%d", &a)) {
                        if (errno == EINTR)
                                printf("scanf interrupted by signal\n");
                } else {
                        raise(SIGUSR1);
                }

                if (raised_signal == SIGUSR1) {
                        printf("user terminated\n");
                        kill(pid, SIGINT);
                } else if (raised_signal == SIGCHLD) {
                        printf("child done working\n");
                }

                exit(1);
        }

        return 0;
}

【讨论】:

  • 您好,感谢您的回复。您的代码按原样对我来说工作得很好,但是当我尝试在我的代码中实现时(如 here 所示),程序只是挂起并退出,并在任何输入上出现错误 User defined signal 1。有什么原因可能会出现问题吗?
  • 我还有一个问题,所以我想我会单独发表评论。你能解释一下为什么if (-1 == scanf("%d", &amp;a)) 不阻止吗? scanf 不会等待输入吗?
  • 我没有看到 sigaction(SIGCHLD, &amp;act, NULL), sigaction(SIGUSR1, &amp;act, NULL) 在您的代码中被调用。如果您以后不想使用它,也请remove the signal handler
  • 嗨,我正在阅读有关信号处理的更多信息,并且我读到您不能从信号处理程序修改全局变量,除非它们是 volatile sig_atomic_t 类型,但是,在我的代码中,我将 int raised_signal 从信号处理程序。为什么会这样?
  • 是的,如果您尝试从信号和主进程更新全局变量,则可能会出现竞争条件。在这种情况下,我们正在从信号处理程序更新raised_signal,而主进程只是读取变量。所以这不会在给定时间有两个单独的raised_signal 副本。就原子变量而言,它保证变量将在一条指令中更新,这样我们就没有它的多个副本。这只是一个例子,你可以进一步阅读“Linux System Programming by Robert Love”中的“信号”一章
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-09
  • 2020-10-09
  • 1970-01-01
  • 2015-06-22
  • 2020-06-01
相关资源
最近更新 更多