尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

可重入函数和不可重入函数详解

可重入函数和不可重入函数详解 一、核心定义1. 可重入函数Reentrant Function可重入函数是指可以被多个执行流如线程、中断服务程序同时安全调用或在函数未执行完时再次进入直接或间接递归而不会导致数据错乱或未定义行为的函数。简单说无论函数的执行被如何打断、重叠或并发它的运行结果都是正确且可预期的。2. 不可重入函数Non-reentrant Function反之不满足上述安全调用条件的函数。如果在并发、中断嵌套或递归场景下调用不可重入函数会导致数据被意外修改、状态混乱甚至系统崩溃。二、可重入函数的特征一个函数是可重入的通常意味着它在设计上严格满足以下几点不操作任何非栈上的共享数据不使用全局变量、静态局部变量C语言中static修饰的局部变量或动态分配的静态对象。所有数据要么来自调用者传入的参数要么分配在栈上自动变量。不调用任何不可重入函数可重入性具有传递性。一旦内部调用了一个不可重入函数自身也变为不可重入。例如在中断处理函数里直接调用printf是不安全的。不修改自身代码在绝大多数现代体系下这已不是主要问题但早期或某些特殊系统中有自修改代码这种函数显然不可重入。所有数据都由调用者提供保护函数本身不处理锁、互斥量等同步机制这些是线程安全的范畴后文会区分。如果需要保护应由调用者来做函数本身无状态。典型示例// 可重入函数计算两个整数的和intadd(inta,intb){returnab;}// 可重入的字符串拷贝数据缓冲区由调用者提供char*strcpy(char*dest,constchar*src){char*temp_strdest;while(*dest*src);returntemp_str;}三、不可重入函数的常见特征与隐患不可重入函数通常具有以下一种或多种特征使用了全局变量或静态局部变量这是最常见的情况。函数内部维护着跨调用的状态并发调用会破坏这个状态。如果两个线程同时调用counter可能都读到相同的初始值导致最终计数丢失或者产生数据竞争。// 不可重入函数使用了静态局部变量保存状态intcounter(void){staticintcount0;// 全局唯一的数据区returncount;}返回了指向静态数据的指针一些标准库函数为了省去调用者分配内存的麻烦内部使用了静态缓冲区返回结果。如果两个线程分别调用了int_to_str(100)和int_to_str(200)后一次调用会覆盖前一次的内容导致第一个调用者拿到的字符串变成了“200”。典型的不可重入C标准库函数包括asctime、localtime、strtok、gethostbyname等。它们往往有对应的可重入版本后缀_r如localtime_r、strtok_r。// 不可重入函数把数值转换成字符串char*int_to_str(intvalue){staticcharstr[20];// 所有调用共享同一块缓冲区sprintf(str,%d,value);returnstr;}调用了不可重入的标准库函数正如上文提到函数内部若调用了printf、malloc、free等其本身也变得不可重入。 调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。printf/malloc虽然现代版本多数通过锁等机制保证了线程安全不是可重入但在信号处理函数或中断服务程序中仍绝对不能调用因为它们可能导致死锁同一线程重入时尝试获取已持有的锁。所以线程安全不一定可重入但可重入函数一定是线程安全的在没有其他共享资源的前提下。管理共享硬件资源且未保护状态例如一个直接操作硬件端口的函数如果状态跨调用保存在中断嵌套时就会出错。
返回列表