C语⾔函数重⼊
C语⾔函数重⼊
可重⼊函数:可以被中断的函数,即这个函数执⾏时,可以中断其执⾏,可以由⼀个或多个任务并发使⽤,⽽不⽐担⼼数据错误。
不可重⼊函数(不安全函数)
1. 不能运⾏在多任务环境下,除⾮能保证互斥(使⽤信号量/代码的关键部分禁⽤中断)
2. 是由于使⽤了未受保护的系统资源,如全局变量区,中断向量表等。
可重⼊函数:
1. 没有静态数据结构
2. 不返回指向静态数据的指针
3. 所有函数数据由函数的调⽤者提供
4. 使⽤auto变量,或通过全局变量的拷贝来保护全局变量
5. 若必须访问全局变量,则利⽤互斥信号保护
6. 不调⽤不可重⼊函数
不可重⼊函数有:
如果⼀个函数在重⼊条件下使⽤了未受保护的共享的资源,那么它是不可重⼊的。
1. 函数中使⽤了静态变量,⽆论是全局静态变量还是局部静态变量。
2. 函数返回静态变量
3. 函数中调⽤了不可重⼊函数
4. 函数体内使⽤了静态的数据结构
5. 函数体内调⽤了malloc()或者free()函数
6. 函数体内调⽤了其他标准I/O函数
7. 函数是singleton中的成员函数,⽽且使⽤了不使⽤线程独⽴存储的成员变量
不可重⼊函数改写成可重⼊函数:
1、不使⽤全局变量
molloc函数2、在和硬件发⽣交互时,可能会发⽣中断时,先关闭中断(有些系列叫做“进⼊/退出核⼼”或者⽤
OS_ENTER_KERNAL/OS_EXIT_KERNAL来描述,这是临界区保护)
3、不调⽤不可重⼊函数
4、谨慎使⽤堆栈,最好在使⽤前OS_ENTER_KERNAL
中断是嵌⼊式系统中重要的组成部分,这导致了很多编译开发商提供⼀种扩展—让标准C⽀持中断。中断服务⼦程序ISR:
错误:
1、ISR没有返回值
如果它有返回值,返回给谁,某种中断源产⽣中断,系统使⽤ISR进⾏处理。ISR是链接在某⼀个中断源上的,⽽中断源的产⽣是随机的,所以ISR并没有⼀个固定的调⽤者,也没有固定的返回地址,所以
返回值没有⽤。
2、ISR不能传递参数
3、在许多编译器/处理器中,浮点数是不可重⼊的。有些不允许在ISR中做浮点运算。
4、ISR应该短、有效率的,做浮点数不明智。
5、printf是不可重⼊函数。
解释:
1、浮点数:⼀般浮点运算都是由专门的硬件来完成,举个例⼦假设有个硬件寄存器名字叫做FLOAT,⽤来计算和存放浮点数的中间运算结果。
假设有这么个函数
假如第⼀次执⾏,有个对浮点数操作运算的结果临时存在FLOAT寄存器中,⽽就在这时被中断了,⽽中断函数或者另⼀个进程也调⽤fun函数,这时第⼆次调⽤的fun函数在执⾏的过程中就会破坏第⼀次FLOAT寄存器中的结果,这样当返回到第⼀次fun函数的时候,结果就不正确了。
2、printf函数
引⽤全局变量stdout,这是标准输⼊输出流的⼀个对象
malloc --------全局内存分配表
free --------全局内存分配表
在unix⾥⾯通常都有加上_r后缀的同名可重⼊函数版本
这种情况出现在多任务系统当中,在任务执⾏期间捕捉到信号并对其进⾏处理时,进程正在执⾏的指令序列就被信号处理程序临时中断。如果从信号处理程序返回,则继续执⾏进程断点处的正常指令序列,从重新恢复到断点重新执⾏的过程中,函数所依赖的环境没有发⽣改变,就说这个函数是可重⼊的,反之就是不可重⼊的。
众所周知,在进程中断期间,系统会保存和恢复进程的上下⽂,然⽽恢复的上下⽂仅限于返回地址,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后缀的同名可重⼊函数版本。如果实在没有,不妨在可预见的发⽣错误的地⽅尝试加上保护锁同步机制等等。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论