C中的重入和重入?

我正在读一本名为Linux System Programming的书。 引用本书:

那么系统调用和其他库函数呢? 如果您的进程正在写入文件或分配内存,并且信号处理程序写入同一文件或调用malloc(),该怎么办? 有些function显然不可重入。 如果一个程序正在执行一个非重入函数并且发生信号并且信号处理程序然后调用相同的非重合函数,则可能发生混乱。

但接下来会:

保证重入函数

保证可以安全重入的function用于信号

一些function在这里..

写()

一些function在这里..

我很困惑,是write()可重入,还是不可以? 因为我认为它与声明冲突:

如果您的进程正在写入文件,该怎么办?

只是为了补充@Joachim Pileborg先生在他的回答中提到的内容,根据Reentrancy的wiki条目,函数可重入的基本规则是

  1. 可重入代码可能不包含任何静态(或全局)非常量数据。
  2. 可重入代码可能无法修改自己的代码。
  3. 可重入代码可能不会调用不可重入的计算机程序或例程。

详细说明,该函数(如果是可重入的)不会对其自身的实现 (引入它自己使用的内部数据结构)产生任何问题,无论是从不同的上下文调用。

提供给函数的参数(例如文件描述符)不会影响它的重入。

因此,对于write() ,函数本身是Reentrant,但如果从不同的线程调用相同的文件描述符,则显然会产生错误的结果。 同样,这并不意味着, write()可重入性消失了。 它是可重入的 ,但不是线程安全的,这两个是不同的方面。

如果你可以从不同的上下文调用一个函数而不打扰来自另一个上下文的另一个调用,则可重入更多。

strtok函数为例。 它通常包含一个static局部变量,用于跟踪您要标记的字符串中的下一个位置。 由于本地static变量在函数的所有调用之间共享,因此从两个不同的上下文调用函数将导致问题。

另一方面, write系统调用没有它在调用之间存储的内部数据,这使得从不同的上下文调用是安全的。


重要的是要注意重入与线程安全不同。 以write函数为例,因为它是可重入的,你可以使用不同的文件从不同的线程调用它,而不必担心内部数据会被破坏。 但是,它不是线程安全的。 使用相同的文件描述符从不同的线程调用它将导致问题。

您引用的文档是指信号处理程序 。 这是一种非常特殊的function类型,在特殊情况下调用,并被视为特定的系统编程。 他们无视程序中的正常控制流程。

如果您不是在编写信号处理程序,那么这些文档并不适用于您。 不过,这是Mac OS上信号安全的function列表:

 $ man sigaction The following functions are either reentrant or not interruptible by signals and are async-signal safe. Therefore applications may invoke them, without restriction, from signal-catching functions: Base Interfaces: _exit(), access(), alarm(), cfgetispeed(), cfgetospeed(), cfsetispeed(), cfsetospeed(), chdir(), chmod(), chown(), close(), creat(), dup(), dup2(), execle(), execve(), fcntl(), fork(), fpathconf(), fstat(), fsync(), getegid(), geteuid(), getgid(), getgroups(), getpgrp(), getpid(), getppid(), getuid(), kill(), link(), lseek(), mkdir(), mkfifo(), open(), pathconf(), pause(), pipe(), raise(), read(), rename(), rmdir(), setgid(), setpgid(), setsid(), setuid(), sigaction(), sigaddset(), sigdelset(), sigemptyset(), sigfillset(), sigismember(), signal(), sigpending(), sigprocmask(), sigsuspend(), sleep(), stat(), sysconf(), tcdrain(), tcflow(), tcflush(), tcgetattr(), tcgetpgrp(), tcsendbreak(), tcsetattr(), tcsetpgrp(), time(), times(), umask(), uname(), unlink(), utime(), wait(), waitpid(), write(). 

关于write线程安全性,Sourav Ghosh和Joachim Pileborg的答案似乎不正确:

write应该是根据POSIX.1-2008的线程安全的,因为它不在此列表中 。

但是,来自glibc wiki :

目前Linux写入系统调用不是MT安全的。 竞争写入的多个线程可能获得相同的文件位置值并写入相同的位置,从而导致数据丢失。

似乎这个问题已在Linux内核中修复(参见linux内核邮件列表 )。