可重入和不可重入

重入一般可以理解为一个函数在同时多次调用,例如操作系统在进程调度过程中,或者单片机、处理器等的中断的时候会发生重入的现象。
一般浮点运算都是由专门的硬件来完成,举个例子假设有个硬件寄存器名字叫做FLOAT,用来计算和存放浮点数的中间运算结果
假设有这么个函数
void fun()
{
//...这个函数对FLOAT寄存器进行操作
}
假如第一次执行,有个对浮点数操作运算的结果临时存在FLOAT寄存器中,而就在这时被中断了,而中断函数或者另一个进程也调用fun函数,这时第二次调用的fun函数在执行的过程中就会破坏第一次FLOAT寄存器中的结果,这样当返回到第一次fun函数的时候,结果就不正确了。

可以把fun函数理解为printf()函数。

赞同

(转)可重入和不可重入

2011-10-04 21:38

这种情况出现在多任务系统当中,在任务执行期间捕捉到信号并对其进行处理时,进程正在执行的指令序列就被信号处理程序临时中断。如果从信号处理程序返回,则继续执行进程断点处的正常指令序列,从重新恢复到断点重新执行的过程中,函数所依赖的环境没有发生改变,就说这个函数是可重入的,反之就是不可重入的。 众所周知,在进程中断期间,系统会保存和恢复进程的上下文,然而恢复的上下文仅限于返回地址,cpu寄存器等之类的少量上下文,而函数内部使用的诸如全局或静态变量,buffer等并不在保护之列,所以如果这些值在函数被中断期间发生了改变,那么当函数回到断点继续执行时,其结果就不可预料了。打个比方,比如malloc,将如一个进程此时正在执行malloc分配堆空间,此时程序捕捉到信号发生中断,执行信号处理程序中恰好也有一个malloc,这样就会对进程的环境造成破坏,因为malloc通常为它所分配的存储区维护一个链接表,插入执行信号处理函数时,进程可能正在对这张表进行操作,而信号处理函数的调用刚好覆盖了进程的操作,造成错误。

满足下面条件之一的多数是不可重入函数: (1)使用了静态数据结构; (2)调用了malloc或free; (3)调用了标准I/O函数;标准io库很多实现都以不可重入的方式使用全局数据结构。 (4)进行了浮点运算.许多的处理器/编译器中,浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现。

1) 信号处理程序A内外都调用了同一个不可重入函数B;B在执行期间被信号打断,进入A (A中调用了B),完事之后返回B被中断点继续执行,这时B函数的环境可能改变,其结果就不可预料了。 2) 多线程共享进程内部的资源,如果两个线程A,B调用同一个不可重入函数F,A线程进入F后,线程调度,切换到B,B也执行了F,那么当再次切换到线程A时,其调用F的结果也是不可预料的。 在信号处理程序中即使调用可重入函数也有问题要注意。作为一个通用的规则,当在信号处理程序中调用可重入函数时,应当在其前保存errno,并在其后恢复errno。(因为每个线程只有一个errno变量,信号处理函数可能会修改其值,要了解经常被捕捉到的信号是SIGCHLD,其信号处理程序通常要调用一种wait函数,而各种wait函数都能改变errno。)

可重入函数列表:

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

书上关于信号处理程序中调用不可重入函数的例子:

#include <stdlib.h> #include <stdio.h> #include <pwd.h>

static void func(int signo) {     struct passwd *rootptr;     if( ( rootptr = getpwnam( "root" ) ) == NULL )     {         err_sys( "getpwnam error" );     }     signal(SIGALRM,func);     alarm(1); }

int main(int argc, char** argv) {     signal(SIGALRM,func);     alarm(1);     for(;;)     {         if( ( ptr = getpwnam("sar") ) == NULL )         {             err_sys( "getpwnam error" );         }     }     return 0; }

signal了一个SIGALRM,而后设置一个定时器,在for函数运行期间的某个时刻,也许就是在getpwnam函数运行期间,相应信号发生中断,进入信号处理函数func,在运行func期间又收到alarm发出的信号,getpwnam可能再次中断,这样就很容易发生不可预料的问题。

=================================================================================

不可重入函数不可以在它还没有返回就再次被调用。例如printf,malloc,free等都是不可重入函数。因为中断可能在任何时候发生,例如在printf执行过程中,因此不能在中断处理函数里调用printf,否则printf将会被重入。 
函数不可重入大多数是因为在函数中引用了全局变量。例如,printf会引用全局变量stdout,malloc,free会引用全局的内存分配表。

个人理解:如果中断发生的时候,当运行到printf的时候,假设发生了中断嵌套,而此时stdout资源被占用,所以第二个中断printf等待第一个中断的stdout资源释放,第一个中断等待第二个中断返回,造成了死锁,不知这样理解对不对。

不可重入函数指的是该函数在被调用还没有结束以前,再次被调用可能会产生错误。可重入函数不存在这样的问题。 不可重入函数在实现时候通常使用了全局的资源,在多线程的环境下,如果没有很好的处理数据保护和互斥访问,就会发生错误。 常见的不可重入函数有: printf --------引用全局变量stdout malloc --------全局内存分配表 free    --------全局内存分配表 在unix里面通常都有加上_r后缀的同名可重入函数版本。如果实在没有,不妨在可预见的发生错误的地方尝试加上保护锁同步机制等等。

时间: 2024-10-14 05:26:46

可重入和不可重入的相关文章

C语言之可重入函数 &amp;&amp; 不可重入函数

可重入函数 在 实时系统的设计中,经常会出现多个任务调用同一个函数的情况.如果这个函数不幸被设计成为不可重入的函数的话,那么不同任务调用这个函数时可能修改其他任 务调用这个函数的数据,从而导致不可预料的后果.那么什么是可重入函数呢?所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会 出错.不可重入函数在实时系统设计中被视为不安全函数. 满足下列条件的函数多数是不可重入的: (1)函数体内使用了静态的数据结构: (2)函数体内调用了malloc()或者free()函数: (

[学习笔记]信号处理函数遇上可重入和不可重入函数

可重入函数概念 q  为了增强程序的稳定性,在信号处理函数中应使用可重入函数. q  所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错.因为进程在收到信号后,就将跳转到信号处理函数去接着执行.如果信号处理函数中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函数中返回接着执行时,可能会出现不可预料的后果.不可再入函数在信号处理函数中被视为不安全函数. q  满足下列条件的函数多数是不可再入的:(1)使用静态的数据结构

登入ssh不用輸入密碼

最近在用rsync從ssh通道處理sync的工作,然而ssh是需要經過一道密碼輸入才能進行rsync ,而且也放入cron自動執行,找不到人輸入密碼它會哭哭的 ~ 利用ssh-keygen產生密鑰 id_rsa, id_rsa.pub $ssh-keygen -t rsa 或 ssh-keygen -d (dsa) 將產生完的 id_rsa.pub 用scp傳送至對方的主機.(這過程還是需要密碼) $scp id_rsa.pub server_hostname:~/.ssh/ 連到對方的主機 $

OSPF的rip、ospf、静态重分发及动态重分发配置

动态路由OSPF的rip.ospf.静态重分发及动态重分发实验 实验拓扑如下: R1 ip route 0.0.0.0 0.0.0.0 192.168.20.2 R2 ip route 192.168.10.0 255.255.255.0 192.168.20.1 router ospf 1 network 192.168.30.0 0.0.0.255 area 1 //宣告30网段 redistribute static subnets //重分发静态路由 R3 ip route 0.0.0.

安卓切换界面从顶部滑入,右侧滑入

安卓切换界面从顶部滑入: 我们用到了overridePendingTransition这个方法, 第一个参数指定启动的 activity进入的方式,第二个参数指定当前activity退出的方式 overridePendingTransition(R.anim.ani_top_get_into, R.anim.ani_bottom_sign_out); R.anim.ani_top_get_into文件: <translate xmlns:android="http://schemas.an

人员登入5---实现登入

有了前面做够的准备,我们现在可以来做登入了. 首先打开我们的login.js,在登入按钮事件那里写上 function login()    {        //用户登入方法---表单提交        if(formPanel.form.isValid()){                    Ext.MessageBox.show({                        title:"请等待",                        msg:"正在

linux系统停留在登入界面,登入失败问题

今天一大早打开电脑,发现系统老是登入不了,起初怀疑是不是有人动了我电脑,因为上周五下班就回去了,公司有人加班. 后来发现应该是系统上的问题,才回想起上周五快下班的时候在配置JAVA环境,修改了/etc/profile配置文件,然后想办法进去删掉之前的修改,具体方法如下: 1.在登入界面按:alt+ctrl+f3,在终端输入用户名,密码 2.进入/etc/profile文件,修改或者删除相关配置 3.重启电脑 这样就好了,由于/etc/是系统的配置目录,profile属于系统启动读取的文件,所以出

sqlplus登入和plsql登入的区别

下面是两种登入方式的截图,用sqlplus登入需要输入主机字: 如果是用本机的SQL*Plus连接本机的数据库,则"主机字符串"可以为空. 如果是从远程连接xp的oracle数据库,可用如下方法 1.在windows服务启动oracle相关服务器: 2.在xp的命令行用"sqlplus /nolog"登录后以sysdba连接数据库("conn /as sysdba"): 3.用"select name from v$database;&

求职入职,新入职的小朋友以此为戒

本来要离开这个工作了好几年的地方蛮伤感,真相被揭穿了以后,现在突然感觉有点悲凉,又有几分庆幸.本来以为自己尽心尽力工作几年的单位,同事相处的也不错,技术也有了很多的提升.现在回想一下,感觉自己还蛮傻逼的. 前一段时间看梁文道和陈丹青的老友记对话,其中说到"在某一特定时期XXX宣扬要实现真正的XXX社会"."在第二次世界大战中XXX要创建什么大东亚XXX圈一样"类似于这样的鬼话居然真有人当真趣相信,突然悲从心生.感觉自己就像是在当前场景的那个傻逼. 谈谈各种坑的经历: