可重入函数与不可重入函数介绍

2023-11-02

不可重入函数的定义:

  • 在实时系统的设计中,经常会出现多个任务调用同一个函数的情况。如果有一个函数不幸被设计成为这样:那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。这样的函数是不安全的函数,也叫不可重入函数

  • 不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥 (或者使用信号量,或者在代码的关键部分禁用中断)。

禁用中断原因:不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。我们知道中断时确实保存一些上下文,但是仅限于返回地址,cpu 寄存器等之类的少量上下文,而函数内部使用的诸如全局或静态变量,buffer 等并不在保护之列,所以如果这些值在函数被中断期间发生了改变,那么当函数回到断点继续执行时,其结果就不可预料了。

可重入函数的定义:

  • 这个安全的函数又叫可重入函数,所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。

  • 可重入函数可以在任意时刻被中断, 稍后再继续运行,不会丢失数据。可重入函数要么使用本地变量,要么在使用全局变量时 保护自己的数据。

  • 可重入函数可以允许有该函数的多个副本在运行,由于不同任务使用的是分离的栈,所以不会互相干扰。

  • 它除了使用自己栈上的变量以外不依赖于任何环境(包括 static)

保证函数的可重入性的方法:

  1. 避免使用全局或静态变量,尽量使用局部变量
    全局或静态变量可能被多个线程共享,如果不加控制地访问它们,就会导致不可重入。所以在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量);

  2. 避免使用非线程安全的函数库
    某些函数库可能使用全局或静态变量来存储一些数据,这些数据可能会被多个线程共享。如果这些函数库不是线程安全的,也会导致不可重入。

  3. 避免使用死锁
    在设计不可重入函数时,需要避免出现死锁的情况,即两个或多个线程彼此等待对方释放某些资源的情况。

  4. 使用信号量、互斥量等同步机制或采取关中断
    为了保证不可重入函数的线程安全,我们可以使用信号量、互斥量等同步机制来控制对共享变量或资源的访问。

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

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

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

    • malloc就是一个不可重入函数,如一个进程此时正在执行malloc分配堆空间,此时程序捕捉到信号发生中断,执行信号处理程序中恰好也有一个malloc,这样就会对进程的环境造成破坏,因为malloc通常为它所分配的存储区维护一个链接表,插入执行信号处理函数时,进程可能正在对这张表进行操作,而信号处理函数的调用刚好覆盖了进程的操作,造成错误。
    • 在这里插入图片描述
  3. 函数体内调用了标准 I/O 函数。因为标准I/O库很多实现都以不可重入的方式使用全局数据结构。

  4. 进行了浮点运算,许多的处理器/编译器中,浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现。

  5. 调用printf。

不可重入函数的案例

函数有static变量或者全局变量,则该函数是不可重入函数

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

int g_mysign = 0;
//这个函数会修改全局变量g_mysign的值
void muNEfunc(int value) 
{
    //.....其他处理代码
    g_mysign = value;  
    //.....其他处理代码
}

//信号处理函数
void sig_usr(int signo)
{     

    muNEfunc(22); //因为一些实际需求必须要在sig_user这个信号处理函数里调用muNEfunc

    int myerrno = errno;

    if(signo == SIGUSR1)
    {
        printf("收到了SIGUSR1信号!\n");
    }
    else if(signo == SIGUSR2)
    {
        printf("收到了SIGUSR2信号!\n");
    }
    else
    {
        printf("收到了未捕捉的信号%d!\n",signo);
    }
}

int main(int argc, char *const *argv)
{
    if(signal(SIGUSR1,sig_usr) == SIG_ERR)  //系统函数,参数1:是个信号,参数2:是个函数指针,代表一个针对该信号的捕捉处理函数
    {
        printf("无法捕捉SIGUSR1信号!\n");
    }
    if(signal(SIGUSR2,sig_usr) == SIG_ERR) 
    {
        printf("无法捕捉SIGUSR2信号!\n");
    }
    for(;;)
    {
        sleep(1); //休息1秒
        printf("休息1秒\n");        
        
        /*我们希望在调用muNEfunc函数以后,得到g_mysign的值为15,
        但是,如果在执行完muNEfunc函数以后,突然来了一个信号,这时
        程序执行流程到了信号处理函数当中,而信号处理函数又对muNEfunc
        当中的g_mysign变量的值做出了改变,所以,当信号处理函数执行完毕以后
        再次回到主函数当中时,g_mysign的值便不是我们所希望的了*/
        muNEfunc(15);//调用函数
        printf("g_mysign=%d\n",g_mysign); 
        //拿g_mysign做一些其他用途;
    }
    printf("再见!\n");
    return 0;
}

malloc,free,printf均是不可重入函数(意味不能在中断函数或信号处理函数同时调用)

#include <stdio.h>
#include <stdlib.h> 
#include <unistd.h>
#include <signal.h>

//信号处理函数
void sig_usr(int signo)
{   
    //这里也malloc,这是错用,不可重入函数不能用在信号处理函数中;
    int* p;
    p = (int *) malloc (sizeof(int)); //用了不可重入函数;
    free(p);

    if(signo == SIGUSR1)
    {
        printf("收到了SIGUSR1信号!\n");
    }
    else if(signo == SIGUSR2)
    {
        printf("收到了SIGUSR2信号!\n");
    }
    else
    {
        printf("收到了未捕捉的信号%d!\n",signo);
    }
    
}

int main(int argc, char *const *argv)
{
    /*系统函数,参数1:是个信号,参数2:是个函数指针,代表一个针对该信号的捕捉处理函数*/
    if(signal(SIGUSR1,sig_usr) == SIG_ERR)  
    {
        printf("无法捕捉SIGUSR1信号!\n");
    }
    if(signal(SIGUSR2,sig_usr) == SIG_ERR) 
    {
        printf("无法捕捉SIGUSR2信号!\n");
    }
    for(;;)
    {             
        int* p;
        p = (int *) malloc (sizeof(int));
        free(p);
    }
    printf("再见!\n");
    return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

可重入函数与不可重入函数介绍 的相关文章

  • 如何编写 Hibernate HQL 查询来删除所有“孙子”元素?

    我有学校 里面有团体 里面有学生 我想删除特定学校的所有学生 在 SQL 中我可以编写以下查询 DELETE FROM students1 WHERE students1 group id IN SELECT id FROM group1
  • 无法访问类型的封闭实例。 [复制]

    这个问题在这里已经有答案了 整个代码是 public class ThreadLocalTest ThreadLocal
  • 透明平开窗

    我有一点JWindow上面有一个标志 用户可以将东西拖到上面 我主要在 OS X 上开发我的应用程序 为了获得我使用的透明窗口 setBackground new Color 0 0 0 0 在 Mac 上 这工作得很好 但在 Window
  • 如何让Spring RabbitMQ创建一个新的队列?

    根据我对rabbit mq的 有限 经验 如果您为尚不存在的队列创建新的侦听器 则会自动创建该队列 我正在尝试将 Spring AMQP 项目与rabbit mq 一起使用来设置侦听器 但出现错误 这是我的 xml 配置
  • JavaFX Platform.runLater 的使用以及从不同线程访问 UI

    我有几个问题Platform runLater 我有一个 JavaFX 应用程序类 在这个类中 我运行一个线程 该线程从网络套接字读取数据 现在当我创建一个新的Stage在线程内部 系统抛出异常 JavaFX 事件调度程序线程和我的网络读取
  • 设置 SWT Shell 的默认字体

    有没有办法为整个 Shell 设置默认字体 以便任何新控件都将使用相同的字体 看来现在我必须为我创建的每个控件设置字体 这导致了太多的冗余 默认使用的字体由平台选择 请参阅中的其他信息 类字体 SWT 标准小部件工具包 http book
  • Java:高性能消息传递(单生产者/单消费者)

    我最初问这个问题here https stackoverflow com questions 3367192 java is while true loop in a thread bad whats the alternative 但我意
  • 使用 Android WebViewClient 启用特定 SSL 协议

    我的应用程序使用WebViewClient与服务器建立 SSL 连接 服务器配置为仅接受 TLSv1 1 及以上协议 使用 Android 时 如何检查哪些 SSL 协议是 a 支持的和 b 默认启用的WebViewClient在设备上 如
  • 如何测试调用父类的受保护(不需要的)方法的方法?

    我陷入了一个非常奇怪的情况 我有一些需要测试的特定代码 这里是 public class A The real method of real class is so big that I just don t want to test it
  • Java 唤醒休眠线程

    我阅读了其他帖子 但没有找到我正在寻找的确切答案 所以我希望有人能给出一些澄清 我有一个将运行一段时间的程序 我有一些在后台运行的线程来执行各种任务 为了简单起见 让我们考虑 3 个线程 ThreadA每 10 秒执行一次任务 其中Thre
  • Java 泛型:如何为泛型类型指定类类型?

    我有一个 POJO 指定为 MyClass u where U是泛型类型参数 我正在尝试编写一个接受类引用的实用方法Class u
  • C# 中的协变和逆变

    首先我要说的是 我是一名正在学习 C 编程的 Java 开发人员 因此 我会将我所知道的与我正在学习的进行比较 我已经使用 C 泛型几个小时了 我已经能够在 C 中重现我在 Java 中知道的相同内容 除了几个使用协变和逆变的示例 我正在读
  • Java:使用 Java.util.concurrent 线程访问读取线程串行端口

    我正在尝试编写一个 Java 串行设备驱动程序并想使用 对我来说是新的 java util concurrent包裹 我有一种发送数据包然后等待 ACK 的方法 我打算有炭 接收在不同的线程中运行 如果接收线程收到 ACK 它应该使用发送数
  • 读/写带有特殊字符的.txt文件

    I open Notepad Windows 并写 Some lines with special characters Special 并前往另存为 someFile txt 与Encoding set to UTF 8 在Java中我有
  • Selenium - 等待网络流量

    我们将 Selenium 与 Java API 和一些 Javascript 用户扩展一起使用 我们在应用程序中使用了大量 AJAX 调用 我们的许多测试随机失败 因为有时 AJAX 调用完成得比其他时候慢 因此页面未完全加载 我们通过等待
  • H2 - (相当)长的 INSERT 失败,错误 42000

    H2 内存中 插入 错误 42000 尝试过版本 1 4 196 1 4 197 1 4 199 我还尝试在 H2 服务器 本地 上执行 INSERT 也失败 给出错误的行 抱歉 但出于安全原因 我无法生成更多 INSERT INTO tb
  • 方法签名中带或不带synchronized关键字的方法具有相同的字节码

    对于以下 2 个类 获得相同的 Java 字节码 java版本 java 版本 1 8 0 181 Java TM SE 运行时环境 构建 1 8 0 181 b13 Java HotSpot TM 64 位服务器 VM 内部版本 25 1
  • 将带有 webapp 的 WAR 部署到 Maven 中央存储库是否有意义?

    这样做有意义吗 如果是 我在哪里可以找到使用简单的 Web Hello World 执行此操作的示例 当人们从 Maven 执行 Web 应用程序时 他们会使用 Jetty 来运行它吗 我想 tomcat 太重了 任何帮助将不胜感激 谢谢
  • JMockit - 初始化问题

    当我使用以下测试时 我收到警告 警告 JMockit 是按需初始化的 这可能会导致某些测试失败 请检查文档以获取更好的初始化方法 这是我的测试实现 package test import static mockit Mockit impor
  • 使用 Runtime.getRuntime().exec() 进行重定向不起作用

    我需要从程序执行命令 命令行是可以的 我在终端试了一下 但是在程序中不行 我从我的代码中添加一个副本 File dir new File videos String children dir list if children null Ei

随机推荐

  • 【正点原子STM32连载】 第三十章 PVD电压监控实验 摘自【正点原子】APM32F407最小系统板使用指南

    1 实验平台 正点原子stm32f103战舰开发板V4 2 平台购买地址 https detail tmall com item htm id 609294757420 3 全套实验源码 手册 视频下载地址 http www openedv
  • [951]numpy.concatenate()函数

    numpy concatenate 官方文档 numpy concatenate a1 a2 axis 0 out None 将具有相同结构的array序列结合成一个array axis是拼接方向 0为横轴 1为纵轴 axis 0 拼接方向
  • clickhouse集群安装

    环境介绍 准备四台服务器搭建clickhouse集群 创建两个分片 每个分片一个副本 在任意三台服务器上安装zookeeper 服务 clickhouse集群依赖zookeeper进行服务器之间的数据同步 192 168 1 1 ch1 t
  • 自学python?一般人我还是劝你算了吧

    自我介绍 本人其实属于一枚屌丝 在真正开始学python之前 其实也就是对电脑的认知只限于上个网 玩个office办公软件 废话不多说 我为啥说自学python 一般人我还是劝你算了吧 因为我就是那个不一般的人 基础真的很简单 是个人稍微认
  • Android RxJava:图文详解 变换操作符

    前言 Rxjava 由于其基于事件流的链式调用 逻辑简洁 使用简单的特点 深受各大 Android开发者的欢迎 Github截图 如果还不了解 RxJava 请看文章 Android 这是一篇 清晰 易懂的Rxjava 入门教程 RxJav
  • 黑莓手机时代落幕;阿里巴巴为 Linux 内核调度器提出新概念;清理 Linux 内核“依赖地狱”

    整理 宋彤彤 责编 屠敏 开源吞噬世界的趋势下 借助开源软件 基于开源协议 任何人都可以得到项目的源代码 加以学习 修改 甚至是重新分发 关注 开源日报 一文速览国内外今日的开源大事件吧 一分钟速览新闻点 开源大新闻 黑莓手机时代落幕 阿里
  • 解决异常报错java.sql.SQLException: Value ‘0000-00-00 00:00:00‘ can not be represented as java.sql.Timestam

    今天在执行某个分页查询时 查询MySQL的结果集时遇到如下报错 java sql SQLException Value 0000 00 00 00 00 00 can not be represented as java sql Times
  • SpringBoot 集成PageHelper 分页组件

    第一步 添加依赖
  • 一文读懂如何使用starUML绘制类关系图

    正文 前言 接下来我们画一张比较完整的类图 类间关系有 继承 组合 聚合 依赖 关联 参照的图是 开始画图 如图所示先点击左边Toolbox 中的class 然后在中间白色画布上点击一下 这样就出来了一个类 我们会看到这个类右边有一大堆黑色
  • 嵌入式_Keil (MDK - ARM) 的调试步骤

    目录 1 编译 调试 2 复位 全速运行 3 单步调试 4 逐步调试 5 跳出调试 6 运行到光标处 7 跳转到暂停行 8 调试窗口 首先为什么需要在 MDK 中进行程序的调试呢 在 MDK 中进行程序调试的主要目的是识别和解决程序中的问题
  • kylin随笔

    1 kylin中一个segment是hbase中的一张表 可以通过修改kylin配置来决定将hbase这张表分为几个regin 以此来提高查询的并发度 2 增量日期分区表的合并 每天一个cube构建一个segment 存入hbase一张表
  • 引导微信用户关注公众号(详细步骤加代码)

    实现效果 当用户进入公众号h5页面 弹窗提示 是否要关注公众号 点击关注 跳转到微信关注页面 流程 1 用户在微信公众号端进入h5页面时 前端从后端数据库获取到用户关注状态 2 如果用户未关注 弹窗提示 是否关注公众号 不关注则取消弹窗 关
  • unity--01 个人实践安装与搭建环境

    首先按照网络上的去访问http www unity3d com 但 查找资料 应该访问https unity cn unity分为个人免费版和企业商用收费版 不过我点击下载的时候 并没有发现网络上所说要选择 个人版 压根没这个选项 试试看吧
  • 计算机二级C语言笔记(持续更新)

    C语言二级 第一章 1 1 1 1 1 C程序 程序 连续执行的一条条指令集合 机器语言 由0和1代码够成的二级制指令 源程序 高级语言编写的程序 目标程序 由二级制代码表示的程序 可执行程序 可以直接在操作系统上运行的程序 源程序 c 编
  • Java API

    目录 1 API 1 1API概述 2 String类 2 1String类概述 2 2String类的特点 2 3 String是什么 可以做什么 2 4String是不可变字符串的原因 2 5String类的构造方法 2 6字符串的比较
  • GreenPlum on K8s

    https pgconf in files presentations 2019 01 0103 Greenplum for Kubernetes PGConf India 2019 pdf About the Greenplum Oper
  • React 函数组件中使用hooks中得useState

    useState让函数组件有state 状态 并进行数据的读写操作 1 import React useState from react 引入useState 在函数中使用 const count setCount useState 0 数
  • RabbitMQ 同样的操作一次成功一次失败

    RabbitMQ 是一个功能强大的消息队列系统 广泛应用于分布式系统中 然而 我遇到这样的情况 执行同样的操作 一次成功 一次失败 在本篇博文中 我将探讨这个问题的原因 并提供解决方法 我是在表导出的时候发现的这个问题 因为我的某个设置的值
  • 影像维修工程师专项技能培训

    最近遇到很多咨询的人员都在对医疗行业产生疑惑 新闻报道说很多医院 公司的领导都被查 这样会不会影响设备维修方面 对后期找工作等有没有影响 总不能学好了技术却没有发挥的余地 最近确实是国家整体在对医疗方面做整改 7月21日 国家卫生健康委会同
  • 可重入函数与不可重入函数介绍

    不可重入函数的定义 在实时系统的设计中 经常会出现多个任务调用同一个函数的情况 如果有一个函数不幸被设计成为这样 那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据 从而导致不可预料的后果 这样的函数是不安全的函数 也叫不可重入函