V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
levelworm
V2EX  ›  程序员

和 ChatGPT 纠缠了几个小时,总算弄明白一个问题了,但是不确定她说的对不对,再来确认一下

  •  
  •   levelworm · 360 天前 · 4489 次点击
    这是一个创建于 360 天前的主题,其中的信息可能已经有所发展或是发生改变。

    根据她的解释,在 Linux C 编程中,如果主进程 fork 出去两个子进程 B,C 的话,B 和 C 都会拷贝一份所有打开的 file descriptor 。

    那么,假设主进程 fork 出去三个进程 ABC ,其中 AB 用 pipe1 连在一块,BC 用 pipe2 连在一块(三个连在一起,比如说echo blahblahcat | grep cat | wc)。我发现,就算 C 不用 pipe1 ,它也必须把 pipe1 的写入口关掉。

    ChatGPT 进一步解释道,这是因为,Linux kernel 会监视每一个 pipe 的每一个写入口。因为 B 和 C 分属两个子进程,所以它们其实是相当于打开了两个 pipe1 的写入口。以上面那个命令行为例,B 中的grep通过 pipe1 的读入口读入数据,C 中 pipe1 的写入口一直开着,就导致 kernel 没有发送 EOF ,导致grep挂机。哪怕 B 把自己的 pipe1 写入口关掉都不行,因为 B 和 C 的 pipe1 写入口是两个拷贝。

    我又查了很久的 stackoverflow ,感觉她这个解释还是很有道理的,但是毕竟我没读过源代码,所以也不敢肯定,上来请教一下大家,她说的正确吗?

    19 条回复    2023-04-23 09:37:53 +08:00
    Inn0Vat10n
        1
    Inn0Vat10n  
       360 天前
    这纠缠的几小时都是在解释你到底想问什么吗,我是没看懂
    levelworm
        2
    levelworm  
    OP
       360 天前 via Android
    @Inn0Vat10n 就是输入文字啥的,其实是有些东西我没理解,导致没看懂,但是我又不知道是哪些东西我没理解,所以折腾了半天才弄明白。

    其实我真正没理解的是 fork 的机制。
    qwq11
        3
    qwq11  
       360 天前   ❤️ 1
    https://man7.org/linux/man-pages/man7/pipe.7.html

    If all file descriptors referring to the write end of a pipe have been closed, then an attempt to read(2) from the pipe will see end-of-file (read(2) will return 0). If all file descriptors referring to the read end of a pipe have been closed, then a write(2) will cause a SIGPIPE signal to be generated for the calling process. If the calling process is ignoring this signal, then write(2) fails with the error EPIPE. An application that uses pipe(2) and fork(2) should use suitable close(2) calls to close unnecessary duplicate file descriptors; this ensures that end-of-file and SIGPIPE/EPIPE are delivered when appropriate.
    pcbl
        4
    pcbl  
       360 天前 via Android   ❤️ 5
    这种问题千万别问 chatgpt ,它会一本正经的胡说八道,你质疑一下它又会胡编出来另一个
    greensea
        5
    greensea  
       360 天前   ❤️ 2
    警惕 GPT 依赖,GPT 对于这种定性的问题很容易一本道。对于定性的问题,最好是自己在这个领域已经有了一定程度的了解,才去问 GPT ,否则很容易被带偏还不自知
    levelworm
        6
    levelworm  
    OP
       360 天前 via Android
    @qwq11 对,这个也看了,但是还是没有理解,后来和 ai 掰扯的时候才意识到是哪里弄错了。
    levelworm
        7
    levelworm  
    OP
       360 天前 via Android
    @greensea 你们说的有道理,我也看过文档,但是还是没完全说服自己。怎么说呢,就是不知道自己哪里不知道。
    fds
        8
    fds  
       360 天前   ❤️ 1
    这我也不知道,不过我建议你简单实践一下,编写个程序,可以根据参数定时关闭 fd ,然后监听 pipe 有没有 EOF 打印日志,把几个程序串起来看看。
    fox0001
        9
    fox0001  
       360 天前 via Android   ❤️ 1
    纠正 ChatGPT 时,它会先承认“你说得都对”,然后开始编“故事”…
    lbinv2
        10
    lbinv2  
       360 天前   ❤️ 1
    @fox0001 笑死,我问 GPT-4 它是不是 GPT-4 他说他是 GPT-3 ,我否定它,它就说它是 GPT-4 ,又否定它,它又说它是 GPT-3
    Y25tIGxpdmlk
        11
    Y25tIGxpdmlk  
       360 天前   ❤️ 1
    @fox0001 #9 他还会很乖的跟你道个歉呢
    yin1999
        12
    yin1999  
       360 天前   ❤️ 2
    举个简单的例子,比如我现在主进程里面使用 pipe 创建了一个管道,然后将输入端的文件描述符传递给 fork 出来的 A ,并在 A 中重定向输出到 pipe 的输入端,关闭 pipe 的输出端文件描述符。但我没有在主进程中关闭输入端的文件描述符并直接 fork 出了进程 B ,在 B 中重定向 Pipe 的输出端到标准输入,也在这里关闭 pipe 的输入端。那即使 A 退出了,B 中调用 read 也是会阻塞的(读不到 EOF )。因为主进程里面没有关闭输入端的文件描述符。

    正确的做法是在 fork 出 A 后在主进程直接关闭输入端的文件描述符,再 fork B ,然后再在主进程中关闭输出端文件描述符。这样能保证输入端和输出端的文件描述符仅由两个 fork 出的子进程持有。

    fork 得到的文件描述符可以当作引用吧,如果不是所有的引用都断开了,并不会关闭所指向的文件描述符。(不确定是否完全正确,这两天有踩过没在主进程关闭文件描述符的坑)
    aijam
        13
    aijam  
       360 天前   ❤️ 1
    不就是保证一个 pipe 只有一个 reader 一个 writer 吗?
    veike
        14
    veike  
       360 天前   ❤️ 1
    有些地方 chatgpt 不可靠,我问他休眠会切断电源吗?它回答不会,我说你回答错了,休眠是会切断电源的。它说不好意思,我把休眠和睡眠搞混了。大部分地方 chatGPT 对我确实有帮助,但是有些地方它也会出错。
    swiftg
        15
    swiftg  
       360 天前   ❤️ 1
    chatGPT 给的数据太假了,一本正经地胡编乱造错误的甚至不存在的东西,连网址都给你瞎编,让他查个东西,80%的数据都是错的,不是了解的东西的话,很容易就被带偏了
    levelworm
        16
    levelworm  
    OP
       360 天前 via Android
    @fds 谢谢,我就是写了程序看到了效果,但是想弄明白里头的原理,同时看不懂源代码,所以只好问。我是用 ps 看的,能看到哪个 S+
    levelworm
        17
    levelworm  
    OP
       360 天前 via Android
    @fox0001 我觉得编程问题,一个是用英文,一个是和他说好不能瞎扯,我觉得就还好。不过我觉得最可惜的是数据库一直是 21 年九月之前的数据。
    levelworm
        18
    levelworm  
    OP
       360 天前 via Android
    @yin1999 多谢,我的错误在于把 file descriptor 看成了指针,以为 close 一次就够了。其实每个子进程都有一个拷贝。
    gaifanking
        19
    gaifanking  
       359 天前
    只做过父子管道通信的供参考:
    pipe(fd);
    pid = fork();
    if (pid == 0){
    close(fd[0]);
    } else {
    close(fd[1]);
    }
    管道在 fork 后就要关掉不需要的那端
    我是看的 bili 上李慧芹老师的课程 https://www.bilibili.com/video/BV18p4y167Md?p=236&vd_source=181accc8aeebc9d0f443b049b00fe228
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1682 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 16:50 · PVG 00:50 · LAX 09:50 · JFK 12:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.