c - 为什么vim变成孤儿进程会崩溃?

标签 c linux vim fork exec

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    int pid = fork();
    
    if (pid) {
        sleep(5);
        // wait(NULL); // works fine when waited for it.
    } else {
        execlp("vim", "vim", (char *)NULL);
    }
}
当我运行这段代码时,vim 正常运行,然后在 5 秒后崩溃(即当它的父退出时)。当我等待它时(即不让它成为孤儿进程),代码完全正常。
为什么成为孤儿进程在这里成为一个问题?它是vim特有的吗?
为什么这甚至是 vim 可见的东西?我认为只有 parent 知道它的 child 什么时候死。但是在这里,我看到不知何故, child 会注意到当它被收养时,会发生一些事情并以某种方式崩溃。当他们的父进程也死亡时,子进程会得到通知吗?
当我运行这段代码时,我在崩溃后得到这个输出:
Vim: Error reading input, exiting...
Vim: preserving files...
Vim: Finished.

最佳答案

这实际上是因为 shell 正在执行 fork Vim 的二进制文件!
当 shell 运行一个前台命令时,它会创建一个新的进程组并使其成为连接到 shell 的终端的前台进程组。在 bash 5.0 中,你可以在 give_terminal_to() 中找到转移这个职责的代码,它使用 tcsetpgrp() 来设置前台进程组。
需要正确设置终端的前台进程组,以便前台运行的程序可以从终端获取信号(例如Ctrl+C发送中断信号,Ctrl+Z发送终端停止信号暂停进程) process),并以 Vim 等全屏程序通常所做的方式更改终端设置。 (前台进程组的主题有点超出这个问题的范围,只是在这里提到它,因为它在响应中起作用。)
当 shell 执行的进程(更准确地说,管道)终止时,shell 将收回前台进程组,使用相同的 give_terminal_to() 代码通过 shell 的进程组调用它。
这通常很好,因为在执行的管道完成时,该进程组上通常没有进程,或者如果有,它们通常不会保留在终端上(例如,如果您正在启动来自 shell 的后台守护进程,守护进程通常会关闭 stdin/stdout/stderr 流以放弃对终端的访问。)
但是对于您提出的设置,情况并非如此,其中 Vim 仍然连接到终端和前台进程组的一部分。当父进程退出时,shell 假定管道已完成,并将前台进程组设置回自身,从 Vim 所在的前前台进程组“窃取”它。因此,下次 Vim 尝试从终端读取时,读取将失败并且 Vim 将退出并显示您报告的消息。
自己查看退出的父进程不会影响 Vim 的一种方法是通过 strace 运行它。例如,使用以下命令(假设 ./vim-launcher 是您的二进制文件):

$ strace -f -o /tmp/vim-launcher.strace ./vim-launcher
由于 strace 使用 -f 选项运行以跟随 fork,所以它也将在启动时开始跟踪 Vim。 shell 将执行 strace (不是 vim-launcher ),所以它的前台管道只会在 strace 停止运行时结束。而且 strace 不会停止运行,直到 Vim 退出。 Vim 将在 5 秒后正常工作,即使它已重新设置为 init。
曾经还有一个 fghack tool ,它是 daemontools 的一部分,它完成了相同的阻塞任务,直到所有 fork 的 child 都退出。它将通过创建一个新管道并使其产生的进程继承管道来实现这一点,这种方式将自动由所有其他 fork 子进程继承。这样,它可以阻塞,直到该管道文件描述符的所有副本都关闭,这通常只在所有进程退出时发生(除非后台进程不惜一切代价关闭所有继承的文件描述符,但这实际上表明它们没有) t 想要被跟踪,到那时他们很可能已经放弃了对终端的访问权限。)

关于c - 为什么vim变成孤儿进程会崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63502153/

相关文章:

python - Pydev Python调试中如何跳转到下一个断点

merge - 从命令行在 vim 中混合垂直和水平分割

C:正则表达式与浮点文字不匹配

python - 尝试从Python3中的C函数获取数组

java - 在 Java 中按引用传递和在 C 中传递指针有什么区别?

c++ - 来自 .ui 文件的 Qt : How to generate . h 和 .cpp - Vim 和 Qt 实用吗?

python - Vim:更改整个代码库中的函数调用签名

c - C中快速高效的最小二乘拟合算法?

linux - 如何导出计算节点的路径,并行编程

linux - 通过将 -lrt 传递给 gcc 修复了对 `timer_getoverrun' 的 undefined reference 。但为什么?