C++:在子进程上使用cin

C++: using cin on a child process

本文关键字:cin 子进程 C++      更新时间:2023-10-16

我正在编写一个C++程序,该程序早期分叉,并且在子进程和父进程中都使用std::cout和std::cin。出于某种原因,在Linux上,cin似乎不在子进程中工作;它从不提示任何输入。有趣的是,这个程序在Mac上运行得很好。有人知道为什么会发生这种事吗?谢谢

您所观察到的是因为fork和exec模型1。正如您所期望的,所有的文件描述符都会被复制,但当两个进程从一个描述符中读取时,优先顺序是未定义的2。一旦fork()返回,它们是父级和子级就无关紧要了。

因此,您的情况甚至比简单地依赖于实现更糟糕。在同一个系统上可以得到两个不同的结果。

您的场景称为竞赛条件。在这种情况下,两个程序副本中的哪一个(父或子)获得哪个字符取决于许多与时间相关的细节。即使其他进程对您的系统要求什么资源,也可能对观察到的行为产生影响。

读取操作本质上不是原子操作2
如果在任何操作系统上,父操作系统和子操作系统都能从同一个流中读取相同的字符,则该操作系统无法正确地防止这种竞争条件,这是一个内核问题,应报告为可能的错误3

您可以使用信号量或其他同步方法来解决这种功能模糊性。如果正确地使用这样的机制来保证原子读取,您可能会实现线程安全(在这种情况下是进程安全),但您可能仍然没有想要的。

经典的解决方案可能是决定要读取两个进程中的哪一个std::cin,并在另一个进程中关闭std::cin。执行此操作的标准机制是测试fork调用的返回整数。如果(fork()==0),则您处于子级中。(示例在fork()文档中给出。)

如果在两个进程中都需要该值,则可以在fork之前使用pipe()和dup2(),并在每个进程中使用正确的close()将字符副本从主读取器流式传输到辅助读取器。这就是代理设计模式。如果不同的消息类型应该由不同的流程处理,那么您可能还希望实现责任链设计模式。

值得注意的是,由std::cout和std::cerr包装的输出文件描述符不存在种族条件风险,并且可以混合来自父级和子级4的输出。

[1]POSIX标准可以追溯到早期的UNIX,最早可以追溯到PDP11。

[2]开放式分组基础规范第7期IEEE Std 1003.1-20082016版手册页,用于扩展和读取状态,"标准开发人员考虑将原子性要求添加到管道或FIFO中,但认识到由于管道和FIFO的性质,无法保证{pipe_BUF}或任何其他有助于应用程序可移植性的大小的读取的原子性。">

[3]连续读取相同的消息字节可能违反标准标准。在从输入流中获取字节或字符时,应该有系统信号量或其他关键代码保护,以便在再次读取之前丢弃读取的字节或字符

[4]当消息可能超过POSIX保证时,从父级和子级向同一流写入<=512字节将原子性地进入输出流(根据Linux"man7管道")。此外,要使用多个编写器维护年表,需要在每次编写后刷新更高级别的C函数或C++方法。但是,如果已知消息在限制范围内,并且只执行低级别的write()操作,那么拥有多个编写器是完全安全的