深入探究RTOS的IPC机制----邮箱

news2024/10/24 22:23:32

阅读引言: 因为将来工作需要, 最近在深入学习OS的内部机制,我把我觉得重要的、核心的东西分享出来, 希望对有需要的人有所帮助, 阅读此文需要读友有RTOS基础, 以及一些操作系统的基础知识, 学习过Linux的最佳, 特别是想RT-Thread适合Linux非常像的, 代码风格、IPC机制等等。

目录

一、RT-Thread中邮箱的特性

二、 邮箱的源码实现

1,邮箱控制块

2, 创建/初始化邮箱

3, 往邮箱中发送邮件

三、邮箱的简单使用


其实想深入的理解OS的内部工作机制, 无非就是涉及到一个问题, OS使用了那些数据结构组织和管理这些内核对象, 从而实现功能的。RTOS就两点链表和定时器

一、RT-Thread中邮箱的特性

消息队列的本质是链表:
 空闲消息块链表:往队列里写入消息时,先从空闲链表中得到消息块;从队列读出消息后,把消息块放入空闲链表
 消息块头部链表:消息写入消息块后,该消息块被放到尾部;从队列里读消息时,从头部读。


使用消息队列可以传递各类大小的消息,它使用memcpy的方式写入消息、读出消息。


如果我们只是传递很小的数据,比如一些数值,可以使用邮箱:它的效率更高。这一点就可以理解为邮箱和消息队列的区别
 

邮箱在内核中的实现使用的数据结构是一个循环缓冲区, 代码再后面展示

邮箱中的每一封邮件,只能容纳4字节内容(对于32位系统,指针大小刚好为4字节);发送邮件的源码如下, 邮箱中邮件的大小定死了。


 邮件的发送通常是非阻塞的,线程、中断都可以发送邮件;也可使用阻塞方式发送;
 邮件的接收通常是阻塞的,取决于邮箱中是否有邮件;
 当一个线程向邮箱发送邮件时, 如果邮箱没满,就把数值写入邮箱中
 如果邮箱满了, 发送线程可以直接返回-RT_EFULL, 也可以挂起一段时间,在挂起的期间,别的线程或中断服务程序读了邮箱,会唤醒挂起的线程。


 当一个线程从邮箱接收邮件时:如果邮箱不为空,就读取邮箱中的数值, 如果邮箱为空:接收线可以直接返回-RT_ETIMOUT, 也可以挂起一段时间,在挂起的期间,别的线程或中断服务程序写了邮箱,会唤醒挂起的线程。

二、 邮箱的源码实现

使用邮箱的流程:创建/初始化邮箱、发送邮件、接收邮件、删除/脱离邮箱。

1,邮箱控制块

2, 创建/初始化邮箱

#ifdef RT_USING_HEAP
/**
 * This function will create a mailbox object from system resource
 *
 * @param name the name of mailbox
 * @param size the size of mailbox, 邮箱中邮件的数量, 每一封邮件4byte
 * @param flag the flag of mailbox, 邮箱采用的等待方式, 优先级, 等待的时间大小
 *
 * @return the created mailbox, RT_NULL on error happen
 */
rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag)   //flag为邮箱等待方式
{
    rt_mailbox_t mb;                            //定义一个邮箱结构体指针

    RT_DEBUG_NOT_IN_INTERRUPT;

    /* allocate object, 为邮箱这样一个内核对象分配内存空间 */
    mb = (rt_mailbox_t)rt_object_allocate(RT_Object_Class_MailBox, name);
    if (mb == RT_NULL)
        return mb;

    /* set parent */
    mb->parent.parent.flag = flag;            //从内核对象的基类继承过来, 初始化成员

    /* initialize ipc object */
    rt_ipc_object_init(&(mb->parent));

    /* initialize mailbox */
    mb->size     = size;

    /* 申请邮件的空间, 根据邮件的数量 * 固定大小(4byte) */
    mb->msg_pool = (rt_ubase_t *)RT_KERNEL_MALLOC(mb->size * sizeof(rt_ubase_t));  //每一封邮件大小4byte
    if (mb->msg_pool == RT_NULL)
    {
        /* delete mailbox object */
        rt_object_delete(&(mb->parent.parent));

        return RT_NULL;
    }
    mb->entry      = 0;
    mb->in_offset  = 0;
    mb->out_offset = 0;

    /* initialize an additional list of sender suspend thread */
    rt_list_init(&(mb->suspend_sender_thread));

    return mb;
}
RTM_EXPORT(rt_mb_create);

前面我们说邮箱的数据结构是一个循环缓存区, 是从源码的这个位置看出来的。

  

3, 往邮箱中发送邮件

/**
 * This function will send a mail to mailbox object. If the mailbox is full,
 * current thread will be suspended until timeout.
 *
 * @param mb the mailbox object
 * @param value the mail
 * @param timeout the waiting time
 *
 * @return the error code
 */
rt_err_t rt_mb_send_wait(rt_mailbox_t mb,    		//往哪一个邮箱中发送邮件
                         rt_ubase_t   value,        //邮件内容
                         rt_int32_t   timeout)		//超时等待时间
{
    struct rt_thread *thread;   		//定义一个线程指针
    register rt_ubase_t temp;		    //unsigned long类型的变量
    rt_uint32_t tick_delta; 			//unsigned int类型的变量

    /* parameter check */
    RT_ASSERT(mb != RT_NULL);
    RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);

    /* initialize delta tick */
    tick_delta = 0;
    /* get current thread */
    thread = rt_thread_self();		//获取自己的线程控制块

    RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mb->parent.parent)));

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();

    /* for non-blocking call, 邮箱中没位置 */
    if (mb->entry == mb->size && timeout == 0)
    {
        rt_hw_interrupt_enable(temp);

        return -RT_EFULL;
    }

    /* mailbox is full 当邮箱中没位置时, 设置了超时时间的情况 */
    while (mb->entry == mb->size)
    {
        /* reset error number in thread */
        thread->error = RT_EOK;

        /* no waiting, return timeout */
        if (timeout == 0)
        {
            /* enable interrupt */
            rt_hw_interrupt_enable(temp);

            return -RT_EFULL;
        }

        RT_DEBUG_IN_THREAD_CONTEXT;
        /* suspend current thread */
        rt_ipc_list_suspend(&(mb->suspend_sender_thread),
                            thread,
                            mb->parent.parent.flag);

        /* has waiting time, start thread timer */
        if (timeout > 0)
        {
            /* get the start tick of timer */
            tick_delta = rt_tick_get();

            RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_send_wait: start timer of thread:%s\n",
                                        thread->name));

            /* reset the timeout of thread timer and start it */
            rt_timer_control(&(thread->thread_timer),
                             RT_TIMER_CTRL_SET_TIME,
                             &timeout);
            rt_timer_start(&(thread->thread_timer));
        }

        /* enable interrupt */
        rt_hw_interrupt_enable(temp);

        /* re-schedule */
        rt_schedule();

        /* resume from suspend state */
        if (thread->error != RT_EOK)
        {
            /* return error */
            return thread->error;
        }

        /* disable interrupt */
        temp = rt_hw_interrupt_disable();

        /* if it's not waiting forever and then re-calculate timeout tick */
        if (timeout > 0)
        {
            tick_delta = rt_tick_get() - tick_delta;
            timeout -= tick_delta;
            if (timeout < 0)
                timeout = 0;
        }
    }

    /* set ptr */
    mb->msg_pool[mb->in_offset] = value;
    /* increase input offset */
    ++ mb->in_offset;
    if (mb->in_offset >= mb->size)
        mb->in_offset = 0;

    if(mb->entry < RT_MB_ENTRY_MAX)
    {
        /* increase message entry */
        mb->entry ++;
    }
    else
    {
        rt_hw_interrupt_enable(temp); /* enable interrupt */
        return -RT_EFULL; /* value overflowed */
    }

    /* resume suspended thread */
    if (!rt_list_isempty(&mb->parent.suspend_thread))
    {
        rt_ipc_list_resume(&(mb->parent.suspend_thread));

        /* enable interrupt */
        rt_hw_interrupt_enable(temp);

        rt_schedule();

        return RT_EOK;
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);

    return RT_EOK;
}
RTM_EXPORT(rt_mb_send_wait);

三、邮箱的简单使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1842729.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SSM+Jsp的书店仓库管理系统

摘要&#xff1a;仓库作为储存货物的核心功能之一&#xff0c;在整个仓储中具有非常重要的作用&#xff0c;是社会物质生产的必要条件。良好的仓库布局环境能够对货物进入下一个环节前的质量起保证作用&#xff0c;能够为货物进入市场作好准备&#xff0c;在设计中我们根据书店…

Vue与SpringSecurity认证整合-06

Vue与SpringSecurity整合 我们要知道springsecurity是一个安全框架,我们在后端的时候没有接触前端,springsecurity引入依赖之后,启动项目会对我们进行拦截,让我们登录,然后我们制定了一个登录页面,也是后端的,我们可以指向我们的登录页面,但是与Vue整合之后,登录页面肯定是在Vu…

【JavaScript复习二】选择结构if和Switch(1)

### []( )2、单分支条件分支语句if (条件表达式) { // 条件为真时&#xff0c;做的事情 } else { // 条件为假时&#xff0c;做的事情 } ### []( )2,、多分支的 if 语句if (条件表达式1) { // 条件1为真时&#xff0c;做的事情} else if (条件表达式2) { // 条件1不满足&…

打造完美启动页:关键策略与设计技巧

启动页&#xff08;Splash Screen&#xff09;设计是指在应用程序启动时&#xff0c;首先展示给用户的界面设计。这个界面通常在应用加载或初始化期间显示&#xff0c;其主要目的是为用户提供一个视觉缓冲&#xff0c;展示品牌标识&#xff0c;并减少用户在等待过程中的焦虑感。…

kafka基础概念

目录 1、kafka简介 2、kafka使用场景 3、kafka基础概念 3.1、消息 3.1.1、消息构成详解 3.1.2、消息存储设计 3.2、topic 3.3、partition 3.4、offset 3.5、replication 3.5.1、replication简介 3.5.2、副本角色 3.5.3、副本类型 3.5.3.1、副本类型简介 3.5.3.2、…

【前端开发工具】VS Code安装和使用

文章目录 一、前言二、下载三、安装四、配置五、使用5.1 导入项目5.2 本地运行项目5.3 修改界面文案&#xff0c;验证效果5.4 添加日志打印5.5 代码调试5.6 代码提交到Git仓库 六、总结 一、前言 本文介绍一下在前端vue项目中&#xff0c;VS Code的安装和配置。 什么是VS Code…

最新版本的MathType软件2024最新和谐绿色版下载

&#x1f31f; 数学公式的美学&#xff0c;MathType全新升级&#xff01; 亲爱的笔记本小达人&#xff0c;你是否曾为在文档中插入美观又准确的数学公式而头疼&#xff1f;今天&#xff0c;我就来给你种草一个神奇的工具 —— 最新版本的MathType软件&#xff01;&#x1f389…

React Native性能优化红宝书

一、React Native介绍 React Native 是Facebook在React.js Conf2015 推出的开源框架&#xff0c;使用React和应用平台的原生功能来构建 Android 和 iOS 应用。通过 React Native&#xff0c;可以使用 JavaScript 来访问移动平台的 API&#xff0c;使用 React 组件来描述 UI 的…

PhotoShop自动生成号码牌文件

1、说明 设计卡牌的时候&#xff0c;遇到自动生成编号&#xff0c;从01500到-02500&#xff0c;一个一个的手写&#xff0c;在存储保存成psd格式的文件&#xff0c;会很耗时。 下面将介绍如何使用ps自动生成psd格式的文件 2、使用excle生成数字 从01500到-02500 第一步&…

华为---VLAN-配置Eth-Trunk链路聚合(三)

6.3 配置Eth-Trunk链路聚合 6.3.1 原理概述 在没有使用Eth-Trunk前&#xff0c;百兆以太网的双绞线在两个互连的网络设备间的带宽仅为100Mbit/s。若想达到更高的数据传输速率&#xff0c;则需要更换传输媒介&#xff0c;使用千兆光纤或升级成为千兆以太网。这样的解决方案成本…

浙江农林大学2024年成人高等继续教育招生简章

浙江农林大学&#xff0c;作为浙江省内享有盛誉的高等学府&#xff0c;一直以其深厚的学术底蕴和严谨的教学态度为广大学子所赞誉。近年来&#xff0c;随着社会对高素质、专业化人才的需求日益增长&#xff0c;成人高等继续教育逐渐成为越来越多人提升自身综合素质、拓宽职业发…

Mybatis save、saveOrUpdate、update的区别

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 1. save方法 Mybatis的save方法用于插入一条新的记录。当数据库中不存在相同的记录时&#xff0c;会执行插入操作&#xff1b;如果已经存在相同的记录&#xff0c;则会抛出异常。 int result sqlSession.insert(&…

SAP 接口-联行号主数据维护接口【MDM->SAP】开发说明书(包括测试样例、程序代码仅作参考,不保证一定可以运行)

接口映射字段 开发通用说明 根据接口传输字段【MDMWHLX 维护类型】=0则调用联行号创建【BAPI_BANK_CREATE】【前台事务码FI01】对联行号进行创建; 根据接口传输字段【MDMWHLX 维护类型】=1时则调用联行号修改【BAPI_BANK_CREATE】【前台事务码FI02】对联行号进行修改; 开发…

易优CMS网站如何打包备份(搬家教程)

易优CMS网站如何打包备份(搬家教程) 1. 搬家前备份数据库 2.删除install_********目录下的install.lock文件 3.再改名install_1540256968 为install 4.删除data untime所有文件夹 5.打包根目录下所有文件&#xff0c;上传空间解压即可安装 注&#xff1a;安装后直接进入后台…

视频二维码怎么设置全屏播放?默认全屏效果的添加技巧

视频做成二维码如何全屏展示呢&#xff1f;现在很多人都会将视频生成二维码后&#xff0c;分享二维码给其他人来扫码查看视频内容&#xff0c;设置视频默认全屏播放可以带来展示更好的效果&#xff0c;那么横版和竖版视频扫码自动全屏播放是如何生成的呢&#xff1f; 视频二维…

嵌入式开发者转战AI大模型,是机遇还是挑战?

前言 在当今日新月异的科技浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;大模型无疑是技术前沿的明星领域&#xff0c;它们以其卓越的性能和广泛的应用前景&#xff0c;吸引了全球范围内的关注。对于嵌入式开发者而言&#xff0c;从熟悉的硬件嵌入式领域转战AI大模型…

【专利】一种光伏加工产品缺陷检测方法

申请号CN202410053862.4公开号&#xff08;公开&#xff09;CN118037635A申请日2024.01.12申请人&#xff08;公开&#xff09;超音速人工智能科技股份有限公司发明人&#xff08;公开&#xff09;张俊峰(总); 叶长春(总); 许春夏 摘要 本发明公开一种光伏加工产品缺陷检测方…

今天碰到一个gitee的严重问题

今天碰到一个gitee的严重问题 今天访问gitee的官网&#xff0c;无法访问… 代码无法提交 接下来 接下来 gitee的客服给我说 不知道哪天会不会代码直接没了 不知道哪天会不会代码直接没了

高效电商数据分析:电商爬虫API与大数据技术的融合应用

一、引言 随着电子商务的迅猛发展和数据量的爆炸式增长&#xff0c;电商数据分析已成为企业决策的关键依据。在竞争激烈的电商市场中&#xff0c;如何高效、准确地获取并分析数据&#xff0c;以洞察市场趋势、优化运营策略、提升用户体验&#xff0c;成为电商企业面临的重要挑…

【机器学习】Transformer框架理论详解和代码实现

1. 引言 1.1.讨论背景 在本文中&#xff0c;我们将深入探讨近两年最具影响力的架构之一&#xff1a;Transformer模型。自从2017年Vaswani等人发表划时代论文《Attention Is All You Need》以来&#xff0c;Transformer架构便在众多领域&#xff0c;尤其是自然语言处理&#x…