tzz1983 发表于 2023-11-21 16:07:06

多看看你的OS还是很有好处的,STC32出来的时间不长. 就因为和51内核比较像,相当于拿来就会用,所以很少去看编绎手册之类的东西. 比如REMOVEUNUSED,之前我还不知道,我一直苦恼编绎器为什么不把没用的代码自动删除。直到昨天才知道还有这玩意{:titter:},解决了我一大困惑

tzz1983 发表于 2023-11-23 09:55:59

CosyOS大侠, 全局不关中断,是如何互斥访问全局变量的,这里面的原理能给大家讲透吗{:lol:}

比如有一个全局变量,无法元子一次性读写, 现在有4个优先级中断允许读写, 并且任务也可以读写, 不关中断如何保证读写正确?

这两天在看你的OS代码, 本为想从原代码入手去窥察一番, 但发现有些耗时呀,嘿嘿. 自己一下子又想不透, 向你来求助了{:smile:}

我自己想到一个办法分两步实现:
1. 中断写的同时创建一个OS服务,由服务更新一个副本的值用来提供给任务读取.
2. 任务写,写的是任务副本,写完以后做一个元子标识, 只要提供两个副本给任务循环使用, 中断即可正确读到任务写的值.
我说的这个办法, 一个中断对应任务共享还勉强说得过去, 如果是多级中断嵌套访问,那就不行了, 或者说就算能实现, 开销也非常的大, 实际上是得不偿失的.
你究竞是用了什么办法呢?

tzz1983 发表于 2023-11-23 10:24:44

本帖最后由 tzz1983 于 2023-11-23 10:26 编辑

给CosyOS两个建议:

1. 做一个最简范本, 项目内只有必要代码, 比如STC32G设备库这种就暂时不要加入到最简范本.做一个建项目流程, 引导新手一步一步的创建项目,创建第一个任务, 功能尽量简单, 比如只要闪烁某个LED就可.降低门槛引流, 用户只有入门了才有后话.
2. 各OS档都出一份代码讲解,这个任务量比较大, 但对推广是非常有用的. 但凡要用到RTOS的程序员,都不是泛泛之辈, 他们不可能对你的代码不了解就放心使用,但要自己通读那肯定是有困难的.如果做到详细讲解, 最终赢得用户量, 这对你OS的发展非常有帮助, 因为使用者也无形之中成为了你的开发团队中的一员.

CosyOS 发表于 2023-11-23 12:01:29

本帖最后由 CosyOS 于 2023-11-23 12:03 编辑

tzz1983 发表于 2023-11-23 09:55
CosyOS大侠, 全局不关中断,是如何互斥访问全局变量的,这里面的原理能给大家讲透吗

比如有一个全局变量,无 ...
目前,CosyOS对于中断本地服务(只读访问),主要是中断中读全局变量、中断中接收邮件,在使用上还是有一定的限定条件的。
这个问题也是迄今为止仍然困扰我的问题。实际上,当你深入研究、思考所有的访问情况后,你会发现其它的问题都不是问题,只有一个问题不好解决,就是中断中对全局资源的只读访问。
如果我们假定在中断中不会读取一个非原子的全局变量、不会接收邮件,将取得完美的结果。
但对于OS来说,我们又不能做这种假定,必须要允许用户在中断中做相应的读访问。
这几日,我一直在考虑到底该如何解决这个问题。
首先,消息队列是采用的互斥访问(每个队列都有一个二值信号量)。所以,在中断中接收消息会有失败机率(返回NULL)。然而对于消息队列来说,也只能如此。
对于非原子全局变量,我现在考虑如下两种方式:
1、OS不再提供相应的服务。这样也是说的通的,因为全局变量本就是用户对象,非内核对象。用户对非原子的全局变量的访问,可自行处置,如使用二值信号量实现互斥访问,或全局临界区等。
2、我已经想到了一种方法,可以完美解决相关问题,就是如果在中断中读一个非原子的全局变量,则必须创建一个副本,这个副本可以,用户自己创建,也可以OS创建,而后由OS提供相应的服务来实现。但这个方法在实现起来比较繁琐,对于用户来说并不友好,又要占用更多的内存,处理上又需花费更多的时间。


对于邮箱,暂时先不议。



CosyOS 发表于 2023-11-23 12:08:01

本帖最后由 CosyOS 于 2023-11-23 12:09 编辑

我现在正在考虑重新解决中断中的读访问问题,因为现有方式存在着一定的限定条件,这就是不可靠因素,如果用户不注意这些问题,访问就可能会出错。
所以说聪明的方法还是关总中断呢,中断一关,什么问题都没有了。

CosyOS 发表于 2023-11-23 13:35:18

本帖最后由 CosyOS 于 2023-11-23 13:37 编辑


下面是CosyOS当前在中断中读一个全局变量时的注意事项:
当用户在中断中读一个全局变量时,
如果在某个与该中断优先级不同的非最低优先级的中断中会写同一个全局变量,那么仅在下面两种情况下会确保万无一失:
1、在那个中断中,用于写该全局变量的局部变量为常量(不会改变);
2、那个中断的发生频率足够低,可保证在PendSV中最终完成服务(写全局变量)以后,那个中断才会再次发生。

所以说,如果恰巧遇到相应的情况,还是会有读错的机率。

CosyOS 发表于 2023-11-23 14:41:31

本帖最后由 CosyOS 于 2023-11-23 14:52 编辑

实际上,对于一个非原子访问的全局变量来说,问题的关键就在于,不怕任何地方读写,就怕中断中读。
除了在中断中的读访问外,其它任何位置(任务、滴答、中断)中的读写访问都是在“服务层”中执行,操作流不会被打断,全局变量是不会重入的。
只有在中断中的读访问是非常令人讨厌的,因为它必须在本地执行,无法挂起到PendSV中。因为既然要读,就是要用,而且是马上就用,就必须读到啊?
中断是有生命期的,每次中断响应后再返回,本次的生命期就结束了。所以在中断中读,就只能在本地立即读到。
而中断又是动态的,不一定什么时候就会进入,所以,对中断中的读访问的处理是十分艰难的。
OS即然提供相应的服务支持,就要让它能读到,所以不能用一般意义的互斥访问的方法,因为可能会读取失败。
如果实现不了,干脆就不要管了,由用户自己去处理吧。
我也正在考虑提供不同的方案给用户。。。



tzz1983 发表于 2023-11-23 15:37:08

CosyOS 发表于 2023-11-23 14:41
实际上,对于一个非原子访问的全局变量来说,问题的关键就在于,不怕任何地方读写,就怕中断中读。
除了在 ...

在你的介绍文中提到Keil RTX是不关中断的, 没有可借鉴的方法吗

CosyOS 发表于 2023-11-23 15:56:11

tzz1983 发表于 2023-11-23 15:37
在你的介绍文中提到Keil RTX是不关中断的, 没有可借鉴的方法吗
我正在考虑我说的能完美解决这个问题的那个方案,做为选项提供给用户选择。

CosyOS 发表于 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)



/*
* 应用示例
* 有三个非原子访问的全局变量,均会在任务、滴答、中断中执行只读访问、只写访问、自运算。
*/

/* 定义全局变量 */
u32xdata g_var = 2147483647;
char xdata g_ary;
char xdata g_str;

/* 定义全局变量的副本 for 中断中的只读访问 */
u32xdata g_var_copy = 2147483647;
char xdata g_ary_copy;
char xdata g_str_copy;



/*
* 方案一:智能应用
* 在全局变量钩子中更新所有副本。
*/

任务中:
{
      u32l_var;
      char l_ary;
      char l_str;
      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;
}

滴答中:
{
      u32l_var;
      char l_ary;
      char l_str;
      
      /* 读全局变量 */
      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++;
}

中断中:
{
      u32l_var;
      char l_ary;
      char l_str;
      
      /* 读全局变量 */
      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针对更新副本

任务中:
{
      u32l_var;
      char l_ary;
      char l_str;
      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;
}

滴答中:
{
      u32l_var;
      char l_ary;
      char l_str;
      
      /* 读全局变量 */
      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++;
}

中断中:
{
      u32l_var;
      char l_ary;
      char l_str;
      
      /* 读全局变量 */
      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、如果一个非原子访问的全局变量不会在中断中读取,那么事情将变得异常简单。即不用定义副本,也不用更新副本,其它各处访问如同示例中访问即可。





页: 10 11 12 13 14 15 16 17 18 19 [20] 21 22 23 24 25 26 27 28 29
查看完整版本: 全局不关总中断的 RTOS,CosyOS-III-V1.2.0, 送 擎天柱-AI8051U转89C52核心板