事件驱动框架(五)——框架的实现

2023-11-12

事件驱动框架(五)——框架的实现


说明

这里先描述一下QP的一些策略和源码。
因为某原因这个系列先停更。后面主要是内核介绍。


实现

1.临界区

临界区内每次只准许一个线程(进程)进入,进入后不允许其他线程(进程)进入。因此临界区的代码不可分割。
在嵌入式系统中,保护临界区,就是在进入临界区时锁中断,在从临界区退出时解锁中断。在不支持锁中断的系统中,可采用其他底层操作系统支持的机制。
为了可以做统一,便在头文件中加入宏,后面只需根据对应的芯片特性,修改.h文件中宏对应的进出临界的方式就可以了。

临界种类

1>.第一种是保存恢复通用状态。最通用的临界区实现牵涉到在进入临界区前保存中断状态,在从临界区退出后恢复这个状态。
下为临界区的使用方法:

{
    unsigned int lock_key;    //保存中断状态变量
    . . .
    lock_key = get_int_status();   //从CPU中获得中断状态,并保存到变量
    int_lock();         //中断加锁
    . . .
    /* critical section of code */   //临界区代码
    . . .
    set_int_status(lock_key); //中断解锁
}
#define QF_INT_KEY_TYPE unsigned int
#define QF_INT_LOCK(key_) do { \
    (key_) = get_int_status(); \
    int_lock(); \
    } while (0)
#define QF_INT_UNLOCK(key_) set_int_status(key_)

主要优点是可以嵌套临界区的能力

2>.另一种是无条件解锁。较简单和较快的临界区策略是总是无条件的解锁。

#define QF_INT_LOCK(key_) int_lock()
#define QF_INT_UNLOCK(key_) int_unlock()

“无条件上锁和解锁”策略是简单和快捷的,但是不允许临界区的嵌套,因为从一个临界区退出时中断总是被解锁的,无论在进入中断时中断是否已经被上锁。不能嵌套临界区并不意味着你不能嵌套中断。许多处理器拥有基于优先级的中断控制器,比如在PC 里的Interl 8259A可编程中断控制器,或集成在ARM Cortex-M3 内的嵌套向量中断控制器NVIC 。这类中断控制器在中断到达处理器内核时处理中断优先级和嵌套。因此,你可以安全的在处理器层解锁中断,从而你可以避免在ISR 内部嵌套临界区。

2.主动对象

活动对象=控制线程+事件队列+状态机
主动对象继承了事件处理所提的状态机(也可以是PT协程),同时将事件队列和线程控制封装到一个对象里作为框架控制的基本对象。同时主动对象还进行了封装,留出初始化,构造,开始对象,调度,发送事件,停止线程,事件延时和恢复等接口。

0>.start()函数

QActive_start()函数创立活动对象的线程并提醒框架开始管理活动对象。绝大多数情况下,所有活动对象只是在系统初始化时被启动一次。

//QActive_start()伪函数
void QActive_start(QActive *me,
     uint8_t prio, /* 优先级*/
     QEvent const *qSto[], uint32_t qLen, /* 事件队列*/
     void *stkSto, uint32_t stkSize, /* 任务堆栈*/
     QEvent const *ie) /* 初始化事件*/
{
    me->prio = prio; /* 设立优先级 */
    QF_add(me); /* 将当前活动对象加入到框架中*/
    QF_ACTIVE_INIT_(me, ie); /* 执行活动对象初始化*/

    /*创建事件队列*/
}

3.事件管理

1>.事件队列

事件队列为提供了事件的缓冲,而防止一段时间内发送的事件过多,或有事件未处理完而造成的事件的丢失情况。在活动对象中事件队列需要有单读多写的功能,这就要求事件队列需要有一个合适的互斥机制来应对这种情况。
活动对象一般管理的是一个环形事件缓冲。根据前一章的策略,支持零复制事件队列,所以事件缓冲存放的事件的指针,指针指着事件池中的动态事件,也可以指着静态事件。
事件队列可以由RTOS的消息队列实现,也可以用原生的事件队列实现。
事件的机构定义

typedef struct QEventTag { 
QSignal sig;
uint8_t dynamic_; /* 动态事件编号 */
} QEvent;

dynamic为0保留给静态事件,其他代表内存池编号和累计数大小。

/**************对于事件池的分配至今很不清楚******************************/

对动态事件的事件池的初始化。一个应用程序可能最多调用这个函数3次去初始化最多3个事件池。为了可能快的事件分配,事件池数组必须根据块尺寸降序排序。

QF_EPOOL_TYPE_ QF_pool_[3]; /* 分配3个事件池*/
uint8_t QF_maxPool_; /* 已初始化事件池*/
/*..........................................................................*/
void QF_poolInit(void *poolSto, uint32_t poolSize, QEventSize evtSize) {
    /* 不执行多于被支持数量的内存池*/
    Q_REQUIRE(QF_maxPool_ < (uint8_t)Q_DIM(QF_pool_));
    /* 应用程序用事件尺寸的递增次序初始化事件池 */
    Q_REQUIRE((QF_maxPool_ == (uint8_t)0)
            || (QF_EPOOL_EVENT_SIZE_(QF_pool_[QF_maxPool_ - 1]) < evtSize));
    /* 执行初始化事件池 */
    QF_EPOOL_INIT_(QF_pool_[QF_maxPool_], poolSto, poolSize, evtSize);
    ++QF_maxPool_; /* 增加事件池*/
}

从事件池中分配事件的策略:

QEvent *QF_new_(QEventSize evtSize, QSignal sig) {
    QEvent *e;
    /* 找到最适合的池ID可以适应事件尺寸 */
    uint8_t idx = (uint8_t)0;
    while (evtSize > QF_EPOOL_EVENT_SIZE_(QF_pool_[idx])) {
        ++idx;
        Q_ASSERT(idx < QF_maxPool_); /* 不能超出初始化池的范围*/
    }
    QF_EPOOL_GET_(QF_pool_[idx], e); /*从找到的池中等到分配事件*/
    Q_ASSERT(e != (QEvent *)0); /* 如果不能正常分配,说明这个事件池已经枯竭*/
    e->sig = sig; /* 设置事件信号量 */
    /* 存储事件的动态属性:
    * 事件池的ID 和计数器为0
    */
    e->dynamic_ = (uint8_t)((idx + 1) << 6);
    return e;
}

垃圾回收:

void QF_gc(QEvent const *e) {
    if (e->dynamic_ != (uint8_t)0) { /* 判断是否静态事件 */
        QF_INT_LOCK_KEY_
        QF_INT_LOCK_();/*加锁*/
        if ((e->dynamic_ & 0x3F) > 1) { /* 是否累计计数器>1 */
            --((QEvent *)e)->dynamic_; /* 递减计算器*/
            QF_INT_UNLOCK_();  /*解锁*/
        }
        else { /* 这里处理最后一次事件的回收*/
            uint8_t idx = (uint8_t)((e->dynamic_ >> 6) - 1); /*得到事件池ID*/
            QF_INT_UNLOCK_();/*解锁*/
            Q_ASSERT(idx < QF_maxPool_); 
            QF_EPOOL_PUT_(QF_pool_[idx], (QEvent *)e); /* 把事件放回池中*/
        }
    }
}

事件延时和恢复

当事件在某个特别不方便的时刻到底时,事件的延迟是很方便的,但是可以被延迟一些时间直到系统有一个比较好的状态去处理这个事件。(将事件延时和恢复也做一个的队列(AO原生)???)下为延时和恢复的实现代码:

/* 事件延时*/
void QActive_defer(QActive *me, QEQueue *eq, QEvent const *e) {
    (void)me; 
    QEQueue_postFIFO(eq, e); /*把延迟的事件发送到给定的“原始”队列,这个事件发送将递增一个
                            动态事件的引用计数器,因此这个事件在当前RTC步骤结束后不会被回收*/
}

/* 事件恢复*/
QEvent const *QActive_recall(QActive *me, QEQueue *eq) {
    QEvent const *e = QEQueue_get(eq); /* 从延时队列中获得事件*/
    if (e != (QEvent *)0) { /* 事件是否有效 */
        QF_INT_LOCK_KEY_
        QActive_postLIFO(me, e); /* LIFO的策略发送给活动对象的事件队列*/
        QF_INT_LOCK_(); /*加锁*/
        if (e->dynamic_ != (uint8_t)0) { /* 是否动态事件 */
        /*在这一刻引用计数器必须最少是2 ,因为事件被最少2 个事件队列引用*/
            Q_ASSERT((e->dynamic_ & 0x3F) > 1);
            --((QEvent *)e)->dynamic_; /* 递减计数器*/
        }
        QF_INT_UNLOCK_();
    }
    return e;
}
2>.事件的派发

直接发送事件
过QActive_postFIFO()和QActive_postLIFO()函数支持直接事件发送

发行- 订阅事件发送
这种策略下的事件派发通过建立一张查找表找到某个事件的全部订阅对象,来快速发送事件。
查找表

在你可以发行任何事件前,需要初始化订阅者查找表。这部分函数的实现代码如下:

typedef struct QSubscrListTag {
    uint8_t bits[((QF_MAX_ACTIVE - 1) / 8) + 1];/*订阅对象列表*/
} QSubscrList;

QSubscrList *QF_subscrList_; 
QSignal QF_maxSignal_;

void QF_psInit(QSubscrList *subscrSto, QSignal maxSignal) {
    QF_subscrList_ = subscrSto;
    QF_maxSignal_ = maxSignal;
}

订阅信号函数(取消订阅过程类似):

void QActive_subscribe(QActive const *me, QSignal sig) {
    uint8_t p = me->prio; /* 获得优先级*/
    uint8_t i = Q_ROM_BYTE(QF_div8Lkup[p]); /*索引i 代表对多字节的字节索引,
                                        方式为QF_div8Lkup[p] = (p – 1)/8*/
    QF_INT_LOCK_KEY_
    Q_REQUIRE(((QSignal)Q_USER_SIG <= sig)
            && (sig < QF_maxSignal_)
            && ((uint8_t)0 < p) && (p <= (uint8_t)QF_MAX_ACTIVE)
            && (QF_active_[p] == me));
    QF_INT_LOCK_();
    QF_subscrList_[sig].bits[i] |= Q_ROM_BYTE(QF_pwr2Lkup[p]);/*在订阅者清单里,对应于活动对象优先级的bit被置位*/
    QF_INT_UNLOCK_();
}

事件发行函数必须确保,在这个事件的所有订阅者收到这个事件前,这个事件不会被某个订阅者回收。

void QF_publish(QEvent const *e) {
    QF_INT_LOCK_KEY_
    /* 确保发送的信号在配置范围内。*/
    Q_REQUIRE(e->sig < QF_maxSignal_);
    QF_INT_LOCK_();
    if (e->dynamic_ != (uint8_t)0) { /* 动态事件 */
        ++((QEvent *)e)->dynamic_; /* 增加计数器 */
    }
    QF_INT_UNLOCK_(); 
    #if (QF_MAX_ACTIVE <= 8)/* 使用条件编译区分单字节*/
    {
        uint8_t tmp = QF_subscrList_[e->sig].bits[0];
        while (tmp != (uint8_t)0) {
            uint8_t p = Q_ROM_BYTE(QF_log2Lkup[tmp]);
            tmp &= Q_ROM_BYTE(QF_invPwr2Lkup[p]); /* 清除subscriber位 */
            Q_ASSERT(QF_active_[p] != (QActive *)0); /* 断言必须被注册 */
            /* 如果队列溢出就使用内部断言 */
            QActive_postFIFO(QF_active_[p], e);
        }
    }
    #else
    {
        uint8_t i = Q_DIM(QF_subscrList_[0].bits);
        do { /* 遍历subscription 列表中的所有字节 */
            uint8_t tmp;
            --i;
            tmp = QF_subscrList_[e->sig].bits[i];
            while (tmp != (uint8_t)0) {
                uint8_t p = Q_ROM_BYTE(QF_log2Lkup[tmp]);
                tmp &= Q_ROM_BYTE(QF_invPwr2Lkup[p]);/* 清除 subscriber 位*/
                p = (uint8_t)(p + (i << 3)); /* 调整优先级*/
                Q_ASSERT(QF_active_[p] != (QActive *)0);/* 断言必须被注册 */
                /* 如果队列溢出就使用内部断言 */
                QActive_postFIFO(QF_active_[p], e);
            }
        } while (i != (uint8_t)0);
    }
    #endif
    QF_gc(e); /* 运行垃圾回收*/
}

4.时钟管理

(这部分的策略不是很了解)
在当前的版本里,时间事件不能是动态的,必须被静态的分配。
在时间事件中用户可自定义给时间事件添加更多成员(也就是自定义的信息)。
下面是时间结构体和操作它的函数:

typedef struct QTimeEvtTag {
    QEvent super; /* 继承时间事件*/
    struct QTimeEvtTag *prev;/* 链接到上一个时间事件*/
    struct QTimeEvtTag *next; /* 链接到下一个时间事件*/
    QActive *act; /* 时间事件的接收对象 */
    QTimeEvtCtr ctr; /* 内部时间递减器 */
    QTimeEvtCtr interval; /* 时钟周期时间 */
} QTimeEvt;

void QTimeEvt_ctor(QTimeEvt *me, QSignal sig);
#define QTimeEvt_postIn(me_, act_, nTicks_) do { \
                    (me_)->interval = (QTimeEvtCtr)0; \
                    QTimeEvt_arm_((me_), (act_), (nTicks_)); \
                    } while (0)

#define QTimeEvt_postEvery(me_, act_, nTicks_) do { \
                    (me_)->interval = (nTicks_); \
                    QTimeEvt_arm_((me_), (act_), (nTicks_)); \
                    } while (0)
uint8_t QTimeEvt_disarm(QTimeEvt *me);
uint8_t QTimeEvt_rearm(QTimeEvt *me, QTimeEvtCtr nTicks);

void QTimeEvt_arm_(QTimeEvt *me, QActive *act, QTimeEvtCtr nTicks);

系统时钟节拍和Tick()函数
为了管理时钟节拍需要加入Tick()函数。系统时钟节拍典型的运行速率是10到100Hz 。
这个函数被设计为可以从中断上下文调用。如果底层OS/RTOS不允许存取中断或者你想让ISR 非常短,也可以从任务层上下文调用。该函数不允许被嵌套和同是调用,应该仅被一个任务调用,理想地情况是被最高优先级任务调用。

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

事件驱动框架(五)——框架的实现 的相关文章

  • windgb调试

    reference http hi baidu com lewutian blog item 191047882b9c399fa5c27261 html 调试前的必备工作 在开始调试前首先要做的工作是设置好符号 Symbols 路径 没有符
  • easy-excel批量导出数据

    easy excel批量导出 前言 最近遇到个用户数据批量导出excel的需求 第一次看到这个需求大家第一时间想到的应该大多都是easy excel这个框架吧 哈哈 我第一时间想到的也是这个框架 但是对于少量的数据 比如有10个用户这样的数
  • Spring源码分析(一):Spring底层核心原理解析

    本节只讲结论 不做验证 后面会专门拉代码讲解验证 Spring的核心是IOC和AOP 大概有这么几个核心知识点 Bean的生命周期底层原理 依赖注入底层原理 初始化底层原理 推断构造方法底层原理 AOP底层原理 Spring事务底层原理 S
  • 解析WINDOWS中的DLL文件---经典DLL解读

    在Windows世界中 有无数块活动的大陆 它们都有一个共同的名字 动态链接库 现在就走进这些神奇的活动大陆 找出它们隐藏已久的秘密吧 初窥门径 Windows的基石 随便打开一个系统目录 一眼望去就能看到很多扩展名DLL的文件 这些就是经
  • SSM三层架构之间整合

    一 前言 之前学习ssm框架的时候都是每个框架独立分散的系统性学习 对于框架的整体 总结效果不太好 后来看了黑马视频教程有个老师的思路给了我比较大的启发 以spring为中心 去整合springmvc mybatis 无论是搭建环境 还是理
  • 框架--SpringWeb

    文章目录 一 springweb 1 概述 2 springWeb层搭建 3 请求中的地址如何定义 4 如何接收请求中的数据 5 直接使用对象接收 6 post请求中文乱码处理 7 Ajax 返回 JSON 8 跨域问题 9 拦截器 10
  • mybatis的熟练运用以及反射知识讲解

    JSP常用设计模式MVC模式 Mybatis mybatis的使用 我们在写项目的时候必定要写DAO 写DAO的时候不难发现对每张表的DAO都差不多 只是sql语句不同 DAO中的每个方法其实也差不多 所以直接用JDBC写DAO是在太麻烦
  • vue中引用cdn中的js文件或者json的用法

    1 现在有一个js文件要放在cdn上 这个js文件的内容如下 var testArr a 1 2 我要在vue项目中使用这个变量 因为这个变量可能是经常变化的 但是不能变化一次就打包一次 所以将他放在cdn上 有使用的话直接改变cdn上的j
  • layui的分页实例详解

    原 layui的分页实例详解 2018年09月20日 17 43 07 李什么泽 阅读数 11571 更多 分类专栏 layui分页 版权声明 本文为博主原创文章 遵循 CC 4 0 BY SA 版权协议 转载请附上原文出处链接和本声明 本
  • SpringMVC:从入门到精通,7篇系列篇带你全面掌握--六.JSON处理和全局异常处理

    Welcome Huihui s Code World 接下来看看由辉辉所写的关于SpringMVC的相关操作吧 目录 Welcome Huihui s Code World 前言 一 SpringMVC中json数据的处理 1 导入相关依
  • AVFoundation 框架小结

    AVFoundation 小结 概述 AVFoundation 是 Objective C 中创建及编辑视听媒体文件的几个框架之一 其提供了检查 创建 编辑或重新编码媒体文件的接口 也使得从设备获取的视频实时数据可操纵 但是 通常情况 简单
  • 架构师--IT策略灵魂的创造者

    http blog csdn net aspop archive 2006 01 21 585823 aspx 在比尔 盖茨的众多称谓中 据说他更偏爱 首席软件架构师 同样 在网易创始人丁磊名字前 也有 首席架构师 这样的称谓 对于企业来说
  • 访谈:小学学历的程序员自主研发出框架级产品

    提到许松森 也许你并不知道他是谁 在Google中敲入这个名字 能找到的结果也寥寥无几 那么做为我们这一期采访的主角 他究竟是用什么在吸引着我们呢 打开许松森的blog 开篇就是 我的悲惨人生 读在字里行间 对他在逆境中的自我成长很是敬佩
  • struts验证框架

    关于validation xml 在validator xml中使用了validation rules xml的验证规则注意一定要加其已经定义好的出错消息考到你的资源包里去 否则出错的时候 显示错误对话框里面却什么错误消息都没有 不允许发生
  • 如何在spring框架中解决多数据源的问题

    在我们的项目中遇到这样一个问题 我们的项目需要连接多个数据库 而且不同的客户在每次访问中根据需要会去访问不同的数据库 我们以往在 spring 和 hibernate 框架中总是配置一个数据源 因而 sessionFactory 的 dat
  • 接口的加密解密

    接口加密 1 接口参数加密 基础加密 2 接口参数加密 接口时效性验证 一般达到这个级别已经非常安全了 3 接口参数加密 时效性验证 私钥 达到这个级别安全性固若金汤 4 接口参数加密 时效性验证 私钥 Https 我把这个级别称之为金钟罩
  • 使用jasypt为springboot配置文件加密

    使用jasypt为配置文件加密 配置项明文可能出现的问题 先看一份典型的配置文件 配置MySQL数据库连接 spring datasource driver class name com mysql jdbc Driver spring d
  • 写需求分析必须牢记的5大要点

    需求验证的5大要点 要做好需求验证 必须在思想 方法 语言 人员 内容5个要点上做好相应的工作 否则就会产生很多负面的影响 1 思想 前面已经说过 由于Review被翻译成 评审 导致很多人将其与中国人常说的评审相混淆 其实它们之间是有区别
  • 值得一看的WinPhone入门系列文章

    今天发现了一些Windows Phone开发入门的系列文章 很适合初学者 下面把其连接总结如下 1 概论 http www winphonecoder com forum php mod viewthread tid 185 extra p
  • 一些大厂的开源平台

    百度 http fex baidu com http efe baidu com 饿了么 https fe ele me 腾讯 http www alloyteam com 美团 https tech meituan com 滴滴 http

随机推荐

  • 贝叶斯做文本分类,代码实现数据处理

    import os import time import random import jieba 处理中文 import nltk 处理英文 import sklearn from sklearn naive bayes import Mu
  • IPV6网络地址

    目录 1 IPV6地址简介 2 IPV6地址与分类 2 1 单播地址 2 2 组播 Multicast 地址 2 3 任意播 Anycast 地址 3 总结 1 IPV6地址简介 地址长度 128bit V4地址只有32bit IP标识方法
  • 【Unity3D】如何在uniyt中切换画布实现切换界面的交互操作

    我们在切换不同界面的时候 时常会用到切换场景的操作 如果在一个场景里就可以去实现切换界面的时候 若再使用切换场景来实现 会占用很大的空间 不妨在一个场景里使用切换画布的方法来实现切换界面的交互操作 1 在unity场景中添加两个画布以及文本
  • 汽车LiDAR的“先行者”——机械式LiDAR

    转自 http www mems me mems system integrator 201711 5547 html http www mems me mems system integrator 201711 5636 html 机械式
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>

    目录 一 工作环境及项目简介 二 原理图设计 1 核心板 电源电路 板对板连接器 复位电路 晶振电路 主控电路 2 底板 串口转USB电路 TF卡电路 WIFI电路 TFT屏幕 音频 板对板连接器 40Pin4 3寸屏幕 三 PCB展示 四
  • [spring处理webservice报文] 1 spring如何搭建webservice服务

    目录 1 背景 2 sping里头搭建webservice 2 1 wsapplication 2 2 futurwebservice 2 3 futurewebserviceimpl 2 4 webserviceconfig 3 soap
  • 画心形图 C#

    public static PointF Random PointF P PointF Center float R float Math Sqrt P X Center X P X Center X P Y Center Y P Y Ce
  • Redis(主从复制、哨兵模式、集群)概述及部署

    Redis主从复制 Redis主从复制的概念 主从复制 是指将一台Redis服务器的数据 复制到其他的Redis服务器 前者称为主节点 Master 后者称为从节点 Slave 数据的复制是单向的 只能由主节点到从节点 默认情况下 每台Re
  • Python练习题——阶乘累计求和

    题目来源 Python语言程序设计 中国大学MOOC 授课老师 嵩天 黄天羽 礼欣 题目描述 获得用户输入的整数n 输出 1 2 n 的值 如果输入数值为0 负数 非数字或非整数 输出提示信息 输入有误 请输入正整数 方法一 factTes
  • vue : 无法加载文件 C:\Users\jianfei\AppData\Roaming\npm\vue.ps1,因为在此系统上禁止运行脚本。...

    背景 在新电脑上配置vue环境 PS E CODE PROJ myvue vue23 P61 使用脚手架 vue test gt npm install g vue cli npm WARN deprecated source map ur
  • python——class类和方法的用法详解

    因为一直不太清楚面向对象的类和方法的编程思想 所以特地补了一下python class的知识 在这里记录和分享一下 文章目录 类和方法的概念和实例 1 python类 class 2 类的构造方法 init 3 类中方法的参数 self 4
  • Redis持久化机制

    目录 Redis的持久化 RDB Redis会在以下几种情况下对数据进行快照 AOF append only file AOF的实现 AOF的重写原理 Redis的持久化 Redis支持两种方式的持久化 一种是RDB方式 另一种是AOF a
  • 一个完整的性能测试流程

    下午逛一个测试交流群时 聊起性能测试 然后某位群成员说他们用的loadrunner做性能 当时觉得这话有点偏颇 虽然我也是一个性能测试道路上的摸索前进者 诚然 我们在进行性能测试工作的过程中 需要借助工具的辅助来帮我们完成一些工作 但loa
  • 计算机存储容量单位读法及换算B、KB、MB、GB、TB、PB、EB、ZB、YB、RB、QB

    1KB 1024B 1MB 1024KB 1024 1024B 1B byte 字节 8 bit 位 比特 1KB Kilobyte 千字节 1024 B 2 10 B 1MB Megabyte 兆字节 百万字节 简称 兆 1024 KB
  • 2023国赛数学建模B题思路代码 - 多波束测线问题

    1 赛题 B 题 多波束测线问题 单波束测深是利用声波在水中的传播特性来测量水体深度的技术 声波在均匀介质中作匀 速直线传播 在不同界面上产生反射 利用这一原理 从测量船换能器垂直向海底发射声波信 号 并记录从声波发射到信号接收的传播时间
  • C++ 中栈对象的使用总结、RAII

    背景 栈区用于存放函数的参数 局部变量 返回值等 栈区的数据由编译器自动进行分配 在作用域内有效 在超出变量作用域后 栈中数据由编译器自动释放 栈内存分配运算内置于处理器的指令集 效率高 但是分配的内存容量有限 栈对象 栈对象在创建时会自动
  • yagmail发送附件

    效果图 经测试代码 导入yagmail第三方库 import yagmail yagmail SMTP user 邮箱名 host SMTP服务器域名 yag yagmail SMTP user 284036658 qq com host
  • 程序员的自我修养——链接,装载与库(一)

    程序员的自我修养 链接 装载与库这本书看了差不多有一个多月了 这本书讲了很多计算机底层的知识 也补充了我的知识盲区 但是感觉看完以后前面有的知识有遗忘 因此就想好好的总结一下 也可以更好的理解这本书 计算机三个最重要的硬件是 中央处理器CP
  • Linux用户与用户组

    Linux目录详解 目录名 说明 bin 重要的二进制 binary 应用程序 包含二进制文件 系统的所有用户使用的命令都在这个目录下 boot 启动 boot 配置文件 包含引导加载程序相关的文件 开机时用到的引导文件 data 数据存储
  • 事件驱动框架(五)——框架的实现

    事件驱动框架 五 框架的实现 说明 这里先描述一下QP的一些策略和源码 因为某原因这个系列先停更 后面主要是内核介绍 实现 1 临界区 临界区内每次只准许一个线程 进程 进入 进入后不允许其他线程 进程 进入 因此临界区的代码不可分割 在嵌