C中的重入和重入?

时间:2015-06-05 06:21:03

标签: c concurrency reentrancy

我正在读一本名为 Linux系统编程的书。引用本书:

  

系统调用和其他库函数怎么样?如果你的   进程正在写入文件或分配内存,   并且信号处理程序写入同一文件或调用malloc()?   有些功能显然不可重入。如果一个程序在   执行非重量函数和信号的中间发生和   然后信号处理程序调用相同的非导入函数,混乱   可以随之而来。

但接下来会:

  

保证可重入函数

     

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

     

这里的一些功能..

     

写()

     

这里的一些功能..

我很困惑,write()是否可以折返?因为我认为它与声明冲突:

  

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

4 个答案:

答案 0 :(得分:6)

只是根据answer添加他wiki entry for Reentrancy中已提及的 @Joachim Pileborg 先生,可重入函数的基本规则是< / p>

  1. 可重入代码可能不包含任何静态(或全局)非常量数据。
  2. 可重入代码不得修改自己的代码。
  3. 可重入代码不得调用不可重入的计算机程序或例程。
  4. 详细说明,该函数(如果是可重入的)不会对其自己的实现(引入它自己使用的内部数据结构)产生任何问题,无论是否从不同的上下文调用。

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

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

答案 1 :(得分:5)

如果您可以从不同的上下文中调用函数而不会干扰来自其他上下文的另一个调用,则可以进行更多操作。

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

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

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

答案 2 :(得分:2)

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

如果您没有编写信号处理程序,那么此文档对您来说并不实用。不过,这里是Mac OS上信号安全的功能列表:

$ 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().

答案 3 :(得分:1)

Sourav Ghosh和Joachim Pileborg的回答似乎与write的线程安全无关:

根据POSIX.1-2008,

write应该是线程安全的,因为它不在list中。

但是,来自glibc wiki

  

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

似乎这个问题已在Linux内核中修复(请参阅linux kernel mailing list)。