【问题标题】:Some pipe file descriptor stays open even though i close everything即使我关闭了所有内容,一些管道文件描述符仍保持打开状态
【发布时间】:2021-05-31 07:25:43
【问题描述】:
static int pipefd[2];
static pid_t cpid = 0;

void sigtimeout(int num) {
    kill(cpid, 9);
    close(pipefd[1]);
    close(pipefd[0]);
    pipe(pipefd);
    write(pipefd[1], "T/O\n", 5);
}

void settimer(float time) {
    struct itimerval timer;
    timer.it_value.tv_sec = (int)time;
    timer.it_value.tv_usec = (timeout - (int)time) * 1000000;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &timer, NULL);
}

pid_t popen2(char *cmd) {
    if (pipe(pipefd) == -1)
        return -1;
    int pid;
    if ((pid = fork()) == -1)
        return -1;
    if (pid == 0) {
        close(STDIN_FILENO);
        close(STDERR_FILENO);
        dup2(pipefd[1], STDOUT_FILENO);
        close(pipefd[0]);
        close(pipefd[1]);
        execlp("sh", "sh", "-c", cmd, NULL);
        _exit(EXIT_SUCCESS);
    } else
        settimer(timeout);
    return pid;
}

void getcmd(const Block *block, char *output)
{
    if (block->signal)
    {
        output[0] = block->signal;
        output++;
    }
    strcpy(output, block->icon);
    char *cmd;

    if (button)
    {
        cmd = strcat(exportstring, block->command);
        cmd[14] = '0' + button;
        cpid = popen2(cmd);
        if (cpid == -1) {
            close(pipefd[0]);
            close(pipefd[1]);
            return;
        }
        cmd[16] = '\0';

    }
    else
    {
        cmd = block->command;
        cpid = popen2(cmd);
        if (cpid == -1) {
            close(pipefd[0]);
            close(pipefd[1]);
            return;
        }
    }
    button = 0;

    waitpid(cpid, 0, 0);
    settimer(0);
    kill(cpid, 9);
    close(pipefd[1]);

    int i = strlen(block->icon);
    read(pipefd[0], output+i, CMDLENGTH-i-delimLen);
    close(pipefd[0]);
    for (char *c = output; *c; c++)
        if (*c == '\n') {
            c[1] = '\0';
            break;
        }
    i = strlen(output);
    if (delim[0] != '\0') {
        //only chop off newline if one is present at the end
        i = output[i-1] == '\n' ? i-1 : i;
        strncpy(output+i, delim, delimLen); 
    }
    else
        output[i++] = '\0';
}

所以我正在尝试修改 dwmblocks 以添加超时功能。这样,如果命令挂起,整个状态栏不会冻结。

似乎一切正常,但有一个小问题。

我的代码使我的整个 linux 系统崩溃,因为它每次运行命令时都会打开一些文件描述符。您可能知道,Linux 对此有保护,因此我系统上尝试打开文件描述符的所有其他应用程序也会崩溃。

问题是,我实际上是在关闭我在代码中打开的每个管道,即使是在你 fork 时自动打开的管道。我就是想不通是什么问题。

非常感谢任何帮助。

顺便说一句:我只是把相关代码放在这里,因为问题出在文件描述符上。这是我在代码中使用文件描述符的唯一地方。

如果您觉得在某些方面相关,请随时询问更多代码部分:)

【问题讨论】:

    标签: c linux pipe fork


    【解决方案1】:

    您的popen2() 函数使父进程中的两个管道末端保持打开状态。父进程应始终关闭写入端(子进程写入)。也许那是泄漏的那个。

    如果您要打印(int)getpid() 以显示进程ID,您可以列出/proc/PID/fd/ 中的伪文件,因为这些伪文件描述了进程当前打开的文件描述符。 (如果您使用带有进程 ID 号的 ls -laF /proc/PID/fd/ 而不是 PID,您甚至可以看到描述符打开的内容/位置。)

    this answer here 中,我最近展示了如何实现safe_pipe()run()wait_all_children() 函数来探索子进程之间的复杂管道方案。 safe_pipe() 默认为每个管道描述符设置 close-on-exec 标志,run() 仅在子进程使用这样的描述符时清除该标志,因此分叉的子进程没有其他管道的描述符完全开放。包含的 example.c 还显示了在子进程已启动时父进程必须如何关闭子进程使用的所有管道端,以便子进程正确检测输入的结束。 (如果父进程打开了写入端,那么它可以写入管道,因此从该管道的读取端读取的任何子进程都不会看到输入端。)

    如果有必要或有帮助,我愿意解释 safe_pipe()run()wait_all_children() 是如何做的,以及为什么。特别是,run() 在 exec 之前的子进程和父进程之间使用了一个额外的管道,以检测执行指定二进制文件的问题(和其他错误)。虽然其中可能存在拼写错误,但两者都是非常健壮的实现,不会泄漏资源等。

    【讨论】:

    • 我会调查的。谢谢你的时间:)
    • 这可能是由于 settimer 和 sleep 之间未定义的交互而导致的竞争条件。真是头疼……
    【解决方案2】:

    这个好尴尬哈哈哈

    我的 settimer 函数有一个错字:超时应该是时间。

    所以真正的问题是由于比赛条件,即使我的管道系统远非完美。

    这表明有时调试错误是多么困难。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-02
      • 1970-01-01
      • 2014-03-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多