什么是可重入

  • Post author:
  • Post category:其他



我写博文不一定是按照一个系列来写得:比如今天写得是内核相关的博文,那么我不一定这几天都是写有关内核的博文。因为我不是研究人员,我是工作人员,不是天天研究一个问题。而是今天遇到了什么问题,然后觉得大家工作中可能也会遇到,或者是感觉这个方法有价值,写下来和大家共享还有供自己以后查看的资料。而已。


今天我突然遇到一个问题,是工作中同事随口说的一个问题。我觉得有价值,所以记录下来,供大家参考下。

问题就是:什么是可重入?

其实这个问题在面试的时候也可能会问到。这里先不分什么可重入函数,可重入代码之类的。只先来说下什么是可重入?


提到可重入,首先要

有前提条件:内核环境(内核中常有中断异步发生)或者多进程、多线程,总之是异步环境下。

然后我用我自己的话来解释下

什么是可重入:很简单,就是可以重新再进入。

就是在运行某个函数或者代码时因为某个原因(中断或者抢占资源问题)而中止函数或代码的运行,等到问题解决后,重新进入该函数或者代码继续运行。其结果不会受到影响(和没有被打断时,运行结果一样)。那么这个函数或者代码就称为可重入的,个人为了好记所以理解为:可以再重新进入。


内核中断情况下:用可重入函数来分析下在内核发生中断时运行情况,当CPU执行到该函数内时,突然接到一个中断信号,CPU将对保存现场(把一些参数,返回地址,执行状态之类的保存到堆栈中去),然后去执行中断服务程序;当执行完中断服务程序后,CPU就会还原现场,把一些参数,返回地址,执行状态之类的进行出栈,然后CPU再次进入该函数,从上次执行的地方开始执行,最后得到的结果记为:A。另一种情况下就是在执行该函数时内核没有发生中断,CPU一直都在该函数内运行,最后得到的结果记为:B。那么如果结果A和结果B相等(每一次都要相等,因为一两相等可能是偶然性),就可以说该函数是可重入函数。即:

如果执行结果和没有发生中断时执行得到的结果是一样的就表明该函数是可重入的。(是不管有没有异步事件发生,该函数执行的结果都是一样的才算是可重入函数)。

可重入代码也是一样的。其实应该说明下,上面情况的本质

就是服务子程序能不能修改函数中所要操作的变量,如果能修改的话该函数就不是可重入的

。因为运行函数时,发生中断后,调用中断服务子程序,中断程序修改了函数中接下来要操作的变量数据;等中断程序结束后,函数继续运行,但它没察觉操作的变量其实已经被更改了;那么得到的结果不可能每次都是一样的了。


多进程多线程情况下:用可重入函数来分析下其在多进程环境下的运行情况,假设有2个进程或者线程都可以。当进程1 执行到该函数时,发现缺少了一个执行下去的资源(如果没有该资源,进程就不能执行下去),于是进程就会循环等待其他占有该资源的进程释放(其实就是监听拥有该资源的进程,等待它把资源释放)。就在进程1 循环等待资源释放时,假设有个进程2 在修改进程1 中本来要用来下一步操作的数据。当进程1获取到资源后,会从等待的地方开始继续执行下去,不会检测操作的数据是否改变过,CPU也不知道下一步所要操作的数据其实早就被其他进程做了手脚。所以如果进程2(或者其他进程)能修改函数中的变量,那么该函数就不是可重入函数了。因为你不知道其他进程怎么修改函数中的变量,所以得到的结果也是未知的。上面说到的循环等待资源,其实是为了大家好理解特意扩大了时间段(好让其他进程去修改数据)。在实际中就算进程1 不循环等待资源的释放,其他进程也有可能修改掉函数中的变量。

所以只要函数中的数据能被多个进程或者线程共享,那么这个函数就一定不是可重入函数了。


不可重入会带来不能预见的结果,尤其在内核中,如果操作不当会让系统直接死掉。所以内核代码一定要是可重入的。

代码实现:用简单代码来说明下情况吧(在用户空间,多进程或者多线程环境)


用内核中断情况来分析:

发生中断情况:CPU开始在test1()函数中执行,当执行完第13行时,突然发生中断;CPU就会保存现场,然后去处理中断服务子程序,假设中断服务子程序是test2()函数;中断程序test2()函数中会把global变量自增,现在其实 global = 2 了;当处理完中断程序test2()后,还原现场,CPU重新到test1()函数中第14行继续执行;因为global是全局变量,大家都共享同一个。所以这时候的global其实是等于2了。但CPU不知道global被修改过了,或者说不去管它有没有被修改过。而继续往下执行,得到的结果为: 4

没发生中断情况:没发生中断就好计算了,直接顺序执行到底。最后结果为: 2

所以两种情况下得到的结果不一样,那么说该函数不是可重入的。




用多进程多线程情况来分析:

这里没有用循环等待资源释放,但也能说明问题。首先进程 1 运行到第13行代码时,睡眠10秒(10秒对CPU来说是非常漫长的时间);刚好进程 2运行到第14行,那么最后test1()函数运行的结果为:4; 如果进程1 执行第13行时,进程2还没有执行第12行时,那等进程1醒来执行第14行的时候,进程2 还没有醒来,来不及执行第14行。那test()函数运行的结果为:2;又或者进程2 一直就没被调用过,那结果也是为:2;所以说这是个不能预料到的结果,那么该函数也是不可重入的了。


最后来说下可重入函数(可重入代码也一样)有什么条件:



不能含有静态(全局)非常量数据;


不能返回静态(全局)非常量数据的地址;不能有指向alloc申请的内存变量;


只能处理由调用者提供的数据;


不能调用(


call


)不可重入的函数(有呼叫(


call




)到的函数需满足前述条件)。



其实总结起来就一句话不能有可被其他函数或者代码所共享的数据。所以在写有关可重入函数或者代码时,所有函数内都应该只处理本函数内的数据,而不要去操作共享数据,也不要通过地址去操作其他函数内部的数据。




版权声明:本文为YuZhiHui_No1原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。