Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 什么';这两个中断服务例程中的竞争条件是什么?_C_Multithreading_Queue_Arm_Interrupt - Fatal编程技术网

C 什么';这两个中断服务例程中的竞争条件是什么?

C 什么';这两个中断服务例程中的竞争条件是什么?,c,multithreading,queue,arm,interrupt,C,Multithreading,Queue,Arm,Interrupt,我在大学里用ARM微控制器学习实时系统课程。在我目前正在进行的项目中,我正在实现向量场直方图(VFH)算法 问题是:我需要在线程之间进行通信;更具体地说,我希望有一个线程从RangeFinder获取传感器数据,对其进行必要的转换并将其放入队列中。对于它们,另一个线程必须获取该数据并进行处理,等等 目前,我正在使用一个更简单的版本-一个线程从ADC(SensorAcquisitionHandler)获取数据,另一个线程将前5个项目的平均值(最多)输出到显示器(ControlSignalHandle

我在大学里用ARM微控制器学习实时系统课程。在我目前正在进行的项目中,我正在实现向量场直方图(VFH)算法

问题是:我需要在线程之间进行通信;更具体地说,我希望有一个线程从RangeFinder获取传感器数据,对其进行必要的转换并将其放入队列中。对于它们,另一个线程必须获取该数据并进行处理,等等

目前,我正在使用一个更简单的版本-一个线程从ADC(SensorAcquisitionHandler)获取数据,另一个线程将前5个项目的平均值(最多)输出到显示器(ControlSignalHandler)

/*用于存储测距仪传感器数据的队列*/
静态无符号整数传感器[100];
静态int传感器AD=0;
静态int传感器STAIL=0;
void传感器获取处理程序(void){
/*清除中断*/
ADCIntClear(ADC0_基,1);
int i;/*度量缓冲区的索引*/
/*只使用了3个测距仪*/
if(ADCSequenceDataGet(ADC0_BASE,1,rangeBuffer)==3){
/*将rangeBuffer的数据放入SensorDataQueue*/
/*另外,在使用SensorDataQueue时,必须将
相应的量程测量*/
/*前方是关键路段!!!关闭中断*/
IntMasterDisable();
/*暂时使用简单的FIFO*/
对于(i=0;i<3;++i){
如果(传感器负载<100){
传感器[SensorsHead]=范围缓冲器[i];
SensorsHead++;
}
}
/*一切正常,打开中断*/
IntMasterEnable();
}
}
void控制信号处理器(void){
/*清除计时器中断*/
TimerIntClear(TIMER0\u BASE,TIMER\u TIMA\u TIMEOUT);
测量缓冲区的无符号字符i;/*索引*/
无符号长平均值=0;
字符缓冲区[20];
/*平均前n(n=传感器负载){
SensorsTail=0;
SensorsHead=0;
}
显示96x16x1StringDraw(缓冲区,0,0);
IntMasterEnable();
}
结果在一段时间内相对稳定,但在随机时间间隔内,结果非常高(几乎所有时间的结果都是~330)。此外,当我在“非常高的值”时刻使用符号调试器时,SensorTail和SensorHead的索引可以达到300+(队列是一个100元素的数组)

这听起来像是某种溢出,但我无法想象它是如何发生的。有人能帮我找到它吗


我知道问题的答案是“使用线程安全队列”,但我想了解这里的竞争条件是如何发生的,索引是如何混乱的,等等。谢谢

查看程序集转储。非易失性代码可以相对于易失性代码重新排序

想象一下你有:

assignmentA_with_volatile_operands;
assignmentB_with_non_volatile_operands;
assignmentC_with_volatile_operands;
编译器可以自由地将其重新排序为:

assignmentA_with_volatile_operands;
assignmentC_with_volatile_operands;
assignmentB_with_non_volatile_operands;
例如,第一个处理程序
SensorAcquisitionHandler
中的
For
循环实际上可以在
IntMasterEnable
之后执行,因为
For
循环中出现的任何对象都不符合
volatile
条件

编辑:

有些人认为这种代码重新排序是不允许的。事实是,它们是由现实生活中的编译器执行的

易失性在程序中不充当内存屏障。对于非易失性访问,不应假定易失性访问充当内存屏障

本标准在C11,5.1.2.3中定义了与程序可观察行为有关的一致性实施的最低要求

gcc
举例说明:对非易失性对象的访问不按易失性访问的顺序排列。不能将易失性对象用作内存屏障,以对非易失性内存的写入序列进行排序


很多编译器都很谨慎,不在volatile存在的情况下执行此类代码重新排序优化,但我已经看到编译器(例如,
gcc
)执行这些优化。

通过清除中断,您允许它再次发生。想象一下如果(例如)<代码>控制信号处理程序< /代码>在它的第一个循环中间被重新输入会发生什么,因为定时器设法超出了你的代码…


将整个功能(两者)包装在
IntMasterDisable
+
IntMasterEnable
中,并在禁用后和启用前清除中断。(我会在启用之前立即执行。)

您使用的是什么特定处理器/微控制器?什么RTO(如有)

ARM微控制器上的中断堆栈通常非常小。像
snprintf()
这样的运行时例程很容易需要数百字节,并且可能会溢出一个小堆栈。即使撇开堆栈空间的考虑,在中断上下文中使用C运行时函数通常也是不安全的——您通常非常受限于从中断中可以调用哪些函数。具体细节取决于您使用的实际RTO和编译器工具链


如果您违反了这些限制,则很容易导致数据损坏。

您可以通过使用无锁单读单写FIFO来避免头指针和尾指针上的争用情况-其中头指针只在一个线程中写入(或在您的情况下为ISR),尾指针在另一个线程中写入。这意味着您要在每个ISR中执行缓冲区包装测试

若您这样做了,并在每个ISR结束时重置了中断源,那个么您应该根本不需要任何锁定—全局禁用中断是非常不礼貌的。目前你持有的锁都很长时间了

您需要重写FIFO实现的另一个原因是:

    for (i = 0; i < 3; ++i) {
        if (SensorsHead < 100) {
在某些情况下,将对
    for (i = 0; i < 3; ++i) {
        if (SensorsHead < 100) {
/* Average first n (n <= 5) elements from Sensors queue. */
for (i = 0; i < 5 && SensorsTail < SensorsHead; ++i) {
    average += Sensors[SensorsTail];
    SensorsTail++;
}