AUTOSAR OS模块详解(三) Alarm

AUTOSAR OS模块详解(三) Alarm

本文主要介绍AUTOSAR OS的Alarm,并对基于英飞凌Aurix TC3XX系列芯片的Vector Microsar代码和配置进行部分讲解。

文章目录

  • AUTOSAR OS模块详解(三) Alarm
    • 1 简介
    • 2 功能介绍
      • 2.1 触发原理
      • 2.2 工作类型
      • 2.3 Alarm启动方式
      • 2.4 Alarm配置
      • 2.5 代码分析
        • 2.5.1 主要数据结构
        • 2.5.2 Alarm初始化阶段
        • 2.5.3 Alarm启动阶段
        • 2.5.4 Alarm运行阶段
      • 3 小结

1 简介

在上一篇Autosar OS模块详解中,我们介绍了OS Counter,其作为OS时间Tick管理的重要元素,为OS提供了重要的时间基准,并驱动OS进行时间调度。

而Counter仅为OS注入时间驱动力,却不负责管理OS中各个任务或者其他定时事件,比如我们要设置某个Task的周期为10ms,或者有一些定时事件,比如100ms之后触发某个event,从概念上我们不能直接让Counter管理这些定时业务,毕竟它的职责仅仅是驱动OS,就像发动机仅仅负责动力,而不负责车辆的档位。

因此我们还需要一个中间层,来管理所有的定时业务。在Autosar OS中,这个元素称为Alarm,它就像车辆的变速器,来管理不同时间频率的定时事件。在Autosar OS中,Counter通过累加来驱动Alarm,Alarm根据自身的设定参数周期地激活Task或者触发事件。

在这里插入图片描述

对于不同的周期事件,我们只需要设置多个Alarm,每个Alarm驱动所关联的业务,这样的方式实现了代码的抽象和业务分层,具备良好的架构体系。

2 功能介绍

2.1 触发原理

在Autosar OS中,Alarm顾名思义,就是闹钟,用于实现定时业务。在前面的Counter模块中我们已经介绍了,Counter在每个Tick会累加所有关联的Alarm,然后判断Alarm的状态,如果Alarm到期,则会执行相应的Job,也就是下文会提到的工作类型。

2.2 工作类型

在Autosar OS中,Alarm有4种工作类型,用户可配置不同的类型,并关联不同的Task、Event等。

Os_AlarmActionSetEvent

该工作类型用来设置某个Task中的一个Event,关于Event我们后面会介绍到,它是扩展任务中的关键元素,Event触发后该任务进入就绪状态,并且不同的Event关联不同的Runable。

Os_AlarmActionActivateTask

该工作类型用于激活某个Task,用于基础任务。基础任务是一种不带Event的普通任务,该任务每次运行都需要被激活,运行完成之后会自动Terminate,也就是挂起。

Os_AlarmActionIncrementCounter

前文提到过,并非每个Counter都需要关联到独立的硬件,也可以使用软件Counter。使用软件Counter的时候就是通过其他硬件Counter中的Alarm进行累加,使软件Couter得以产生连续的Tick。

Os_AlarmActionCallback

该工作类型为调用相应的CallBack函数,需要注意的是,这种工作类型只能在SC1下使用。该工作类型使用不多,一般情况下很少会将业务逻辑的执行与OS内部Counter、Alarm等元素进行直接关联,而是通过Task或者Event激活等进行业务逻辑的设置。

2.3 Alarm启动方式

Alarm的启动有两种方式,一种是绝对启动(SetAbsAlarm),另一种是相对启动(SetRelAlarm)。

绝对启动

如果以Counter的值作为一个时间轴的话,绝对启动就是Alarm的触发在Counter的初始时刻,所有通过绝对启动的Alarm的启动时刻相等。在Vecotr Os中,所有的Alarm启动之后下一次Counter就会立即执行一次,因此所有的Alarm会在第一个Tick统一执行。

当存在1ms、5ms、10ms三个Alarm时,如果使用绝对启动,其在Counter Tick的时间轴上的触发关系如下图所示。可以看到当Alarm的最小公倍数Tick数出现时,所有的Alarm都会同时触发,此时系统的瞬态负载较高。

在这里插入图片描述

相对启动

相对启动在设置Alarm启动时,可以添加一个Offset,这个Offset的分辨率是Os的Tick。我们可以通过设置Alarm的Offset将Alarm的触发错开,相应的Task的执行也会因此错开。

同样是1ms、5ms、10ms三个Alarm,我们将10ms的Alarm添加一个2ms的Offset,将5ms的Alarm添加一个1ms的Offset,则其在Counter Tick时间轴上的触发如下图所示,我们可以看到,此时最多两两之间发生碰撞,系统的瞬态峰值负载得到了降低。

在这里插入图片描述

另外值得一提的是,如果Os Tick的分辨率是0.5ms,则可将这三个Alarm通过设置1.5ms、2.5ms的Offset的方式全部错开。但是分辨率增加也意味着Os运行开销的增大,需要根据项目实际情况进行斟酌。

2.4 Alarm配置

其实在Vector工具中,Alarm是不需要配置的,工具会根据Task内部配置的Rbl的周期,自适应生成对应的Alarm,相对启动的Offset也是通过配置Task中Rbl的Offset来实现的。这里我们以一个Bsw任务中的10ms Alarm为示例,介绍Alarm各配置的含义。

在这里插入图片描述

OsAlarmCounterRef

如上一章所述,Counter是Alarm的驱动源,因此每个Alarm都需要关联一个Counter,一般情况下是Alarm所在核内的Counter。

OsAlarmAction

如前所述,Alarm具有四种Action,一般最常使用的是OsAlarmSetEvent和OsAlarmActivateTask。这里由于Bsw Task分配了多个周期的Rbl,属性为Extended Task,因此需要使用SetEvent的形式进行调度。

OsAlarmTask

每个Alarm的动作都需要对应一个Task。

OsAlarmCallbackName

SC1类型的Os中可以使用Os_AlarmActionCallback类型,因此如果是该类型的Alarm这里可以配置Callback函数。

OsAlarmCounter

当Alarm的工作类型为Os_AlarmActionIncrementCounter时,这里就需要配置一个Counter,以实现软件Counter的激活。

OsAlarmEvent

当Alarm的工作类型为Os_AlarmActionSetEvent时,这里配置对应的需要设置的Event即可。

OsAlarmAlarmTime

Alarm的Offset,但是实际上Offset是根据对应Task中分配的Rbl的Offset来计算的,因此这里不用配置。

OsAlarmAutostartType

启动类型,绝对启动还是相对启动,这个一般由工具计算也不需要配置。

OsAlarmCycleTime

Alarm的周期,如果是一次性的Alarm,这里配置为0,但是这项配置其实也是工具自动计算的,不用配置。

OsAlarmAppModeRef

Os的启动是有启动模式的,这里可以配置为:在哪种启动模式下,该Alarm自动启动,即初始化时启动。这项配置一般不用配置,Rte启动时会统一启动所有Alarm。

OsAlarmAccessingApplication

Alarm的访问权限配置,一般情况下Alarm的激活都是由Os来执行,权限也是由Os自动分配,少数比较特别的Alarm用户或者运行时的RTE会操作,因此Os无法识别所有访问源,因此这里有时候需要手动配置权限。如果执行Os服务时报权限访问的错误,一般是这种权限配置的地方有缺失。

在这里插入图片描述

前文提到Alarm可以配置Offset以错开激活时机,在Vector工具中,这是通过Task中实现的。如上图中将所有5ms的Rbl都配置了1ms的Offset,则该5ms的Alarm就会由工具自动计算出Offset。

2.5 代码分析

此处以Rte_Al_TE2_Default_BSW_Async_Task_Core0_1_5ms为例,分析Vector Os中关于Alarm的代码执行逻辑。

2.5.1 主要数据结构

Alarm中的主要数据结构为Os_AlarmSetEventConfigType、Os_AlarmActivateTaskConfigType、Os_AlarmIncrementCounterConfigType和Os_AlarmCallbackConfigType,分别对应Alarm的四种工作类型,此处我们的Alarm工作类型为设置Event,因此我们重点介绍Os_AlarmSetEventConfigType类型。

在Os_Alarm_Lcfg.c文件中我们可以找到配置生成的每个Alarm的数据类型:

/*! Alarm configuration data: Rte_Al_TE2_Default_BSW_Async_Task_Core0_1_5ms */
CONST(Os_AlarmSetEventConfigType, OS_CONST) OsCfg_Alarm_Rte_Al_TE2_Default_BSW_Async_Task_Core0_1_5ms =
{/* .Alarm = */{/* .Job                   = */{/* .Dyn      = */ OS_ALARM_CASTDYN_ALARM_2_JOB(OsCfg_Alarm_Rte_Al_TE2_Default_BSW_Async_Task_Core0_1_5ms_Dyn),/* .Counter  = */ OS_COUNTER_CASTCONFIG_TIMERPFRT_2_COUNTER(OsCfg_Counter_SystemTimer),/* .Callback = */ Os_AlarmActionSetEvent},/* .Autostart             = */{/* .AlarmTime        = */ 0uL, /* 0.0 sec *//* .Cycle            = */ 0uL, /* 0.0 sec *//* .ApplicationModes = */ OS_APPMODE_NONE,/* .AlarmMode        = */ OS_ALARMMODE_ABSOLUTE},/* .AccessingApplications = */ (OS_APPID2MASK(OsApplication_NonTrusted_Core0) | OS_APPID2MASK(OsApplication_NonTrusted_Core1) | OS_APPID2MASK(OsApplication_NonTrusted_Core2) | OS_APPID2MASK(OsApplication_NonTrusted_Core3) | OS_APPID2MASK(SystemApplication_OsCore0) | OS_APPID2MASK(SystemApplication_OsCore1) | OS_APPID2MASK(SystemApplication_OsCore2) | OS_APPID2MASK(SystemApplication_OsCore3)),  /* PRQA S 0410 */ /* MD_MSR_Dir1.1 *//* .OwnerApplication      = */ &OsCfg_App_OsApplication_NonTrusted_Core0},/* .Task  = */ &OsCfg_Task_Default_BSW_Async_Task_Core0,/* .Mask  = */ Rte_Ev_Cyclic2_Default_BSW_Async_Task_Core0_1_5ms
};

我们可以看到,配置生成的数据为OsCfg_Alarm_前缀加上Alarm配置名。 这里我们可以看到所有的配置值都在这个结构体里,我们重点关注其中的.Alarm.job.Dyn,这个指针指向的结构体中有一个ExpirationTimestamp成员,是Alarm下一个需要被激活的Counter Tick值,也就是“闹钟”的设定值。

2.5.2 Alarm初始化阶段

关于Os的初始化,在本系列Counter章节中已经介绍过,这里就不赘述了,与Counter相同,Alarm的初始化也是在Os_AppInit中进行的,其调用栈如下图所示。

在这里插入图片描述

在初始化阶段,仅仅是将Alarm的状态切换为OS_ALARMSTATE_CANCELED,并没有其他操作。

2.5.3 Alarm启动阶段

Alarm的启动是在EcuM的StartupTwo阶段完成的,关于EcuM的启动时序,这里不做过多介绍,后续聊到EcuM模块时再做解读。

StartupTwo运行在Default_Init_Task中,这是一个系统自带的初始化Task,优先级较高。在StartupTwo阶段,有两个地方会启动Alarm,SchM_Init和Rte_Start。SchM_Init中主要初始化EcuM等Bsw模块相关联的Alarm,而Rte_Start则主要初始化用户定义的相关的Rbl的Alarm。其时序图如图所示。

在这里插入图片描述

我们从中可以看到,Os_AlarmSetRelAlarm是最终实施的主要服务函数,我们从该函数解读SetRelAlarm的实现,这里忽略掉其中的错误检查。

OS_FUNC_ATTRIBUTE_DEFINITION(OS_LOCAL_INLINE Os_StatusType, OS_CODE, OS_ALWAYS_INLINE, Os_AlarmSetRelAla
(P2CONST(Os_AlarmConfigType, AUTOMATIC, OS_CONST) Alarm,TickType Increment,TickType Cycle
))
{Os_StatusType status;if(ErrorCheck...){}else{P2VAR(Os_AlarmType, AUTOMATIC, OS_VAR_NOINIT) alarmDyn;alarmDyn = Os_AlarmGetDyn(Alarm);                                                                   /* #20 Set alarm's state to SET. */alarmDyn->State = OS_ALARMSTATE_SET;                                                                /* #30 Set alarm's cycle time to Cycle. */alarmDyn->Cycle = Cycle;                                                                            /* #40 Tell counter to execute alarm's job in Increment ticks. */Os_JobAddRel(&Alarm->Job, Increment);                                                               status = OS_STATUS_OK;}return status;
}

这里alarmDyn的数据指向的是OsCfg_Alarm_Rte_Al_TE2_Default_BSW_Async_Task_Core0_1_5ms_Dyn变量,大家能够很容易注意到其命名是有规律的。

首先,该函数将Alarm的状态切换到OS_ALARMSTATE_SET,表示该Alarm已经启动。

然后设置该Alarm的周期为Cycle,这里我们是5ms的Alarm,周期为5。

然后通过Os_JobAddRel将Alarm添加到其关联的Counter中,这里的Increment就是Offset,如果没有偏移的话Increment为1,表示第一次Counter Tick就会激活该Alarm,这里我们设置了1ms的Offset,因此Increment=2,表示第二次Counter Tick才会激活该Alarm。(Counter的逻辑是先累加再判断,因此第一次工作时Counter Tick值为1)

这里的Os_JobAddRel,也就是Os_CounterAddRelJob,是Counter内部的实现函数,我们观察其代码。

FUNC(void, OS_CODE) Os_CounterAddRelJob
(P2CONST(Os_CounterConfigType, AUTOMATIC, OS_CONST) Counter,P2CONST(Os_JobConfigType, AUTOMATIC, OS_CONST) Job,Os_TickType Offset
)
{Os_TickType now;Os_TickType newExpTime;P2CONST(Os_PriorityQueueConfigType, AUTOMATIC, OS_CONST) jobQueue;jobQueue = &Counter->JobQueue;/* #10 Get the current counter value (now). */now = Os_CounterGetPhysicalValue(Counter);                            /* #20 Set job's expiration time to mod(now + Offset). */newExpTime = Os_TimerAdd(Counter->Characteristics.MaxAllowedValue,Counter->Characteristics.MaxCountingValue,now,Offset);Os_JobSetExpirationTimestamp(Job, newExpTime);                        /* #30 Enqueue the given job in counter's job queue. */Os_PriorityQueueInsert(jobQueue, Job);                                /* #40 If the top element of the queue has changed: */if(Job == Os_PriorityQueueTopGet(jobQueue))                           {/* #50 Update timer's compare value. */Os_CounterSetCompareValue(Counter, newExpTime);                     }
}

首先通过Os_CounterGetPhysicalValue计算出Counter的当前值,一般启动时该值都是0。然后通过Os_TimerAdd结合当前值和Offset,计算“闹钟”的定时时刻ExpirationTimestamp,也就是时间轴上的下一个触发点。这里需要最大值来判断溢出。这里now=0,Offset=2,计算出结果为ExpirationTimestamp=2。

然后通过Os_JobSetExpirationTimestamp函数,将计算出的ExpirationTimestamp设置为超时值,在Counter的中断中,就是通过比较Counter的Tick值和ExpirationTimestamp,来判断该Alarm是否需要激活。

然后通过Os_PriorityQueueInsert来将Alarm插入到Counter的Job队列中,这里使用了完全二叉树进行优先级排序,时间轴上未来最近的一个Alarm具有最高优先级,感兴趣的读者可以自行研究,这里不进行算法层面的探讨。

最后一步是判断是否更新了待激活的Alarm,比如当前Counter待激活的时刻为Tick=5,现在插入的Alarm需要在Tick=2时激活,那么就需要更新Counter的Compare值,因为Counter并不是每一次Timer中断都会工作,而是按需执行。

到这里就完成了Alarm的启动,只需要等待Counter中断去执行了。

2.5.4 Alarm运行阶段

前篇我们提到,在Counter中的Os_CounterWorkJobs函数,会轮询任务队列中的所有Alarm,如果该Alarm的ExpirationTimestamp与Counter Tick的值吻合,则执行该Alarm的Callback,同时将该Alarm从Job队列中取出。这部分逻辑不再赘述,读者如果有疑问可以阅读前篇Counter详解。

这里我们在运行阶段重点介绍Alarm在的Callback中的动作,这是Counter激活Alarm之后由Alarm实施的主要业务。我们的Alarm选取的是5ms的SetEvent,因此其Callback是Os_AlarmActionSetEvent。

在这里插入图片描述

如果是周期Alarm,在Os_CounterReloadJob中,该Alarm会被重载,会根据Counter的当前Tick值加上周期,算出最新的ExpirationTimestamp,以设置下一个周期的触发;如果是一次性Alarm,则在Os_AlarmCancelOrReload中Alarm的状态会被设置为OS_ALARMSTATE_CANCELED,以表示关闭该Alarm。

然后执行Os_EventSetInternal以进行Event的设置,来触发对应Task中的Rbl执行。该部分我们在后面的Event文章内容中进行详细介绍,这里就一概而过。

3 小结

本文承接上篇Counter,介绍了Autosar Os中的Alarm机制,解释了Vector工具中的Alarm配置项,并根据Vector代码详细解读了其初始化、启动、运行等阶段,结合详细的时序图,介绍了其由Counter激活后的执行逻辑。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLO目标检测1

一. 参考资料 《YOLO目标检测》 by 杨建华博士 二. 背景 2.1 目标检测发展简史 2014年,RCNN问世,R-CNN的思路是先使用一个搜索算法从图像中提取出若干感兴趣区域(region of interest,RoI),然后使用一个卷积神经网络(convolutio…

【Qt 常用控件】显示类控件——QLabel

目录 1.QLabel 1.1 textFormat 文本类型 普通文本和富文本 Markdown格式 1.2 alignment 文本对齐方式 1.3 wordWrap 自动换行 1.4 indent 文本缩进 1.5 margin 边距 1.6 buddy,qlabel伙伴 1.7 pixmap图片 和 scaledContents自动填充 1.QLabel 功能&#x…

vif-方差膨胀因子计算

vif-方差膨胀因子 使用statsmodels中的variance_inflation_factor,数据集使用乳腺癌数据集 import pandas as pd import numpy as np from sklearn.datasets import load_breast_cancer from tqdm import notebook from statsmodels.stats.outliers_influence impor…

查看电脑或笔记本CPU的核心数方法及CPU详细信息

一、通过任务管理器查看 1.打开任务管理器 可以按下“Ctrl Shift Esc”组合键,或者按下“Ctrl Alt Delete”组合键后选择“任务管理器”来打开。 2.查看CPU信息 在任务管理器界面中,点击“性能”标签页,找到CPU使用记录区域&#xff0c…

数据恢复常见故障(四)关键信号的耦合电容撞件后导致SATA前端通信异常

数据恢复常见故障(四)关键信号耦合电容撞件后导致SATA前端通信异常 SATA固态硬盘SATA差分信号上有耦合电容,电容被撞件后,偏移,导致接触不良,引起SATA前端信号通信异常,故障现象表现为不认盘&a…

[HCTF 2018]WarmUp

题目&#xff1a;一上来给了个图片还是很懵的&#xff0c;于是尝试查看一下源代码&#xff1a;发现有提示&#xff1a;于是访问source.php得到了php代码&#xff1a;(这里将代码和代码分析放一块) <?phphighlight_file(__FILE__); class emmm{public static function chec…

MYSQL数据库基础-01.数据库的基本操作

数据库的语法是大小写不敏感的&#xff0c;可以使用大写&#xff0c;也可以使用小写。 每条语句要以&#xff1b;结尾&#xff0c;可以多行输入。 名称不能是关键字,若想用关键字命名,要用反引号 引起来。 目录 一.数据库的基本操作 1.创建数据库&#xff1a; 2.查看数据库…

Decode Global专注金融创新,构建便捷的交易平台

随着金融市场持续进阶&#xff0c;越来越多的年轻正在涌入金融交易的体系中&#xff0c;针对当下年轻人崇尚精简&#xff0c;优中取优的特点&#xff0c;Decode Global紧跟金融市场发展的步伐&#xff0c;不断创新体验&#xff0c;致力于打造一个精简快捷&#xff0c;安全便捷的…

系统思考—转型

我们大多数问题的来源是&#xff1a;人们的思考方式与大自然的运作方式之间的差异。——葛雷果利贝特森&#xff08;人类学家、生物学家及系统思考先驱&#xff09; 在企业转型的过程中&#xff0c;许多企业创始人常常面临一个困境——过去的成功经验和旧有的思维方式&#xf…

Flutter:carousel_slider 横向轮播图、垂直轮播公告栏实现

安装依赖 carousel_slider: ^5.0.01、垂直滚动公告栏 import package:carousel_slider/carousel_options.dart;// 垂直滚动公告栏Widget _buildNotice() {return <Widget>[<Widget>[TDImage(assetUrl: "assets/img/home11.png",width: 60.w,height: 60.w…

【二叉树的深搜】计算布尔二叉树的值 求根节点到叶节点数字之和

文章目录 2331. 计算布尔二叉树的值解题思路&#xff1a;后序遍历129. 求根节点到叶节点数字之和解题思路&#xff1a;深度优先搜索 前序遍历 2331. 计算布尔二叉树的值 2331. 计算布尔二叉树的值 给你一棵 完整二叉树 的根&#xff0c;这棵树有以下特征&#xff1a; 叶子节…

【高阶数据结构】布隆过滤器(BloomFilter)

1. 概念 1.1 背景引入 背景&#xff1a;在计算机软件中&#xff0c;一个常见的需求就是 在一个集合中查找一个元素是否存在 &#xff0c;比如&#xff1a;1. Word 等打字软件需要判断用户键入的单词是否在字典中存在 2. 浏览器等网络爬虫程序需要保存一个列表来记录已经遍历过…

偏序关系.

一、偏序&#xff08;半序&#xff09;关系 偏序关系 自反反对称传递性 二、全序&#xff08;线序、链&#xff09;关系 三、偏序集中的重要元素 1. 极大元与极小元 极大元找所在集合的一个或几个最高点&#xff1b; 极小元找所在集合的一个或几个最低点。 2. 最大元与最小…

国产编辑器EverEdit - 列编辑模式

1 列模式 1.1 应用背景 在编辑CSV格式&#xff0c;或者比较规整的配置文件时&#xff0c;可能会用到一列的内容都要进行修改的情况&#xff0c;在不支持列模式的编辑器中&#xff0c;可能需要用户逐行去编辑&#xff0c;比如有下面一段扯淡文本&#xff1a; ADD NRNFREQ:LOCA…

论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(一)

Understanding Diffusion Models: A Unified Perspective&#xff08;一&#xff09; 文章概括引言&#xff1a;生成模型背景&#xff1a;ELBO、VAE 和分层 VAE证据下界&#xff08;Evidence Lower Bound&#xff09;变分自编码器 &#xff08;Variational Autoencoders&#x…

【重庆市乡镇界】面图层shp格式arcgis数据乡镇名称和编码wgs84坐标无偏移内容测评

标题中的“最新重庆市乡镇界面图层shp格式arcgis数据乡镇名称和编码wgs84坐标无偏移最新”指的是一个地理信息系统&#xff08;GIS&#xff09;的数据集&#xff0c;特别设计用于ArcGIS软件。这个数据集包含了重庆市所有乡镇的边界信息&#xff0c;以Shapefile&#xff08;.shp…

【0x04】HCI_Connection_Request事件详解

目录 一、事件概述 二、事件格式及参数 2.1. HCI_Connection_Request 事件格式 2.2. BD_ADDR 2.3. Class_Of_Device 2.4. Link_Type 三、主机响应 3.1. ACL链接类型 3.2. SCO或eSCO链接类型 四、应用场景 4.1. 设备配对场景 4.2. 蓝牙文件传输场景 4.3. 蓝牙物联网…

9. 神经网络(一.神经元模型)

首先&#xff0c;先看一个简化的生物神经元结构&#xff1a; 生物神经元有多种类型&#xff0c;内部也有复杂的结构&#xff0c;但是可以把单个神经元简化为3部分组成&#xff1a; 树突&#xff1a;一个神经元往往有多个树突&#xff0c;用于接收传入的信息。轴突&#xff1a;…

CTTSHOW-WEB入门-爆破25-28

web25 题目&#xff1a;解题思路及步骤&#xff1a;分析代码&#xff1a; error_reporting(0); include("flag.php");//包含文件flag.php if(isset($_GET[r])){$r $_GET[r];//获取参数rmt_srand(hexdec(substr(md5($flag), 0,8)));$rand intval($r)-intval(mt_ra…

win32汇编环境,对多行编辑框添加或删除文本

;运行效果 ;win32汇编环境,对多行编辑框添加或删除文本 ;主要要先设置文本的开始点与结束点&#xff0c;然后把一段文本顶替上去。没有添加文本或删除文本的概念&#xff0c;只有顶替。如果开始点与结束点都是前面文本的长度值&#xff0c;则成了从后面添加文本的效果。如果结束…