关于可重入函数

简介: 关于可重入函数

1 前言


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


那什么是不可重入函数呢?


为什么中断处理函数不能直接调用不可重入函数?


怎样写可重入函数?


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


2 什么是不可重入函数?


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


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


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


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


函数体内调用了标准 I/O 函数;


A. 可重入函数


void strcpy(char *lpszDest, char *lpszSrc) 
{
    while(*lpszDest++=*lpszSrc++);///< 使用的局部变量
    *dest=0;
}

B. 不可重入函数1


char cTemp;    ///< 全局变量
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;   ///< 使用了静态局部变量
}

3 为什么中断处理函数不能直接调用不可重入函数?


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


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


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


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


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


4 如何写出可重入的函数?


在函数体内不访问那些全局变量;


如果必须访问全局变量,记住利用互斥信号量来保护全局变量。或者调用该函数前关中断,调用后再开中断;


不使用静态局部变量;


坚持只使用缺省态(auto)局部变量;


在和硬件发生交互的时候,切记关闭硬件中断。完成交互记得打开中断,在有些系列上,这叫做“进入/退出核心”或者用 OS_ENTER_KERNAL/OS_EXIT_KERNAL 来描述;


不能调用任何不可重入的函数;


谨慎使用堆栈。最好先在使用前先 OS_ENTER_KERNAL;


相关文章
|
1月前
|
存储 安全 Linux
【C/C++ 可重入函数与不可重入函数】理解C/C++ 中函数的可重入性以及与线程安全性的关系
【C/C++ 可重入函数与不可重入函数】理解C/C++ 中函数的可重入性以及与线程安全性的关系
36 0
【C/C++ 可重入函数与不可重入函数】理解C/C++ 中函数的可重入性以及与线程安全性的关系
|
9月前
|
存储 缓存 安全
可重入函数与不可重入函数
可重入函数与不可重入函数
106 0
|
7月前
|
安全 Linux 调度
【Linux系统编程】可重入和不可重入函数
【Linux系统编程】可重入和不可重入函数
106 0
|
物联网 Linux 开发者
可重入函数|学习笔记
快速学习可重入函数
76 0
|
物联网 Linux 开发者
线程函数传参|学习笔记
快速学习线程函数传参
96 0
|
存储 安全 C语言
【C语言安全编码之可重入函数】1、可重入函数的基本概念
【C语言安全编码之可重入函数】1、可重入函数的基本概念
155 0
【C语言安全编码之可重入函数】1、可重入函数的基本概念
|
Linux
Linux系统编程-(pthread)线程通信(自旋锁)
自旋锁不管是内核编程,还是应用层编程都会用到;自旋锁和互斥量类似,它不是通过休眠使进程阻塞,而是在获取锁之前一直处于忙等(也就叫自旋)状态。
353 1
|
Linux API
Linux系统编程-(pthread)线程通信(信号量)
信号量的运用环境与互斥锁一样,但是信号量比互斥锁增加灵活,互斥锁只有两个状态(开锁和解锁),而信号量本质上是一个计数器,它内部有一个变量计数信号值,可以保护一个资源可以同时被1个或者2个或者3个线程同时使用,如果信号量的值只是设置1(状态只有0和1),那么和互斥锁就是一样的功能。
493 0
|
Linux
Linux系统编程-(pthread)线程通信(互斥锁)
这篇文章介绍Linux下线程同步与互斥机制--互斥锁,在多线程并发的时候,都会出现多个消费者取数据的情况,这种时候数据都需要进行保护,比如: 火车票售票系统、汽车票售票系统一样,总票数是固定的,但是购票的终端非常多。
400 0
|
监控 Linux
Linux系统编程-(pthread)线程通信(条件变量)
条件变量是线程可用的一种同步机制,条件变量给多个线程提供了一个回合的场所,条件变量和互斥量一起使用,允许线程以无竞争的方式等待特定的条件发生。 条件变量本身是由互斥体保护的,线程在改变条件状态之前必须首先锁住互斥量,其他线程在获取互斥量之前就不会觉察到这种变化,因为互斥量必须锁定之后才改变条件。
146 0