- 打卡等级:以坛为家II
- 打卡总天数:432
- 最近打卡:2025-05-01 16:19:56
荣誉版主
- 积分
- 4263
|
发表于 2023-11-27 12:46:43
|
显示全部楼层
本帖最后由 CosyOS 于 2023-12-1 19:29 编辑
CosyOS-II 最新版 V1.2.0-beta 发布!
中断本地服务(只读访问)的问题,一直以来都是CosyOS的历史遗留问题,未能得到有效的解决。
本次发布新版,就是为了更好的解决相关问题。
CosyOS的中断本地服务主要包括:全局变量的只读访问、接收邮件、接收消息、接收飞信。
对于中断中接收邮件、接收消息、接收飞信,本次未做调整,原有方案不变。
本次升级就是要彻底解决全局变量的访问问题,给用户提供更多的选择。
一个非原子访问的全局变量,在不关闭总中断的前提下,如何实现全局“成功的互斥访问”?CosyOS-II为您提供了解决方案。
这里说“成功的互斥访问”,是为了区别采用信号量、互斥量等方式实现的互斥访问,强调其访问结果一定是成功的,不会失败。
一、任务中访问
对于任务中的访问,无论只读访问、只写访问、读写访问,只需在任务临界区中访问即可实现“成功的互斥访问”。
二、滴答中访问
对于滴答中的访问,无论只读访问、只写访问、读写访问,直接访问即可实现“成功的互斥访问”。
三、中断中访问
对于中断中的访问,针对只读访问、只写访问、读写访问,需做不同的处理。
1、只读访问
对于中断中的只读访问,用户需定义全局变量的副本,并按照指定方式来更新副本。
读全局变量时,需调用 iWhichGVarToRead 来询问:应该读哪一个全局变量?返回0读正本,返回1读副本。
即可实现“成功的互斥访问”。
2、只写访问
对于中断中的只写访问,直接调用相应的API即可实现“成功的互斥访问”。
(1)写全局变 量:iWriteGVar(gv, lv);
(2)写全局数 组:iWriteGAry(gp, lp, size);
(3)写全局字符串:iWriteGStr(gs, ls);
3、读写访问
对于中断中的读写访问,有下述两种方式可供选择:
(1)挂起服务调用:iPendSVC(fp),fp为自定义的函数指针。
(2)中断服务钩子:void pendsv_hook(void)。
关于中断中的读写访问,要想说透还是不太容易的,这里先说一个结论:
(1)在PendSV中,整个中断挂起服务的执行过程是不安全的,在这个过程中最好不要有全局变量的读访问。
因为在这个过程中,有可能会出现临时性的写结果错误,但只要整个过程完成后,其结果便会复写为正确。
这种临时性的写结果错误,仅可能发生在当一个中断连续两次进入间隔时间过短并调用了只写访问(局部写给全局)。
(2)中断服务钩子是在整个中断挂起服务执行完成后被调用,可实现安全的读访问。
具体应用:
(1)一般情况下,全局变量的自运算,可采用挂起服务调用,因为该全局变量通常不会再在其它中断中执行只写访问。
(2)多个全局变量的混合运算或读写,应采用中断服务钩子。
(3)可尽量采用中断服务钩子,以确保实现安全访问。
/*
* CosyOS-II 最新版 V1.2.0 全局变量访问相关API
*/
/* 更新全局变量副本 */
uUpdateCopy_GVar(code)
tUpdateCopy_GVar(code)
/* 读哪一个全局变量?(返回0读正本,返回1读副本)*/
iWhichGVarToRead
/* 写全局变量 */
iWriteGVar(gv, lv)
/* 写全局数组 */
iWriteGAry(gp, lp, size)
/* 写全局字符串 */
iWriteGStr(gs, ls)
/* 挂起服务调用 */
iPendSVC(fp)
/*
* 应用示例
* 有三个非原子访问的全局变量,均会在任务、滴答、中断中执行只读访问、只写访问、自运算。
*/
/* 定义全局变量 */
u32 xdata g_var = 2147483647;
char xdata g_ary[32];
char xdata g_str[32];
/* 定义全局变量的副本 for 中断中的只读访问 */
u32 xdata g_var_copy = 2147483647;
char xdata g_ary_copy[32];
char xdata g_str_copy[32];
/*
* 方案一:智能应用
* 在全局变量钩子中更新所有副本。
*/
任务中:
{
u32 l_var;
char l_ary[32];
char l_str[32];
uEnterCritical; // 进入任务临界区
/* 读全局变量 */
l_var = g_var;
memcpy(l_ary, g_ary, 32);
strcpy(l_str, g_str);
/* 写全局变量 */
g_var = l_var;
memcpy(g_ary, l_ary, 32);
strcpy(g_str, l_str);
/* 全局变量自运算 */
g_var++;
uExitCritical; // 退出任务临界区
uEndTasking;
}
滴答中:
{
u32 l_var;
char l_ary[32];
char l_str[32];
/* 读全局变量 */
l_var = g_var;
memcpy(l_ary, g_ary, 32);
strcpy(l_str, g_str);
/* 写全局变量 */
g_var = l_var;
memcpy(g_ary, l_ary, 32);
strcpy(g_str, l_str);
/* 全局变量自运算 */
g_var++;
}
static void g_var_inc(void) MCUCFG_USING
{
g_var++;
}
中断中:
{
u32 l_var;
char l_ary[32];
char l_str[32];
/* 读全局变量 */
l_var = !iWhichGVarToRead ? g_var : g_var_copy;
memcpy(l_ary, !iWhichGVarToRead ? g_ary : g_ary_copy, 32);
strcpy(l_str, !iWhichGVarToRead ? g_str : g_str_copy);
/* 写全局变量 */
iWriteGVar(g_var, l_var);
iWriteGAry(g_ary, l_ary, 32);
iWriteGStr(g_str, l_str);
/* 全局变量自运算 */
iPendSVC(g_var_inc);
}
/* 全局变量钩子 */// 更新所有副本
void gvar_hook(void) MCUCFG_USING
{
g_var_copy = g_var;
memcpy(g_ary_copy, g_ary, 32);
strcpy(g_str_copy, g_str);
}
/*
* 方案二:立即更新 + 针对更新
* 在任务和滴答中写全局变量以后,立即更新副本;
* 针对中断中有可能会写全局变量,在全局变量钩子中做判断并针对更新副本。
*/
uCreateBin(g_ary_bin) = false; // for针对更新副本
uCreateBin(g_str_bin) = false; // for针对更新副本
任务中:
{
u32 l_var;
char l_ary[32];
char l_str[32];
uEnterCritical; // 进入任务临界区
/* 读全局变量 */
l_var = g_var;
memcpy(l_ary, g_ary, 32);
strcpy(l_str, g_str);
/* 写全局变量 */
g_var = l_var;
uUpdateCopy_GVar(g_var_copy = g_var); // 立即更新副本
memcpy(g_ary, l_ary, 32);
uUpdateCopy_GVar(memcpy(g_ary_copy, g_ary, 32)); // 立即更新副本
strcpy(g_str, l_str);
uUpdateCopy_GVar(strcpy(g_str_copy, g_str)); // 立即更新副本
/* 全局变量自运算 */
g_var++;
uUpdateCopy_GVar(g_var_copy = g_var); // 立即更新副本
uExitCritical; // 退出任务临界区
uEndTasking;
}
滴答中:
{
u32 l_var;
char l_ary[32];
char l_str[32];
/* 读全局变量 */
l_var = g_var;
memcpy(l_ary, g_ary, 32);
strcpy(l_str, g_str);
/* 写全局变量 */
g_var = l_var;
tUpdateCopy_GVar(g_var_copy = g_var); // 立即更新副本
memcpy(g_ary, l_ary, 32);
tUpdateCopy_GVar(memcpy(g_ary_copy, g_ary, 32)); // 立即更新副本
strcpy(g_str, l_str);
tUpdateCopy_GVar(strcpy(g_str_copy, g_str)); // 立即更新副本
/* 全局变量自运算 */
g_var++;
tUpdateCopy_GVar(g_var_copy = g_var); // 立即更新副本
}
static void g_var_inc(void) MCUCFG_USING
{
g_var++;
}
中断中:
{
u32 l_var;
char l_ary[32];
char l_str[32];
/* 读全局变量 */
l_var = !iWhichGVarToRead ? g_var : g_var_copy;
memcpy(l_ary, !iWhichGVarToRead ? g_ary : g_ary_copy, 32);
strcpy(l_str, !iWhichGVarToRead ? g_str : g_str_copy);
/* 写全局变量 */
iWriteGVar(g_var, l_var);
iWriteGAry(g_ary, l_ary, 32);
iGiveBin(g_ary_bin); // for针对更新副本
iWriteGStr(g_str, l_str);
iGiveBin(g_str_bin); // for针对更新副本
/* 全局变量自运算 */
iPendSVC(g_var_inc);
}
/* 全局变量钩子 */// 针对更新副本
void gvar_hook(void) MCUCFG_USING
{
g_var_copy = g_var;
if(g_ary_bin){
g_ary_bin = false;
memcpy(g_ary_copy, g_ary, 32);
}
if(g_str_bin){
g_str_bin = false;
strcpy(g_str_copy, g_str);
}
}
中断中的只读访问
1、要慎重选择上述的副本方式,因为副本会占用更多的内存,更新副本会占用额外的时间,影响系统的实时性。
建议只有少量的、小型的全局变量或数组采用此种方式。
2、其它的全局变量,尤其是大型的数组,应尽量实现逻辑时序控制,使其不会重入;
或采用二值信号量等方式实现互斥访问。
3、如果采用二值信号量、全局临界区等方式来实现互斥访问,那么全局(任务、滴答、中断)都要采用相同的方式来访问同一个全局变量。
4、如果一个非原子访问的全局变量不会在中断中读取,那么事情将变得异常简单。即不用定义副本,也不用更新副本,其它各处访问如同示例中访问即可。
|
|