(不)可重入函数

链接:https://baike.baidu.com/item/%E5%8F%AF%E9%87%8D%E5%85%A5%E5%87%BD%E6%95%B0/4521100?fr=aladdin

          http://www.gnu.org/software/libc/manual/html_node/POSIX-Safety-Concepts.html#POSIX-Safety-Concepts

  • 可重入函数

     在 实时系统的设计中,经常会出现多个任务调用同一个函数的情况。如果这个函数不幸被设计成为不可重入的函数的话,那么不同任务调用这个函数时可能修改其他任 务调用这个函数的数据,从而导致不可预料的后果。那么什么是可重入函数呢?所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会 出错。不可重入函数在实时系统设计中被视为不安全函数。

     满足下列条件的函数多数是不可重入的:

(1)函数体内使用了静态的数据结构;

(2)函数体内调用了malloc()或者free()函数(malloc内部维护了全局的链表用来管理分配的内存,这就是状态信息,free也一样),而且malloc函数虽然本身是线程安全的,但系统调用时存在全局的heap lock(堆内存锁(全局锁),保证堆内存分配时的一致连续性),信号句柄中使用malloc函数时,有可能会发生在主程序调用malloc时,但还未结束,heap lock还未释放,被信号所中断,再次调用malloc函数,就会出现同一线程对heap lock连续两次锁,即会出现死锁 deadlock。);

(3)函数体内调用了标准I/O函数

  (4)进行了浮点运算.许多的处理器/编译器中,浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现。

  把一个不可重入函数变成可重入的唯一方法是用可重入规则来重写他。其实很简单,只要遵守了几条很容易理解的规则,那么写出来的函数就是可重入的。   

 (1)不要使用全局变量。因为别的代码很可能覆盖这些变量值。

 (2)在和硬件发生交互的时候,切记执行类似disinterrupt()之类的操作,就是关闭硬件中断。完成交互记得打开中断,在有些系列上,这叫做“进入/退出核心”或者用OS_ENTER_KERNAL/OS_EXIT_KERNAL来描述。//这是临界区保护

 (3)不能调用任何不可重入的函数。
  (4)谨慎使用堆栈。最好先在使用前先OS_ENTER_KERNAL。

  • 与线程安全的关系

可重入与线程安全两个概念都关系到函数处理资源的方式。但是,他们有重大区别:
  • 可重入概念会影响函数的外部接口,而线程安全只关心函数的实现。
    • 大多数情况下,要将不可重入函数改为可重入的,需要修改函数接口,使得所有的数据都通过函数的调用者提供。
    • 要将非线程安全的函数改为线程安全的,则只需要修改函数的实现部分。一般通过加入同步机制以保护共享的资源,使之不会被几个线程同时访问。
  • 操作系统背景与CPU调度策略:
    • 可重入是在单线程操作系统背景下,重入的函数或者子程序,按照后进先出的线性序依次执行完毕。
    • 多线程执行的函数或子程序,各个线程的执行时机是由操作系统调度,不可预期的,但是该函数的每个执行线程都会不时的获得CPU的时间片,不断向前推进执行进度。
  • 可重入函数未必是线程安全的;线程安全函数未必是可重入的。
    • 例如,一个函数打开某个文件并读入数据。这个函数是可重入的,因为它的多个实例同时执行不会造成冲突;但它不是线程安全的,因为在它读入文件时可能有别的线程正在修改该文件,为了线程安全必须对文件加“同步锁”。
    • 另一个例子,函数在它的函数体内部访问共享资源使用了加锁、解锁操作,所以它是线程安全的,但是却不可重入。因为若该函数一个实例运行到已经执行加锁但未执行解锁时被停下来,系统又启动该函数的另外一个实例,则新的实例在加锁处将转入等待。如果该函数是一个中断处理服务,在中断处理时又发生新的中断将导致资源死锁。fprintf函数就是线程安全但不可重入。

 

  

可重入函数列表:

_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()。

 

posted @ 2020-11-25 19:28  chang_shaofei  阅读(516)  评论(0)    收藏  举报