【问题标题】:how fprintf behavior when multi-threaded and multi-processed?多线程和多处理时 fprintf 的行为如何?
【发布时间】:2012-07-24 18:03:41
【问题描述】:

这里是进程ab,它们都是多线程的。

  1. a forks bb 立即执行一个新程序;
  2. a dups 和 freopens stderr 到日志文件(a 是事实上的 apache 的 httpd2.22)
  3. ba 继承打开的标准错误。 (我正在适配 apache httpd,b 是我的程序),b 使用 fprintf(stderr....) 进行日志记录
  4. 所以ab 共享同一个文件进行日志记录
  5. ab没有写日志的锁机制

我发现有些log msg是交错的,还有一点log msg丢失了。

同一文件的两个写入者可以隐式地相互锁定吗?

更重要的问题是:如果我们只在一个多线程进程中使用fprintffprintf 是线程安全的,即fprintf 的一个调用不会干预另一个线程中的另一个fprintf 调用?很多文章都这么说,但是我自己也不容易保证,所以在这里求助。

A:复制fd的代码是这样的:

......
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, ".........");

C:为了方便,我是这样登录的:

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

我很确定 apache 和我使用相同的 fd 进行文件写入。

【问题讨论】:

  • 最好贴出实际代码,而不是代码描述。
  • 这是一个很长的程序,我的 fprintf 分散在各处。我会在 apache2 中找到将 stderr 复制到 error.log 的代码,然后将它们发布在这里
  • 请原谅,我的积分是不是因为这个原因被扣分了?

标签: c linux printf


【解决方案1】:

如果您使用单个 FILE 对象在打开的文件上执行输出,则对该 FILE 的整个 fprintf 调用将是原子的,即锁定在 FILE 上的持续时间fprintf 电话。由于FILE 是单个进程地址空间的本地地址,因此这种设置仅适用于多线程应用程序;它不适用于多进程设置,其中多个不同进程正在访问引用同一底层打开文件的单独 FILE 对象。即使您在这里使用fprintf,每个进程都有自己的FILE,它可以锁定和解锁而其他人看不到更改,因此写入最终可能是交错的。有几种方法可以防止这种情况发生:

  1. 在共享内存中分配一个同步对象(例如进程共享的信号量或互斥锁),并使每个进程在写入文件之前获得锁(因此一次只能写入一个进程);或者

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

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

【讨论】:

  • 一个管道只能有一个写端和一个读端。所以,一个管道只能在2个进程之间使用。如果我设置了一个专用的日志进程,然后超过2个工作进程,这些工作进程中的每一个都必须创建一个管道,然后通过该管道将日志消息传递给日志记录进程。所以,这些管道是相互独立的,即使没有 POSIX 的保证,这些管道中的内容也不会被其他工作进程交错。日志进程有责任保证它从许多管道收到的 msgs 不被交错- ----------我说得对吗?tks
  • 不,您可以为所有这些管道使用相同的管道。
  • tks,我会试试这个:将管道写入端的 fd 复制到多个进程,并且日志记录进程保持读取端。
  • 在使用分叉 apache 设置时如何在 apache 模块中使用 ap_log_error()?日志会使用这个函数交错,还是这个函数可以防止这种情况发生?
  • 选项 #3 毫无意义。如果您首先使用snprintf 在缓冲区中合成您的消息,您可以直接在O_APPEND 文件描述符上使用write 系统调用。不需要管道或其他协调过程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多