你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

不可重入函数

[复制链接]
gaosmile 发布时间:2020-12-18 18:02
1 前言

最近在公司维护的项目中碰到一个解决了定位很久的 bug , bug 找到的时候发现犯了很低级的错误——在中断处理函数中调用了 printf 函数,因为中断处理函数的调用了不可重入函数,导致中断丢失和系统位置错误,这里直接导致嵌入式 linux 系统应用进程中的所有线程停掉,进而导致看门狗进程得不到喂狗,设备重启。

  • 那什么是不可重入函数呢?
  • 为什么中断处理函数不能直接调用不可重入函数?
  • 怎样写可重入函数?
    1 [) `5 u. |+ _( i; ~9 {3 |

就以上三个问题展开小短文:

3 t+ j( t' P+ C, D! k# u$ t0 r/ j2 |$ G

2 什么是不可重入函数?

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

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

  • 函数体内使用了静态(static)的数据结构;
  • 函数体内调用了 malloc() 或者 free() 函数;
  • 函数体内调用了标准 I/O 函数;5 k8 J9 j8 B: c  H- R* @) J( A
A. 可重入函数void strcpy(char *lpszDest, char *lpszSrc) / b, L! t' m) c  o* b& t( F  G) a
{
- o& Z, D  }$ a$ w+ g, j1 k, a$ n+ v    while(*lpszDest++=*lpszSrc++);///< 使用的局部变量1 H: z3 I$ i8 w, g( H. ?3 v, V
    *dest=0;- K1 {3 Q7 @8 x! p4 _
}
) `: \9 v- @8 ^' F
B. 不可重入函数19 P; K) v9 l! C' ^
char cTemp;    ///< 全局变量
2 Z1 l* V% k- j  B* d- R% tvoid SwapChar1(char *lpcX, char *lpcY)
( Y+ i7 f/ q, {5 s( a3 Y{- K( ^' P  [2 d! E% r
    cTemp=*lpcX;1 f. {  d7 b) s0 [& ]
    *lpcX=*lpcY;
/ i8 C. G& u& C# @( B# }' w" u    lpcY=cTemp;   ///< 访问了全局变量
5 d( ?5 _7 n8 [! b% H' R8 A}
; Y- d9 e7 k; u
C. 不可重入函数2
5 V% ^" t- O, a8 b$ fvoid SwapChar2(char *lpcX,char *lpcY)
* E7 n3 X: M( W$ Y! a{) K& S, [" p2 ?$ E/ T+ Y
    static char cTemp;  ///< 静态局部变量* N- u- b, T. J2 [4 I9 f& o
    cTemp=*lpcX;8 {/ x/ ]" P4 N0 C& G, G% f% u' I
    *lpcX=*lpcY;
$ H& M% s# ?' [    lpcY=cTemp;   ///< 使用了静态局部变量
& \; [# w" ?& g7 X5 }}
0 N, R; |. g  }/ Y% [" L3 S

0 v& C, H" t' \$ Z+ P1 a& o2 f
3 为什么中断处理函数不能直接调用不可重入函数?

在多任务系统下,中断可能在任务执行的任何时间发生;如果一个函数的执行期间被中断后,到重新恢复到断点进行执行的过程中,函数所依赖的环境没有发生改变,那么这个函数就是可重入的,否则就不可重入。

在中断前后不都要保存和恢复上下文吗,怎么会出现函数所依赖的环境发生改变了呢?我们知道中断时确实保存一些上下文,但是仅限于返回地址,cpu 寄存器等之类的少量上下文,而函数内部使用的诸如全局或静态变量,buffer 等并不在保护之列,所以如果这些值在函数被中断期间发生了改变,那么当函数回到断点继续执行时,其结果就不可预料了。

在中断处理函数中调用有互斥锁保护的全局变量,如果恰好该变量正在被另一个线程调用,会导致中断处理函数不能及时返回,导致中断丢失等严重问题。

并且在多线程环境中使用,在没有加锁的情况下,对同一段内存块进行并发读写,就会造成 segmentfault/coredump 之类的问题。

总而言之,中断处理函数做的事情越简单越好。


: a0 N: ^4 A6 B, T7 J5 n  d1 e" a

4 如何写出可重入的函数?
  • 在函数体内不访问那些全局变量;
  • 如果必须访问全局变量,记住利用互斥信号量来保护全局变量。或者调用该函数前关中断,调用后再开中断;
  • 不使用静态局部变量;
  • 坚持只使用缺省态(auto)局部变量;
  • 在和硬件发生交互的时候,切记关闭硬件中断。完成交互记得打开中断,在有些系列上,这叫做“进入/退出核心”或者用 OS_ENTER_KERNAL/OS_EXIT_KERNAL 来描述;
  • 不能调用任何不可重入的函数;
  • 谨慎使用堆栈。最好先在使用前先 OS_ENTER_KERNAL;
    / b+ d9 h/ E5 c. K
+ [: [. T9 G: M. m( V$ I! N3 c1 l7 n

9 x# I9 N! C! q
赞 收藏 评论0 发布时间:2020-12-18 18:02

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版