Chinaunix首页 | 论坛 | 博客
  • 博客访问: 326625
  • 博文数量: 72
  • 博客积分: 1730
  • 博客等级: 上尉
  • 技术积分: 743
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-27 18:49
文章分类

全部博文(72)

文章存档

2012年(72)

我的朋友

分类: WINDOWS

2012-08-15 13:39:55

可重入
维基百科,自由的百科全书
跳转到: ,

若一个或可以「安全的被(Parallel computing)」,则称其为可重入(reentrant或re-entrant)的。即当该子程序正在时,可以再次进入并执行它(並行執行時,個別的執行結果,都符合設計時的預期)。若一个是可重入的,则该函数:

  • 不能含有静态(全局)非常量数据。
  • 不能返回静态(全局)非常量数据的地址。
  • 只能处理由调用者提供的数据。
  • 不能依赖于资源的锁。
  • 不能调用(call)不可重入的函数(有呼叫(call)到的函數需滿足前述條件)。

多「用户/对象/进程」以及,一般会使得对可重入代码的控制变得复杂。同时,IO代码通常不是可重入的,因为他们依赖于像磁盘这样共享的、单独的(類似中的靜態(Static)、(Global))资源。

可重入性是的关键特性之一。

目录
例子

在以下的代码中,函数f和函数g都不是可重入的。

int g_var = 1; int f() { g_var = g_var + 2; return g_var; } int g() { return f() + 2; }

以上代码中,f使用了 g_var,所以,如果两个线程同时执行它并访问g_var,则返回的结果取决于执行的时间。因此,f不可重入。而g调用了f,所以它也不可重入。

稍作修改后,两个函数都是可重入的:

int f(int i) { return i + 2; } int g(int i) { return f(i) + 2; } 可重入与线程安全的关系

可重入与两个概念都关系到函数处理资源的方式。但是,他们有一定的区别。可重入概念会影响函数的外部接口,而线程安全只关心函数的实现。

  • 大多数情况下,要将不可重入函数改为可重入的,需要修改函数接口,使得所有的数据都通过函数的调用者提供。
  • 要将非线程安全的函数改为线程安全的,则只需要修改函数的实现部分。一般通过加入以保护共享的资源,使之不会被几个线程同时访问。

与 可重入性是两个不同性质的概念:可重入函数未必是线程安全的;线程安全函数未必是可重入的。

例如,一个函数打开某个文件并读入数据。这个函数是可重入的, 因为它的多个实例同时执行不会造成冲突;但它不是线程安全的,因为在它读入文件时可能有别的线程正在修改该文件,为了线程安全必须对文件加“同步锁”。

另 一个例子,函数在它的函数体内部访问共享资源使用了加锁、解锁操作,所以它是线程安全的,但是却不可重入。因为若该函数一个实例运行到已经执行加锁但未执行解锁时被停下来,系统又启动该函数的另外一个实例,则新的实例在加锁处将转入等待。如果该函数是一个中断处理服务,在中断处理时又发生新的中断将导致资 源死锁。

百度百科可重入函数
百科名片

可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。

目录

简介   可重入函数也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括 static),这样的函数就是purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。如 果确实需要访问全局变量(包括static),一定要注意实施互斥手段。可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代 价。注意事项  编写可重入函数时,若使用全局变量,则应通过关中断、(即P、V操作)等手段对其加以保护。   若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。可重入函数示例  假设Exam是int型全局变量,函数Squre_Exam返回Exam平方值。那么如下函数不具有可重入性。   unsigned int example( int para ) {   unsigned int temp;   Exam = para; // (**)   temp = Square_Exam( );   return temp;   }   此函数若被多个进程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使 用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使Exam赋与另一个不同的para值,所以当控制重新回到“temp = Square_Exam( )”后,计算出的temp很可能不是预想中的结果。此函数应如下改进。   unsigned int example( int para ) {   unsigned int temp;   [申请] //(1)   Exam = para;   temp = Square_Exam( );   [释放信号量操作]   return temp;   }   (1)若申请不到“信号量”,说明另外的进程正处于给Exam赋值并计算其平方过程中(即正在使用此信号),本进程必须等待其释放信号后,才可继续执行。若申请到信号,则可继续执行,但其它进程必须等待本进程释放信号量后,才能再使用本信号。   保证函数的可重入性的方法:在写函数时候尽量使用(例如寄存器、堆栈中的变量),对于要使用的全局变量要加以保护(如采取关中断、信号量等方法),这样构成的函数就一定是一个可重入的函数。   VxWorks中采取的可重入的技术有:   * 动态堆栈变量(各子函数有自己独立的堆栈空间)   * 受保护的全局变量和静态变量   * 任务变量不可重入  在的 设计中,经常会出现多个任务调用同一个函数的情况。如果这个函数不幸被设计成为不可重入的函数的话,那么不同任务调用这个函数时可能修改其他任务调用这个 函数的数据,从而导致不可预料的后果。那么什么是可重入函数呢?所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。 不可重入函数在实时系统设计中被视为不安全函数。   满足下列条件的函数多数是不可重入的:
  1) 函数体内使用了静态的;  
 2) 函数体内调用了malloc()或者free()函数;  
 3)
函数体内调用了标准I/O函数。  
 下面举例加以说明。
  A. 可重入函数   
void strcpy
(char *lpszDest, char *lpszSrc)
{   while(*lpszDest++=*lpszSrc++);
  *dest=0;   }   
B. 不可重入函数1  
 charcTemp;//全局变量  
 void SwapChar1(char *lpcX, char *lpcY) {  
 cTemp=*lpcX;  
 *lpcX=*lpcY;  
 lpcY=cTemp;//访问了全局变量   }
  C. 不可重入函数2  
 void SwapChar2(char *lpcX,char *lpcY) {  
 static char cTemp;//静态局部变量  
 cTemp=*lpcX;  
 *lpcX=*lpcY;
  lpcY=cTemp;//使用了静态局部变量   }问题解答
  如何编写可重入的函数?  
 答:在函数体内不访问那些全局变量,不使用静态局部变量,坚持只使用局部变量,写出的函数就将是可重入的。
如果必须访问全局变量,记住利用互斥信号量来保护全局变量。  
 
如何将一个不可重入的函数改写成可重入的函数?  
 答:把一个不可重入函数变成可重入的唯一方法是用可重入规则来重写它。其实很简单,只要遵守了几条很容易理解的规则,那么写出来的函数就是可重入的。   1) 不要使用全局变量。因为别的代码很可能覆盖这些变量值。   2) 在和硬件发生交互的时候,切记执行类似disinterrupt()之类的操作,就是关闭硬件中断。完成交互记得打开中断,在有些系列上,这叫做“进入/退出核心”。   3) 不能调用其它任何不可重入的函数。   4) 谨慎使用堆栈。最好先在使用前先OS_ENTER_KERNAL。   堆栈操作涉及内存分配,稍不留神就会造成益出导致覆盖其他任务的数据,所以,请谨慎使用堆栈!最好别用!很多黑客程序就利用了这一点以便系统执行非法代码从而轻松获得系统控制权。还有一些规则,总之,时刻记住一句话:保证中断是安全的!
   
实例问题:
曾经设计过如下一个函数,在代码检视的时候被提醒有bug,因为这个函数是不可重入的,为什么?  
 unsigned int sum_int( unsigned int base )
{   unsigned int index;  
 static unsigned int sum = 0; // 注意,是static类型
  for (index = 1; index <= base; index++)
  sum += index;   return sum;   }   
所谓的函数是可重入的(也可以说是可预测的),即只要输入数据相同就应产生相同的输出。
这个函 数之所以是不可预测的,就是因为函数中使用了static变量,因为static变量的特征,这样的函数被称为:带“内部存储器”功能的的函数。因此如果 需要一个可重入的函数,一定要避免函数中使用static变量,这种函数中的static变量,使用原则是,能不用尽量不用。   将上面的函数修改为可重入的函数,只要将声明sum变量中的static关键字去掉,变量sum即变为一个auto类型的变量,函数即变为一个可重入的函数。  
当然,有些时候,在函数中是必须要使用static变量的,比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。
阅读(3041) | 评论(1) | 转发(1) |
给主人留下些什么吧!~~

liuyang8907102012-08-20 17:09:58

不错,长见识了。