LChanceea8

韬光养晦,厚积薄发。

Linux:可重入函数与不可重入函数

函数是一段载入到内存的代码。函数的代码可长可短,执行时间长度也不确定。在多线程中,线程之间是可以进行切换的。函数是一段写好的代码,属于程序公有的代码段。一个进程中有多个线程,每一个线程都可以调用这段函数代码执行。而在多线程环境中,线程的切换是无法预料的,你不知道下一秒是哪个线程在执行,每时每刻的运行环境都不一样,因为线程切换也是变化莫测的。这是操作系统调度进程线程的范围,不是我们能够掌控的。既然我们无法改变进程调度,无法得知线程切换的规律,那就不要依赖线程的切换。我们的程序要做到,无论线程怎么切换,执行的结果都要一致。而我们该如何才能够达到无论线程如何切换,执行结果都是一致的呢?

重入:重复进入!!!

可重入函数

一个函数在执行的过程中被打断,然后会再被再重头执行一次,执行完后,再回来把刚才没执行完的部分执行完。这就相当于嵌套的执行了。函数是公共代码,这样的执行是允许的。函数的执行可以被打断,打断之后还可以再重头执行,执行完后接着执行刚才没有执行的代码,然后第一次执行的代码(被打断的函数)执行结果还是正确的。也就是说,这个函数执行,无论中间把这个函数再嵌入执行多少遍,怎么嵌入,最终执行完,执行的结果都是正确的,这样的函数就是可重入函数。

我们平常所写的函数,常常都是用的函数内部的局部变量,由于是函数内部的局部变量,那么如果出现了函数在执行的过程中被打断,然后被重头执行,执行完毕后,由于我们使用的都是函数内部的局部变量,而函数执行完毕,会释放在函数内部所创建的局部变量等,这样等我们返回到之前被中断的地方去的时候,就不会影响此时这个函数的内部的变量内容,这样的话,也就是说我们函数为可重入的。

void my_strcpy(*dest, *src)
{
    while(*dest++ = *src++)
    {
        ;
    }
    *dest = '\0';
}

比如,像我们平常所写的这类函数,函数在栈上创建,如果此函数被重入,多次重入时,互相之间并不会影响它们的指针指向。所以在重入时是可以的。

常用的可重入函数的方法有:

1.不要使用全局变量,防止别的代码覆盖这些变量的值。
2.调用这类函数之前先关掉中断,调用完之后马上打开中断。防止函数执行期间被中断进入别的任务执行。
3.使用信号量(互斥条件)。
总而言之:要保证中断是安全的。

不可重入函数

那么不可重入函数显而易见,就是在函数执行期间,被中断,从头执行这个函数,执行完毕后再返回刚才的中断点继续执行,此时由于刚才的中断导致了现在从新在中断点执行时发生了不可预料的错误。那么这类函数就是不可重入函数。

我们知道,在中断函数之后会将中断点的一些数据保存起来,上下文数据等等,那么这些数据不是被保存了吗?怎么还会被影响?其实我们在保存上下文数据的时候,仅仅保存了一小部分,而且那一小部分也只是地址,而对全局变量、静态变量这些并没有保存。所以一旦中断之后,返回到中断点,此时就是不可预料的了。

常见的不可重入函数:

  1. 使用了静态数据结构
  2. 调用了malloc和free等
  3. 调用了标准I/O函数
  4. 进行了浮点运算

malloc与free是不可重入的,它们使用了全局变量来指向堆区。标准I/O大多都使用了全局数据结构。浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现)。

如何避免写出不可重入函数

不使用或者互斥使用全局变量,不使用静态局部变量,只是用局部变量,在函数中动态分配的内存只在本函数中使用,不会传递函数外使用,只要保证局部特性,函数中使用的所有东西都只有局部性,对外不公开,用完即释放,就可以保证可重入。这样,不管怎么重叠,反正本层的函数的东西只有本层能够使用,其他层的函数无法使用,就相当于隔离每一层的变量。这样,不管重叠多少次,都不可能相互影响。这样每一层函数执行的结果都是正确的。


欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!

阅读更多
上一篇数据结构之栈与队列面试题:共享栈(利用一个数组实现两个栈)
下一篇Linux:浅析线程(线程控制)
想对作者说点什么? 我来说一句

可重入函数与不可重入函数

2011年06月17日 1.38MB 下载

没有更多推荐了,返回首页

关闭
关闭