共享内存分配与ARM+DSP核间通信IPC(MessageQ)

IPC核间通信

IPC(Inter-Processor Communication)提供与处理器硬件无关的API,可用于多核处理器核间通信、同一处理器进程间通信和设备间通信。对于异构多核处理器,可通过IPC组件进行核间通信。IPC通信中常用的MessageQ——大小可变的消息传递模块。这是一种功能强大但易于使用 MessageQ 的消息传递, 调用MessageQ 模块后,如下图标红部分:MessageQ 模块工作场景图
在MessageQ 模块工作场景图中,灰色的部分并不是不要的,只是在MessageQ中,灰色部分会在应用程序 main ()函数中的 Ipc _ start () API 调用负责配置,我们在只需要关注MessageQ 模块工作场景图的标红部分即可。所有使用 IPC 模块的应用程序都必须调用 IPC _ start () API,该 API 执行以下操作:
1、初始化 IPC 使用的许多对象和模块同步多个处理器,以便它们可以以任何顺序引导。
2、IPC _ start ()使用索引为0(零)的共享区域创建资源管理表,供其他 IPC 模块在内部使用。
所以在调用 Ipc _ attach ()之前,必须在处理器上调用 Ipc _ start ()。
MessageQ 模块支持可变长度消息的结构化发送和接收。它是独立于操作系统的,可以使用任何线程模型。**对于你创建的每个 MessageQ,只有一个读取器,可能有多个写入器。**消息通过消息队列发送和接收。读取器是从消息队列中获取(读取)消息的线程。写入器是一个将消息放入(写入)消息队列的线程。每个消息队列有一个读取器,可以有多个写入器。一个线程可以读取或写入多个消息队列。
单个读线程调用 MessageQ _ create ()、 MessageQ _ get ()、 MessageQ _ free ()和 MessageQ _ delete ()。
Writer 线程调用 MessageQ _ open ()、 MessageQ _ alloc ()、 MessageQ _ put ()和 MessageQ _ close ()。
上述的MessageQ读取和写入流程可以用流程图表示:MessageQ读取和写入流程图
从概念上讲,读线程拥有一个消息队列。读线程创建一个消息队列。然后,Writer 线程打开创建的消息队列以访问它们。

创建 MessageQ 对象MessageQ _ create ()

读线程创建一个消息队列。要动态创建一个 MessageQ 对象,其语法如下:
MessageQ_Handle MessageQ_create(String name,MessageQ_Params *params);
创建队列时,需要指定名称字符串,因为MessageQ _ open ()函数需要此名称。虽然这个名称不是必需的(也就是说,它可以是 NULL) ,但是一个未命名的队列是不能被打开的。如果调用成功,则返回 messageq_handle。如果调用失败,则返回 NULL。
创建 MessageQ 对象

打开消息队列MessageQ _ open ()

Writer 线程打开创建的消息队列来访问它们。为了获得已创建的消息队列的句柄,编写线程必须调用 MessageQ _ open () ,其语法如下。
Int MessageQ_open(String name, MessageQ_QueueId *queueId);
这个函数需要一个名字——String name,必须与创建对象的名字匹配。如果在任何处理器上没有找到匹配的名称, MessageQ_open()将返回MessageQ_E_NOTFOUND,如果 MessageQ open 成功,队列 ID 被填充 MessageQ_S_SUCCESS。打开消息队列

分配消息MessageQ _ alloc ()

MessageQ 通过 MessageQ _ alloc ()和 MessageQ _ free ()函数管理消息分配
MessageQ 使用 Heaps 进行消息分配
MessageQ_Msg MessageQ_alloc(UInt16 heapId, UInt32 size);
MessageQ _ alloc ()中的分配大小必须包括消息头的大小,也就是32字节。一旦消息被分配,它可以被发送到任何消息队列。一旦读取器接收到消息,它可以释放消息或者重新使用消息。如果应用程序没有使用 MessageQ _ alloc ()分配的消息,则无法通过 MessageQ _ free ()函数释放消息,即使消息由不同的处理器接收。另外,传输可能会在内部调用 MessageQ _ free ()并且遇到错误。分配消息

发送消息MessageQ _ put ()

一旦打开消息队列并分配了消息,就可以通过 MessageQ _ put ()函数将消息发送到 MessageQ。函数语法如下:
Int MessageQ_put(MessageQ_QueueId queueId, MessageQ_Msg msg);
可以通过 MessageQ _ getreplyqueue ()函数“发现”消息队列 ID,
/* 使用嵌入的答复目的地 */

replyMessageQ = MessageQ _ getreplyqueue (msg);
if(replyMessageQ == MessageQ _ invalidmessageq)
{ 
	System _ abort (“ Invalid reply queue n”) ;
}

/* 发送回应 */

Status = MessageQ _ put (replyQueue,msg) ;
if(Status < 0)
{
	System _ abort (“ MessageQ _ put 不成功 n”) ;
}

如果 MessageQ _ put ()成功,则返回 MessageQ _ s _ success。
发送消息
在发送消息之前,可以使用 MessageQ _ setmsgid ()函数为接收线程可以检查的消息分配一个数值。
/* Increment… 远程端将检查这个 */

msgId++;
MessageQ _ setmsgid (msg,msgId);

还可以使用 MessageQ _ setmsgpri ()函数来设置消息的优先级。

接收信息MessageQ _ get ()

为了接收消息,读线程调用MessageQ _ get () API接口,接口函数如下所示:
Int MessageQ_get(MessageQ_Handle handle,
MessageQ_Msg *msg, UInt timeout)

Status = MessageQ _ get (MessageQ,& msg,MessageQ _ forever); if (status < 0)
{
	System _ abort (“不应该发生; 永远超时n”) ;
}

接收信息
如果没有消息出现,也没有错误发生,这个函数会在等待消息到达的超时期间阻塞。如果超时过期,则返回 MessageQ _ e _ fail。如果发生错误,msg 参数将不会改变。收到消息后,可以使用以下 api 从消息头获取有关消息的信息:
1、MessageQ _ getmsgid ()获取 MessageQ _ setmsgid ()设置的 ID 值。例如:
/* 获取 id 并将其递增以发送回 */
msgId = MessageQ _ getmsgid (msg) ;
MessageQ _ setmsgid (msg,msgId) ;
2、Messageq_getmsgpri ()获得 messageq_setmsgpri ()设置的优先级。
3、MessageQ _ getmsgsize ()获取以字节为单位的消息大小。
4、Messageq_getreplyqueue ()获取 messageq_setreplyqueue ()提供的队列 ID。

删除 MessageQ 对象MessageQ _ delete ()

MessageQ _ delete ()释放存储在本地内存中的 MessageQ 对象。如果任何消息仍然在内部链接列表中,它们将被释放。句柄的内容被函数取空,以防止删除后使用。
Void MessageQ _ delete (MessageQ _ handle * handle) ;
一旦消息队列被删除,就不能向它发送任何消息。 MessageQ _ close ()是推荐的,但不是必需的。
删除 MessageQ 对象

消息优先级

MessageQ 支持以下三个消息优先级:
1、MessageQ _ normalpri = 0;
2、MessageQ _ highpri = 1;
3、MessageQ _ urgentpri = 3;
可以在发送消息之前使用 MessageQ _ setmsgpri 函数设置消息的优先级:
Void MessageQ_setMsgPri(MessageQ_Msg msg,
MessageQ_Priority priority);
在内部,MessageQ 对象维护两个链表: 普通链表和高优先级链表。一个普通的优先级消息以 FIFO 的方式放置在“普通”链表中。一个高优先级的消息以 FIFO 的方式被放置到“高优先级”链表中。紧急消息被放置在高链表的开头。
由于在阅读一条信息之前可能会发送多条紧急信息,因此不能保证紧急信息的顺序。
当收到消息时,读者首先查看高优先级链接列表。如果一条消息出现在这个列表中,它就会被返回。如果没有,则检查正常的优先级链接列表。如果一条消息出现在那里,它将被返回;否则同步器的等待函数就会被调用。

应答队列

对于某些应用程序,在队列上执行 messageq_open ()是不现实的。例如,服务器可能不想打开所有客户端的队列来发送响应。为了支持这个用例,消息发送者可以使用 MessageQ _ setreplyqueue ()函数在消息中嵌入一个回复 queueId。
Void MessageQ_setReplyQueue(MessageQ_Handle handle,
MessageQ_Msg msg)
这个 API 将消息队列的 queueId 存储到 MsgHeader 中的字段中。
MessageQ _ getreplyqueue ()函数执行相反的操作。例如:

MessageQ_QueueId replyQueue;
MessageQ_Msg msg;
...
/* Use the embedded reply destination */
replyMessageQ = MessageQ_getReplyQueue(msg);
if (replyMessageQ == MessageQ_INVALIDMESSAGEQ) 
{
	System_abort("Invalid reply queue\n");
}

这个函数返回的 MessageQ _ queueid 值可以在 MessageQ _ put ()调用中使用。
应答队列

共享内存分配

AM5728核心板的Linux 内核预留0xa0000000~0xac000000(192MByte)内存作为 CMEM 共享内存。
AM5728内存
本次实验使用 0xa0000000~0xa0200000(2MByte)作为共享内存进行测试,此 2MByte 内存划分为 4 个 512KByte(0x80000,524288)池空间。设备树修改如下:
共享内存
共享内存分区
更换设备树后重新启动开发板,查询内存分配及空间大小:
查询开发板内存空间
在上述共享内存分区中,id 0池空间用于存放原始的时域数据,id 1池空间用于存放处理后的频域数据,剩余两个池空间先分配出来,不使用。DSP 端(DSP1)对共享内存 dataln 的数据进行 FFT 幅值运算,并把结果存入共享内存 dataOut,ARM 端(Cortex-A15_0)读取共享内存 dataOut 的数据并写入到 FFTData文件。
DSP 端创建一个 message 来传递信息,ARM 端使用 CMEM 创建4个空间用于数据的存储,“msg->dataIn”和“msg->dataOut”分别指向这其中两个空间的首地址。线性调频信号(LFM)模拟数据存入共享内存 dataIn,并发送消息至 DSP 端。DSP 端接收到消息后,读取共享内存 dataIn的数据进行 FFT 幅值运算,并把结果存入共享内存dataOut,同时发送消息至 ARM 端。ARM 端接收到消息后,从共享内存 dataOut 中读取数据并写入 FFTData 文件。最后发送ShutDown 消息到 DSP 端,DSP 端接收到该消息后,将信息回传至 ARM 端,关闭自己并重新初始化。
程序运行结果

实例工程文件下载

资源列表
实例工程文件下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值