搜索
bottom↓
回复: 43

如何解决“全局变量”被中断和主程序共享所带来的冲突?

[复制链接]

出0入0汤圆

发表于 2009-12-10 17:48:39 | 显示全部楼层 |阅读模式
之前没太考虑这个问题, 最近一个项目中用到了不少全局变量, 并且在中断中和主循环中都有读写访问, 现在出现了奇怪的现象, 现场说某一功能有问题, 在家里又没重现出来, 看了代码, 发现里面存在不少隐患。 主要是因为用定时中断做键盘扫描, 并且在中断中处理了部分按键, 并改变了系统的一些状态。而在主循环中也修改了状态。 现在才发现, 全局变量混成了一团了, 呜....

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2009-12-10 17:54:28 | 显示全部楼层
中断与主程序都改写的全局变量,读写需要关闭中断。

出0入0汤圆

发表于 2009-12-10 17:57:19 | 显示全部楼层
【1楼】 voidx void *

积分:228
派别:
等级:------
来自:
中断与主程序都改写的全局变量,读写需要关闭中断。


正解!临界变量读写都要关中断!

出0入0汤圆

发表于 2009-12-10 18:06:57 | 显示全部楼层
个人是,
1.尽量少用全局变量。
2.裸露的全局变量全部用结构体封装起来。
3.中断与主程序共享全局变量,用函数(含临界段)封装起来。

出0入0汤圆

发表于 2009-12-10 18:16:18 | 显示全部楼层
裸露的全局变量全部用结构体封装起来???

封成结构体,不照样还是裸露的全局结构体?

出0入0汤圆

发表于 2009-12-10 18:22:17 | 显示全部楼层
加上一些临界段代码,防止中断破坏全局变量。

出0入0汤圆

 楼主| 发表于 2009-12-10 18:48:51 | 显示全部楼层
临界段代码,是关全局中断好还是只关会带来冲突的中断好呢?

出0入0汤圆

 楼主| 发表于 2009-12-10 18:51:12 | 显示全部楼层
“中断与主程序都改写的全局变量,读写需要关闭中断”, 那如果是主程序写, 中断读呢?这种情况要不要加临界段呢? 还有主程序读,中断写呢? 两个都读应该不用保护了吧。

出0入0汤圆

发表于 2009-12-10 20:16:19 | 显示全部楼层
裸露的全局变量全部用结构体封装起来???
封成结构体,不照样还是裸露的全局结构体?
-------------------------------------------

全局变量放在一起,方便统一管理,也不用担心全局变量与局部变量重名(节约变量名称资源)。

假设100个全局变量,分散定义不方便管理,有时写程序,前面定义的全局变量,到后来编程自己都可能忘记已经定义过一个全局变量。

如果把全局变量放在一个结构体里,对外来说,就一个全局变量。

出0入0汤圆

 楼主| 发表于 2009-12-10 23:39:55 | 显示全部楼层
严重同意, 谢谢, 学习了!

出0入0汤圆

发表于 2009-12-11 00:13:14 | 显示全部楼层
不需要关中断

51有原子操作指令"djnz",好好去研究一下吧.

出0入0汤圆

 楼主| 发表于 2009-12-11 12:50:40 | 显示全部楼层
更喜欢平台无关的方法,另外想问一下,AVR或51的每条指令在执行时应该不会被中断吧,这样是不是说任何一条指令的操作都是“原子”的呢?

出0入0汤圆

发表于 2009-12-11 15:20:23 | 显示全部楼层
8L上官的意思这次我理解了...其实...我的代码中都是这么做的...
但在我的想法中,这还是全局变量,所以有所误解.

出0入0汤圆

 楼主| 发表于 2009-12-11 19:44:45 | 显示全部楼层
11楼的问题, 麻烦哪位高手回答一下呢?

出0入0汤圆

发表于 2009-12-11 20:02:28 | 显示全部楼层
只要保证全局变量的原子性(单个内存空间), 全局变量就成了数据在前后台任务之间传递的高效途径。
如果不能保证全局变量的原子性, 那么可以通过”传名(引用)“方式保证数据完整性。

出0入0汤圆

发表于 2009-12-11 20:06:40 | 显示全部楼层
【11楼】 nicksean 不务正业
更喜欢平台无关的方法,另外想问一下,AVR或51的每条指令在执行时应该不会被中断吧,这样是不是说任何一条指令的操作都是“原子”的呢?  
------------------
单条指令不会被中断,便超过8bit的运算要分多条指令才能完成,有可能在进行一半的时候被中断吧。

出0入0汤圆

发表于 2009-12-11 20:19:57 | 显示全部楼层
15楼说的对哈

不关中断进行大规模全局数据操作,同时该数据有在中断中被改写可能,那是不允许的

出0入0汤圆

发表于 2009-12-11 21:14:41 | 显示全部楼层
【4楼】 snoopyzz

Hi
看见你的ID我想求证一个事情
snoopy:以前在玩文曲星上的6502的汇编的时候,就是snoopy将Lee的调试工具改成PC2000的版本,让我有机会能更深入了解文曲星
zz:有一次去我的导师ykp那里的时候,看见一个师兄的名字就是zz,呵呵,不过是在挂科名单上
因为那时候我知道玩文曲星有个厉害的人就在18系,snoopy即zz

如果是你你一定知道我在说什么,如果不是也看不懂我在说什么:)

出0入0汤圆

发表于 2009-12-14 12:56:54 | 显示全部楼层
哈工大航院18系,没想到遇见师弟,诗诺比是我,被认出来了嘿嘿,挂科惭愧呀(其实大多都是旷考)

出0入0汤圆

发表于 2009-12-18 04:37:43 | 显示全部楼层
看来蛮多人不知道"原子操作"是什么意思,呵呵,我解释一下吧,献丑了:

所谓原子操作,并不是指一条指令,而是指一系列操作不能被打断.

这种操作与临界区是密切相关的,可以说原子操作就是临界区引发出来的需求.

以上是给大家提供资料查找的入口,下面我用通俗的话说说,但切不要认为自已看懂了就可以不去查以上相关资料了,知道啥叫"通俗"吗?呵呵.废话不多说:

在多进程的系统里(比如多任务,比如中断服务程序),假如两组程序都要访问同一个资源,而作为程序员的你没有办法避免在一个进程访问完该资源之前,另一进程就来访问该资源,就出现了访问冲突,这就是临界区问题.

举个实际例子:假如A进程用变量a作为临时存储区时,如果运行到一半中断发生了,而中断里也会用到该变量,等中断返回时,变量a中的内容已经被破坏了,进程A并不知道这一点,于是得到错误的运行结果.

避免的办法看似简单:拿个变量来当标志,为0时表示没人用共享资源,为1时表示正在使用.使用时先检查该标志,为1时等待,为0时则置1,并使用资源,用完后将标志置回0,于是资源冲突问题就解决了.

是吗?问题转移了----当你检查完变量,发现它为0,正要将它置1时,此时进程被打断,于是别的进程仍会毫不客气的使用该资源,要命的是在多任务系统中,当CPU控制权回来时,刚才使用这个资源的进程还没用完这个资源呢,于是出错了.

可以看出来,必须有一种这样的指令:先检查某变量,当它为0时则置1并跳转,而这一系列过程不允许被打断,拥有这种能力的指令就叫作"原子操作指令".由于51没有这种指令,这也就是为什么楼上很多人说要关中断的原因了.事实上,关中断这种方式也用于我们桌面机系统!LINUX就用了很多关中断.

虽然51没有上面说的这种指令,却有另一个指令:DJNZ---先减再检查,并根据比较结果跳转,而这足够完成临界区操作了.







系统初始化是必须事先将s置初值为1


aqui_mutex:
DJNZ  s, wait

....共享资源操作代码(也就是临界区)

INC s
RET


wait:
INC s
jmp aqui_mutex


以上代码在任何情况下(跑飞不算,呵呵)不会发生两个进程同时进入临界区的情况.

你一定会想到,假如将上面的代码写成一个子程序,那就可以作为一个标准函数供C调用了,恭喜你,这就是标准的信号量.

关于"临界区","信号量","原子操作",这些词和概念都是操作系统里整天要讨论的东西,而没接触过操作系统的人很难想到困扰自已的问题原来早就有人已经作出了系统的描述并找到解决的方法了,所以说,并不是"用不上"的知识就可以不学,知识是有关联性的,更高级的系统往往也会有更高级的思想.你未必需要穿皮鞋,但你必须了解皮鞋跟草鞋的区别和各自的优缺点

出0入0汤圆

发表于 2009-12-18 08:25:53 | 显示全部楼层
顶下19楼~

出0入0汤圆

发表于 2010-2-11 15:30:10 | 显示全部楼层
19楼看的俺一头雾水

出0入0汤圆

发表于 2010-2-13 19:01:41 | 显示全部楼层
不是很懂,mark,以后慢慢研究

出0入0汤圆

 楼主| 发表于 2010-2-14 00:00:10 | 显示全部楼层
粗看了下AVR指令, 没发现有DJNZ这样功能的, 似乎AVR中防冲突就只能关中断了哈. 中断和主循环中, 如果存在一个读一个写或者两个写, 都应该做临界段保护, 如果两个都是读就不用了吧?

出0入0汤圆

发表于 2010-2-14 00:33:33 | 显示全部楼层
回复【23楼】nicksean  不务正业
-----------------------------------------------------------------------

RISC结构的处理器,一般是Load/Store。除非特别设计,一般不会出现读-修改-写指令的。此时,关中断最简单,也许还有别的方法。
CISC,内存映射寄存器这类,会有产生比较多的原子操作。

至于什么条件需要保护,具体问题具体分析。通常而言,多个读是无所谓的;多于一个的读-修改-写操作,就需要对读-写区间进行保护;如果几个读之间有数据一致性的依赖关系,那么最好用中间变量处理。

出0入0汤圆

发表于 2010-3-25 21:37:17 | 显示全部楼层
mark一下

出0入0汤圆

发表于 2010-3-26 11:47:56 | 显示全部楼层
非常惭愧!!!看不懂,我正在努力~希望各位多多指教

出0入0汤圆

发表于 2010-8-26 09:36:56 | 显示全部楼层
问一下 rainyss:
      如果用你说的DJNZ来实现两个不同优先级的中断之间的同步,如果高优先级中断打断了低优先级中断,高优先级中断是不是有可能饿死?

出0入85汤圆

发表于 2010-8-26 10:24:06 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-5-29 13:37:48 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-5-29 22:20:15 | 显示全部楼层
看了一下19楼的解释,对基本概念有个认识了。虽然现在我还没有发现有这些问题。因为在用MEGA88的时候,每次进入中断的时候,AVR会自动将调用CLI()函数关掉全局中断。我并没有进行中断嵌套,所以也没有手动置位全局中断标志。

出0入0汤圆

发表于 2011-5-29 22:28:46 | 显示全部楼层
学习

出0入0汤圆

发表于 2011-5-29 23:06:39 | 显示全部楼层
这种情形下,我的做法一般是:

主程序处理按键前先设置标志位
按键处理结束则复位标志位

进入中断如果检测有标志置位,则调过本次中断的按键处理或作使用其它合理方案

出0入0汤圆

发表于 2011-5-30 07:54:39 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-5-30 08:02:29 | 显示全部楼层
"1.尽量少用全局变量。 "

absolutely true. global variables are pure evil and should be minimized.

a few ways to minimize the use of global variables:

1) take advantage of C's scoping: if a "global" variable is used by a set of routines, declare that variable in a separate module so that it is out of scope for other modules - localizing a global variable;
2) to access such "localized" global variable, rely on read/write routines.

出0入0汤圆

发表于 2011-5-30 20:33:41 | 显示全部楼层
回复【30楼】xkdwangcs
看了一下19楼的解释,对基本概念有个认识了。虽然现在我还没有发现有这些问题。因为在用mega88的时候,每次进入中断的时候,avr会自动将调用cli()函数关掉全局中断。我并没有进行中断嵌套,所以也没有手动置位全局中断标志。
-----------------------------------------------------------------------

这样也不行!是:中断与主程序都改写的全局变量,读写需要关闭中断。

在主程序中要改写这样的变量前也要关中断,并非只是在中断程序中要关中断允许!

出0入0汤圆

发表于 2011-5-30 20:52:34 | 显示全部楼层
AVR GCC 也分 interrupt 和 signal 的。。。不是都会自动调用cli()。

出0入0汤圆

发表于 2011-5-31 10:04:34 | 显示全部楼层
回复【36楼】huayuliang 花生
avr gcc 也分 interrupt 和 signal 的。。。不是都会自动调用cli()。

-----------------------------------------------------------------------

AVR运行中断函数时不是硬件自动关全局中断的吗?

出0入0汤圆

发表于 2011-6-2 16:18:28 | 显示全部楼层
标记下,阿弥陀佛!

出0入0汤圆

发表于 2011-6-2 16:36:01 | 显示全部楼层
目前没有碰到这个情况;学习;从来不在中断里处理数据;

出0入0汤圆

发表于 2011-6-2 19:50:19 | 显示全部楼层
用volatile定义变量可不可以啊??

出0入0汤圆

发表于 2011-6-2 20:32:53 | 显示全部楼层
学习学习

出100入101汤圆

发表于 2013-11-13 11:07:23 | 显示全部楼层
if (flag == 1) 之类要不要关中断?

出10入95汤圆

发表于 2014-4-23 16:33:20 | 显示全部楼层
xinyou 发表于 2011-6-2 19:50
用volatile定义变量可不可以啊??

不可行。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-23 22:36

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表