当前位置:首页 > 汽车电子1 > 糖果Autosar
[导读]礼拜三接到一个学弟的问题,学弟在实习,说有个很奇葩的bug解决不了,我说你描述一下吧大概就是利用spi命令向flash里的某个地址写入数据,boot之后bootloader从flash的该地址里读数据,但是现在的问题是,偶尔读出来的数据不对,貌似是数据没有写进去,该问题目前只出现...


礼拜三接到一个学弟的问题,学弟在实习,说有个很奇葩的bug解决不了,我说你描述一下吧


大概就是利用spi命令向flash里的某个地址写入数据,boot之后bootloader从flash的该地址里读数据,但是现在的问题是,偶尔读出来的数据不对,貌似是数据没有写进去,该问题目前只出现了三次,再次复现不太好整


遇见这种问题,也没太好的办法,我让他先单步调试,看看write的时候,spi的接口发回来的status是ok还是error


调了大概十来次,都是ok,好,基本可以排除硬件本身的问题了,接口是直接用的cubemx的,应该也不会有问题


那么会是神马问题呢?我自己也很纳闷,按说如果硬件本身没问题,接口是对的,数据的写入没有理由不成功


我问写入和读取的请求是怎么调用的,学弟说代码没法全部发给我看,我说懂的,那你就给我说说架构方面是怎么设计的


学弟语音加屏幕绘图操作一顿,我大概明白了,于是我自己画了一幅图:




学弟还给我发来了文档的链接,我一看,用的是keil lib的接口:


Signal Events


以及


Mail Queue


我和学弟确认之后的伪码如下:


#define WRITE_REQUEST       0
#define READ_REQUEST        1
#define WRITE_SIGNAL        0x01
#define READ_SIGNAL         0x02
osMailQId  mail;


// Write reuqest
void write_request (void) {
T_MEAS *mptr;

mptr = osMailAlloc(mail, osWaitForever); // Allocate memory
mptr->info = WRITE_REQUEST;
osMailPut(mail, mptr); // Send Mail
osSignalWait(WRITE_SIGNAL,TIMEOUT);
/*******
Do something
********/

osMailFree(mail, mptr); // free memory allocated for mail
osThreadYield(); // Cooperative multitasking
}

// Read reuqest
void read_request (void) {
T_MEAS *mptr;

mptr = osMailAlloc(mail, osWaitForever); // Allocate memory
mptr->info = READ_REQUEST;
osMailPut(mail, mptr); // Send Mail
osSignalWait(READ_SIGNAL,TIMEOUT);

/*******
Do something
********/

osMailFree(mail, mptr); // Delete the request from the message queue
osThreadYield(); // Cooperative multitasking
}


//Message Queue
void message_queue_thread (void) {
T_MEAS  *rptr;
osEvent  evt;

for (;;) {
evt = osMailGet(mail, osWaitForever); // wait for mail
if (evt.status == osEventMail) {
if (evt.info == WRITE_REQUEST)
{
/********
Write data to the flash
*********/

if(write succeed)
{
osSignalSet(thread_id, WRITE_SIGNAL); //Send write signal
}
}

if (evt.info == READ_REQUEST)
{
/********
Read data to the flash
*********/

if(read succeed)
{
osSignalSet(thread_id, READ_SIGNAL); //Send write signal
}
}
}
}
}


看着貌似没有什么问题,总共三个函数,两个发请求,一个处理请求,发送请求的函数向请求队列即message queue存储一个请求,处理请求的函数负责接收和处理该请求,处理结束以后向发送请求的线程发送一个信号量,发送请求的线程从阻塞处,即osSignalWait处继续向下运行,从请求队列里free掉该请求


那么问题来了,请求是从哪里free掉的呢?


这其实是一个典型的生产消费者模型,发送读和写请求的线程皆为生产者,处理请求的线程为消费者,但是现在,从请求队列里remove掉请求的操作被生产者自己执行了,而根据经典理论,这个操作的执行权,应该属于消费者,即处理请求的线程


我和学弟说了大概我的意思,学弟表示,可是从逻辑上看似乎这么些也没有问题,我说,没关系,咱们现在来设想一种race condition:




你看,如果你先发送的是写请求,紧随其后跟了一个读请求,而在处理线程里,读请求的完成快于写请求,也就是虽然写请求的发送早于读请求,但是读请求的完成早于写请求,读请求被消息队列free掉也早于写请求,这会导致两种情况:


  1. 你的写请求压根还没完成,读请求就完成了,被消息队列处理并被读请求自己free了,等于读了个寂寞,那么你不可能读到写进去的数据


  2. 因为我们并不知道keil接口的具体实现,库是以库文件的形式呈现的,你看不到代码,如果这个osMailFree相当于queue的pop,应该弹出第一个元素,可生产者自己free掉自己的请求时无法保证该请求的指针指向的是第一个元素,那么有可能造成未知的指针问题,这更为严重,有潜在的内存泄露可能


针对这种问题我的方案是这样的:


#define WRITE_REQUEST       0
#define READ_REQUEST        1
#define WRITE_SIGNAL        0x01
#define READ_SIGNAL         0x02
#define FINISH_SIGNAL       0x03
osMailQId  mail;


// Write reuqest
void write_request (void) {
T_MEAS *mptr;

mptr = osMailAlloc(mail, osWaitForever); // Allocate memory
mptr->info = WRITE_REQUEST;
osMailPut(mail, mptr); // Send Mail
osDelay(10);
osSignalWait(WRITE_SIGNAL,TIMEOUT);

/*******
Do something
********/


osSignalSet(thread_id, FINISH_SIGNAL); // free memory allocated for mail
osThreadYield(); // Cooperative multitasking
}

// Read reuqest
void read_request (void) {
T_MEAS *mptr;

mptr = osMailAlloc(mail, osWaitForever); // Allocate memory
mptr->info = READ_REQUEST;
osMailPut(mail, mptr); // Send Mail
osDelay(10);
osSignalWait(READ_SIGNAL,TIMEOUT);

/*******
Do something
********/

osDelay(10);
osSignalSet(thread_id, FINISH_SIGNAL);
osThreadYield(); // Cooperative multitasking
}


//Message Queue
void message_queue_thread (void) {
T_MEAS  *rptr;
osEvent  evt;

for (;;) {
evt = osMailGet(mail, osWaitForever); // wait for mail
if (evt.status == osEventMail) {
if (evt.info == WRITE_REQUEST)
{
/********
Write data to the flash
*********/

if(write succeed)
{
osSignalSet(thread_id, WRITE_SIGNAL); //Send write signal
}
}

if (evt.info == READ_REQUEST)
{
/********
Read data to the flash
*********/

if(read succeed)
{
osSignalSet(thread_id, READ_SIGNAL); //Send write signal
}
}
osDelay(10);
osSignalWait(FINISH_SIGNAL,TIMEOUT);
osMailFree(mail, evt);
}
}
}


核心思想就是最简单的,让生产者负责生产,由消费者负责消费,避免他们之间串线,才能有效保证任务按照正确的顺序运行,osDelay不能够省略,否则可能造成经典的“你丫先把手撒开”“你TM先撒开我再撒开”的死锁问题


希望学弟调试顺利,对于不能一直复现的问题,其实也很难看到直观的效果,如果以后没再出现相应的问题,就当他解决了吧



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。

编辑精选

技术子站

关闭