我正在开发一种使用网守任务来访问共享资源的设计。我现在的基本设计是网守任务接收的单个队列以及将请求放入其中的多个任务。
这是一个内存有限的系统,我正在使用FreeRTOS(Cortex M3端口)。
问题如下:异步处理这些请求非常简单。请求任务将其请求排队并进行业务,轮询,处理或等待其他事件。为了同步处理这些请求,我需要一种机制让请求任务阻塞,这样一旦处理了请求,网守就可以唤醒调用该请求的任务。
我能想到的最简单的设计是在每个请求中包含一个信号量,但考虑到内存限制以及FreeRTOS中相当大的信号量,这是不切实际的。
我提出的是使用任务暂停和任务恢复功能手动阻止任务,将句柄传递给网守,当请求完成时,它可以恢复任务。暂停/恢复有一些问题,我真的想避免它们。单个恢复调用将唤醒任务,无论其被多少次被其他调用暂停,这可能会产生不良行为。
一些简单的伪C来演示暂停/恢复方法。
void gatekeeper_blocking_request(void)
{
put_request_in_queue(request);
task_suspend(this_task);
}
void gatekeeper_request_complete_callback(request)
{
task_resume(request->task);
}
我计划在此期间使用的解决方法是使用异步调用并在每个请求任务中完全实现阻止。当操作完成时,网守将执行提供的回调,然后可以发布到任务的主队列或特定信号量,或任何需要的信号。对请求进行阻塞调用本质上是一个方便的功能,因此每个请求任务都不需要实现它。
Pseudo-C演示特定于任务的阻止,但这需要在每个任务中实现。
void requesting_task(void)
{
while(1)
{
gatekeeper_async_request(callback);
pend_on_sempahore(sem);
}
}
void callback(request)
{
post_to_semaphore(sem);
}
也许最好的解决方案就是不在网守和API中实现阻塞,并强制每个任务处理它。但是,这将增加每个任务流程的复杂性,我希望我能避免它。在大多数情况下,所有调用都希望在操作完成之前阻塞。
是否有一些我缺少的构造,或者甚至只是一个更好的术语来解决这类问题?我在搜索中没有遇到过这样的事情。
补充说明 - 网守任务的两个原因:
需要大量堆栈空间。网守可以拥有一个只需要所有内存的堆栈,而不是将这个要求添加到每个任务中。
在CPU中无法始终访问资源。它不仅可以同步CPU中的任务,还可以同步CPU外部的任务。
答案 0 :(得分:2)
使用互斥锁并使网守成为子程序而不是任务。
答案 1 :(得分:0)
我发布这个问题已经六年了,我努力让同步工作我需要它。使用的操作系统结构有一些可怕的滥用。我已经考虑过更新这段代码,即使它有效,也不要辱骂,所以我看了更优雅的方法来处理这个问题。 FreeRTOS在过去的六年中也增加了许多功能,其中一个我相信提供了一种轻量级的方法来完成同样的事情。
重新审视我原来提出的方法:
void gatekeeper_blocking_request(void) { put_request_in_queue(request); task_suspend(this_task); } void gatekeeper_request_complete_callback(request) { task_resume(request->task); }
避免使用此方法的原因是因为FreeRTOS任务挂起/恢复调用不会保持计数,因此单个恢复调用将取消多个挂起调用。当时,应用程序正在使用暂停/恢复功能,因此这是一种真正的可能性。
从FreeRTOS 8.2.0开始,直接任务通知本质上提供了一个轻量级的内置任务二进制信号量。当向任务发送通知时,可以设置通知值。此通知将处于休眠状态,直到通知的任务调用xTaskNotifyWait()
的某个变体,否则如果它已经进行了此类调用,它将被唤醒。
上面的代码,可以稍微修改如下:
void gatekeeper_blocking_request(void)
{
put_request_in_queue(request);
xTaskNotifyWait( ... );
}
void gatekeeper_request_complete_callback(request)
{
xTaskNotify( ... );
}
这仍然不是一种理想的方法,就像在其他地方使用任务通知一样,您可能会遇到挂起/恢复的相同问题,其中任务被与其预期的源不同的源唤醒。鉴于此,对我来说,这是一个新功能,它可以在修订后的代码中解决。