多线程和多进程情况下,fprintf函数的行为如何?

3

这里有两个多线程的进程 ab

  1. a 分叉(fork) b,并且 b 立刻执行一个新程序;
  2. a 使用 dupfreopen 把 stderr 重定向到日志文件中(a 实际上是 Apache 的 httpd2.22);
  3. b 继承了来自 a 打开的 stderr。 (我正在修改 Apache httpd, b 是我的程序),因此 b 使用 fprintf(stderr....) 记录日志;
  4. 所以,ab 共享同一个日志文件;
  5. 没有锁机制可以防止 ab 写入日志时互相干扰;

我发现一些日志信息交错,且有一些日志信息丢失。

这两个写入同一文件的进程是否会隐式地将彼此锁定?

更重要的问题是:如果我们只在一个单独的多线程进程内使用 fprintf,那么 fprintf 就是线程安全的,即一个 fprintf 调用不会干扰另一个线程中的 fprintf 调用吗?许多文章都这么说,但我自己难以确保,所以在这里寻求帮助。

A:复制文件描述符的代码如下:

......
rv = apr_file_dup2(stderr_log, s_main->error_log, stderr_p);//dup the stderr to the logfile
apr_file_close(s_main->error_log);//here ,2 fd point to the same file description,so close one of 

那么

B:apache本身使用这种方式进行日志记录:

......
if (rv != APR_SUCCESS) {
    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s_main, ".........");

为了方便起见,我用以下方式登录:

fprintf(stderr,".....\n")

我相信 Apache 和我使用同一个文件描述符进行文件写入。


最好是发布实际的代码,而不是代码的描述。 - Sander De Dycker
这是一个非常长的程序,我的fprintf散布在这里和那里。我会在apache2中找到重复将stderr传输到error.log的代码,然后在这里发布它们。 - basketballnewbie
请问,我的积分因为这个原因被扣除了吗? - basketballnewbie
1个回答

9
如果您正在使用单个FILE对象执行打开文件的输出,则对该FILE进行的整个fprintf调用将是原子的,即在fprintf调用期间保持FILE上的锁定。由于FILE局限于单个进程的地址空间,因此这种设置仅适用于多线程应用程序;它不适用于多进程设置,其中多个不同的进程正在访问引用相同底层打开文件的各个FILE对象。即使在这里使用fprintf,每个进程也有自己的FILE可以锁定和解锁,而其他进程看不到更改,因此写入可能会交错。有几种方法可以防止这种情况发生:
  1. 在共享内存中分配同步对象(例如进程共享信号量或互斥体)并使每个进程在写入文件之前获取锁定(因此一次只能有一个进程写入);或

  2. 使用文件系统级别的咨询锁定,例如fcntl锁定或(非POSIX)BSD flock接口;或

  3. 而不是直接写入日志文件,请写入另一个进程将进入日志文件的管道。只要写入小于PIPE_BUF字节长,就可以保证对管道的写入是原子的(由POSIX保证)。在这种情况下,您不能使用fprintf(因为它可能执行多个底层写操作),但是您可以使用snprintf到一个PIPE_BUF大小的缓冲区,然后是write


一根管道只能有一个写端和一个读端。因此,一根管道只能在两个进程之间使用。如果我为日志设置了一个专用的进程,然后再设置多于两个的工作进程,每个工作进程都必须创建一个管道,并通过这个管道将日志消息传递给日志进程。因此,这些管道彼此独立,即使没有POSIX的保证,这些管道中的内容也不会被其他工作进程交错。而且,保证从多个管道接收到的消息不会被交错是日志进程的责任。没错吧?谢谢。 - basketballnewbie
@ybungalobill:不对。write 可以返回一个小于 nbytes 的值(短写)。 - R.. GitHub STOP HELPING ICE
@R..GitHubSTOPHELPINGICE:当你写入普通文件时,不会发生这种情况:https://dev59.com/bXM_5IYBdhLWcg3w9oMA#35256561 - Yakov Galka
@ybungalobill:我相信你(和那个答案)说的标准要求(以及所提到的操作系统遵守)操作的原子性,但我不明白它在哪里禁止了成功的短写入。 - R.. GitHub STOP HELPING ICE
@ybungalobill:它绝对不能将文件位置增加1000字节,但只写入100字节并返回100。这将破坏所有现有代码,这些代码通过重新启动剩余长度来正确处理短写入,特别是由于在写入完整长度之前到达信号而导致的短写入。(这通常不是常规文件的问题,因为它们进行非可中断睡眠、推迟信号,但对于所有其他类型和使用可中断选项挂载的NFS,则存在问题。) - R.. GitHub STOP HELPING ICE
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接