Unix 如何在两个进程之间安全地共享文件描述符?
这是针对UNIX的后续问题 我想知道由进程打开的文件描述符是否可以安全地用于分叉进程 我已经通过同时运行数百个进程运行了一些测试,所有进程都连续写入同一个文件描述符。我发现:Unix 如何在两个进程之间安全地共享文件描述符?,unix,multiprocessing,file-descriptor,Unix,Multiprocessing,File Descriptor,这是针对UNIX的后续问题 我想知道由进程打开的文件描述符是否可以安全地用于分叉进程 我已经通过同时运行数百个进程运行了一些测试,所有进程都连续写入同一个文件描述符。我发现: 当fwrite()调用最多为8192字节时,所有调用都完全序列化,文件正常 当fwrite()调用超过8192个字节时,字符串被分成8192个字节的块,这些块被随机写入文件,最终导致损坏 我尝试使用flock(),但没有成功,因为每个进程都试图锁定/解锁相同的文件描述符,这是没有意义的。结果是一样的 是否有一种方法可以
- 当
调用最多为8192字节时,所有调用都完全序列化,文件正常fwrite()
- 当
调用超过8192个字节时,字符串被分成8192个字节的块,这些块被随机写入文件,最终导致损坏fwrite()
flock()
,但没有成功,因为每个进程都试图锁定/解锁相同的文件描述符,这是没有意义的。结果是一样的
是否有一种方法可以在所有进程之间安全地共享文件描述符,并使所有
fwrite()
调用正确序列化?首先需要注意的是stdio缓冲区。因为您使用的是stdio(fwrite()
),而不是系统调用directlr(write()
),所以您不知道数据何时会真正刷新到文件中。要绕过此问题,您必须在每次释放锁之前刷新临界区内的stdio缓冲区:
take the lock
fwrite(foo, ...);
fflush(thefile);
release the lock
…或者您可以直接切换到使用write()
现在,转到主要问题:如何锁定文件,以便一次只有一个进程可以独占访问该文件
您可能无法使用flock()
。这取决于不同的进程如何获得同一文件的文件描述符flock()
锁与打开的文件表条目相关联。由于fork()
和dup()
创建了引用相同文件表条目的新文件描述符,因此从flock()
的角度来看,它们是相同的对象,因此在这种情况下不能使用flock()
。另一方面,如果每个进程直接使用open()
打开自己的文件副本,则可以使用flock()
fcntl()
-样式锁定不会遇到此问题(而是遇到不同类型的问题!)fcntl()
锁是每个进程的锁,因此进程如何获取同一文件的文件描述符并不重要
因此,我建议您尝试使用fcntl()
样式的锁定:
struct flock ll;
/* lock */
ll.l_start = ll.l_len = ll.l_whence = 0; /* lock the whole file */
ll.l_type = F_WRLCK; /* exclusive lock */
fcntl(fd, F_SETLKW /* or F_SETLK */, &ll);
/* unlock */
ll.l_type = F_UNLCK;
fcntl(fd, F_SETLKW /* or F_SETLK */, &ll);
您使用文件描述符的方式是完全安全的。写入是不同步的,因此输出可能不是您所期望的,但这并没有什么“不安全”。如果您询问“如何将写入同步到公共文件描述符”,那么答案很明显:使用同步机制。在您描述的多个进程共享一个文件描述符的情况下,可能最简单的方法是在第二个管道上传递令牌。拥有一个在所有进程之间共享的管道,并在其中写入单个字符。当进程想要写入第一个fd时,它会尝试从管道中读取字符。读取成功后,继续写入(确保刷新),然后将一个字符写回管道。当任何进程处于关键部分(即,它已读取但尚未写入令牌)时,任何其他进程都将阻止读取,直到其他进程完成写入,只要您不忘记刷新!这是一个相当昂贵的操作,因为它需要每个进程保持两个额外的文件描述符打开,可用文件描述符数量的上限相当低,通常为1024个左右,但通常非常有效。相关:谢谢,您能详细介绍一下什么问题吗
fcntl()
遭受?每个进程锁定的主要问题是,如果同一进程的两个不同部分在同一文件上运行(例如,主应用程序使用的两个不同库,但它们彼此不了解),那么它们将相互冲突。由于锁是每个进程的,因此使用一个文件描述符解锁文件会从引用同一文件的所有其他文件描述符的角度释放锁。例如,好吧,这很有意义!或者,在给定的系统上,是否可以知道fwrite()
(或write()
)调用原子的最大长度(在我的例子中是1023)?这仅适用于文件记录器,因此写入量无论如何都应该非常小,但最好通过编程方式知道给定的write()
是否具有原子性。有关详细信息,请参阅。小结:如果您正在向管道写入,原子写入的最大大小是PIPE\u BUF
,如果您正在向管道以外的任何对象写入,则写入永远都不是原子的。是的,我读过此链接,但奇怪的是,尽管我没有向管道写入,但我的经验表明,写入的原子大小高达1023字节。欢迎任何解释!非常有趣的解释,谢谢。也就是说,我只使用共享文件描述符写入日志,一次一行。所以,我不太可能需要一个锁定机制,只要我保持在8192字节以下。你知道这个数字(我从经验中得到的)是从哪里来的吗?写操作
被保证是原子的,直到某个特定的值(PIPE_BUF),这取决于系统。很可能,在你的例子中,这个值是8192。实际值可能更小(512和4096是常见的)。检查PIPE_BUF的值。在下面的答案和评论中搜索“原子”,@Celada说PIP_BUF只适用于管道。为什么在我写文件的时候它适用呢?Celada是正确的;该标准仅保证管道的原子性。很可能,该实现对常规文件使用类似的机制,而您