C语言之可重入函数 和不可重入函数

 

可重入函数


      在 实时系统的设计中,经常会出现多个任务调用同一个函数的情况。如果这个函数不幸被设计成为不可重入的函数的话,

那么不同任务调用这个函数时可能修改其他任 务调用这个函数的数据,从而导致不可预料的后果。

那么什么是可重入函数呢?所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会 出错。

不可重入函数在实时系统设计中被视为不安全函数。




  满足下列条件的函数多数是不可重入的:


(1)函数体内使用了静态的数据结构;


(2)函数体内调用了malloc()或者free()函数;


(3)函数体内调用了标准I/O函数。




如何写出可重入的函数?在函数体内不访问那些全局变量,不使用静态局部变量,坚持只使用缺省态(auto)局部变量,

写出的函数就将是可重入的。如果必须访问全局变量,记住利用互斥信号量来保护全局变量。

或者调用该函数前关中断,调用后再开中断。


 


可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断

,一段时间以后又可以运行,而相应的数据不会丢失。可重入函数或者只使用局部变量,

即保存在CPU寄存器中或堆栈中;或者使用全局变量,则要对全局变量予以保护。




说法2:




一个可重入的函数简单来说,就是:可以被中断的函数。就是说,你可以在这个函数执行的任何时候中断他的运行,

在任务调度下去执行另外一段代 码而不会出现什么错误。而不可重入的函数由于使用了一些系统资源,比如全局变量区,

中断向量表等等,所以他如果被中断的话,可能出现问题,所以这类函数是 不能运行在多任务环境下的。


基本上下面的函数是不可重入的
(1)函数体内使用了静态的数据结构;
(2)函数体内调用了malloc()或者free()函数;
(3)函数体内调用了标准I/O函数。


把一个不可重入函数变成可重入的唯一方法是用可重入规则来重写他。
其实很简单,只要遵守了几条很容易理解的规则,那么写出来的函数就是可重入的。


第一,不要使用全局变量。因为别的代码很可能覆盖这些变量值。


第二,在和硬件发生交互的时候,切记执行类似disinterrupt()之类的操作,就是关闭硬件中断。完成交互记得打开中断,在有些系列上,这叫做“进入/退出核心”或者用OS_ENTER_KERNAL/OS_EXIT_KERNAL来描述。//这是临界区保护


第三,不能调用任何不可重入的函数。


第四,谨慎使用堆栈。最好先在使用前先OS_ENTER_KERNAL。


还有一些规则,都是很好理解的,总之,时刻记住一句话:保证中断是安全的!


 


 

相信很多人都看过下面这个面试题


中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。

 


 
 
  1. __interrupt double compute_area ( double radius) 
  2. {
  3.     double area = PI * radius * radius;
  4.     printf( "\nArea = %f", area);
  5.     return area;
  6. }



这个函数有太多的错误了,以至让人不知从何说起了:
1)ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。
2) ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。
3) 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,

有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
4) 与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。

不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。
 
 
-->
如果说中断服务程序有返回值,那么它的值返回给谁呢?
在系统的运行过程中,一定是某种中断源出发了相应的中断,系统上挂接的中断服务程序进行现场的处理,例如告警等操作,然后清中断。
也就是说中断服务程序链接在某一类中断源上,而这些中断源的产生是随机的,所以,中断服务程序并没有一个固定的调用者,

也没有固定的返回地址,所以返回值也没有用


我的问题是,这里所说的printf()经常有重入的问题,具体是指什么?有人能给解释一下么?
这个概念在嵌入式操作系统中比较重要,由于存在任务的调度,它实时系统,可剥夺型内核中是危险的,如同一个安静的水雷。

可能会被触发,也可能安然无恙。由于它运行结果的不可预期性,会使系统带来隐患。



下面引用一段别人的解释:


这主要在多任务环境中使用,一个可重入的函数简单来说,就是:可以被中断的函数。就是说,你可以在这个函数执行的任何时候中断他的运行,

在OS的调度下去执行另外一段代码而不会出现什么错误。而不可重入的函数由于使用了一些系统资源,

比如全局变量区,中断向量表等等,所以他如果被中断的话,可能出现问题,所以这类函数是不能运行在多任务环境下的。


把一个不可重入函数变成可重入的唯一方法是用可重入规则来重写他。


其实很简单,只要遵守了几条很容易理解的规则,那么写出来的函数就是可重入的。


第一,不要使用全局变量。因为别的代码很可能覆盖这些变量值。


第二,在和硬件发生交互的时候,切记执行类似disinterrupt()之类的操作,就是关闭硬件中断。完成交互记得打开中断,在有些系列上,

这叫做“进入/退出核心”或者用OS_ENTER_KERNAL/OS_EXIT_KERNAL来描述。


第三,不能调用任何不可重入的函数。


第四,谨慎使用堆栈。最好先在使用前先OS_ENTER_KERNAL。


还有一些规则,都是很好理解的,总之,时刻记住一句话:保证中断是安全的!


通俗的来讲吧:由于中断是可能随时发生的,断点位置也是无法预期的。所以必须保证每个函数都具有不被中断发生,

压栈,转向ISR,弹栈后继续执行影响的稳定性。也就是说具有不会被中断影响的能力。既然有这个要求,

你提供和编写的每个函数就不能拿公共的资源或者是变量来使用,因为该函数使用的同时,ISR(中断服务程序)

也可那会去修改或者是获取这个资源,从而有可能使中断返回之后,这部分公用的资源已经面目全非




满足下列条件的函数多数是不可重入的:


(1)函数体内使用了静态的数据结构;


(2)函数体内调用了malloc()或者free()函数;


(3)函数体内调用了标准I/O函数。




     下面举例加以说明。


可重入函数

 


 
 
  1. void strcpy( char* lpszDest, char* lpszSrc)
  2. {
  3.       while(*lpszDest++ = *lpszSrc++);
  4.      *dest= 0;
  5. }




非可重入函数1

 


 
 
  1. char cTemp;             // 全局变量
  2. void SwapChar1( char* lpcX, char* lpcY)
  3. {
  4.      cTemp = *lpcX; 
  5.      *lpcX = *lpcY; 
  6.      lpcY = cTemp;     // 访问了全局变量,在分享内存的多个线程中可能造成问题
  7. }




非可重入函数2

 


 
 
  1. void SwapChar2( char* lpcX, char* lpcY)
  2. {
  3.       static char cTemp;   // 静态局部变量
  4.      cTemp = *lpcX; 
  5.      *lpcX = *lpcY; 
  6.      lpcY = cTemp;   // 使用了静态局部变量,在分享内存的多个线程中可能造成问题
  7. }




     如何写出可重入的函数?在函数体内不访问那些全局变量,不使用静态局部变量,坚持只使用局部变量,写出的函数就将是可重入的。

如果必须访问全局变量,记住利用互斥信号量来保护全局变量。

 

 

备注:


可重入和线程安全(Thread-Safe)是两个不同的概念:可重入函数一定是线程安全的;线程安全的函数可能是重入的,也可能是不重入的;线程不安全的函数一定是不可重入的。

reentrant函数与是不是多线程无关,如果是reentrant函数,那么要求即使是同一个进程(或线程)同时多次进入该函数时,该函数仍能够正确的运作.
该要求还蕴含着,如果是在多线程环境中,不同的两个线程同时进入该函数时,该函数也能够正确的运作.

thread safe函数是与多线程有关的,它只是要求不同的两个线程同时对该函数的调用在逻辑上是正确的.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本文转自:

http://www.cnblogs.com/AlwaysOnLines/p/3912680.html

https://blog.csdn.net/zyboy2000/article/details/51120771

 

 

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: C语言中的重采样函数是指在音频处理中对音频信号进行采样频率的变换的函数。重采样可以将音频信号的采样率从原始采样率转变为需要的采样率,使得音频信号能够在不同的设备或系统中播放或处理。 重采样函数通常可以通过插值算法来实现,常见的插值算法有最近邻插值、线性插值、样条插值等。重采样函数需要接受原始音频信号的采样率、目标采样率以及音频信号的样本数据作为输入,然后根据采样率之间的比例关系来计算输出音频信号的样本数据。 在实际应用中,重采样函数还可能需要处理抗混叠滤波器的设计和实现,以及可能的音频信号失真问题。为了减小重采样过程中带来的失真,可以选择合适的重采样算法和参数,并通过信号处理的技术手段来减小重采样引起的音质损失。 总的来说,C语言提供了一些库函数和算法可以使用来实现音频信号的重采样。重采样函数的正确实现需要考虑采样率之间的关系、插值算法的选择和抗混叠滤波器的设计。通过合适的参数和算法选择,可以实现高质量的音频重采样,从而适应不同设备或系统的需求。 ### 回答2: 在C语言中,重采样是指将一个信号的采样率变换为另一个采样率的过程。重采样函数是用来实现这一过程的函数。 在下载重采样函数之前,我们需要明确我们的需求和目的。首先,我们应该确认需要重采样的信号是什么类型的信号,例如音频信号、图像信号或者其他类型的信号。其次,我们需要确定目标采样率是多少,这将决定重采样函数的具体实现方式和算法选择。 一种常用的重采样算法是线性插值法。其基本思想是根据原始信号的采样点,通过线性插值计算出需要的新采样点。其他常用的重采样算法还包括最近邻插值法、卷积插值法等,每种算法都有其适用的场景和优势。 有很多开源的重采样函数库可供下载使用,例如libsamplerate库和SoX库。这些库提供了方便易用的函数,能够快速实现重采样功能。下载这些库的头文件和相应的函数库文件后,我们可以在C语言程序中引入这些头文件,并使用其提供的重采样函数来进行重采样操作。 需要注意的是,使用重采样函数应该遵循适当的参数设置和算法选择,以确保重采样过程不引入过多的失真或伪像。此外,对于特定应用场景,还需要根据需求进行性能优化,以提高重采样的效率和准确性。 总而言之,在C语言中下载重采样函数可以通过获取相应的重采样函数库来实现,然后在程序中引入相关头文件并调用相应的函数来进行重采样操作。 ### 回答3: 重采样是指将信号从一个采样率转换为另一个采样率的过程。在C语言中,我们可以通过编写重采样函数来实现这个过程。 首先,我们需要定义输入和输出的采样率,以及输入和输出的信号数据。然后,我们可以使用插值或抽取的方法将输入信号进行重采样。插值方法使用已知的采样点之间的数学模型来估计新的采样点的值,抽取方法则是直接选择已有的采样点作为新的采样点的值。 在C语言中,我们可以使用for循环来遍历输入信号的每个采样点,并根据重采样方法来计算输出信号的采样点。例如,如果使用线性插值方法,我们可以将输出信号的采样点设置为输入信号相邻两个采样点间的线性插值结果。 另外,重采样还需要注意保持输出信号的采样率与输入信号的采样率之间的比率,这可以通过改变输出信号的采样点之间的间隔来实现。 最后,我们可以将重采样函数进行封装,以便在其他程序中调用。这样,我们就可以将特定采样率的输入信号转换为需要的采样率的输出信号。 总之,通过编写C语言中的重采样函数,我们可以实现信号的采样率转换。这需要考虑重采样方法、采样率比率以及输出信号的计算方式等因素。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值