Qualcomm Hexagon DSP入门篇之开始连接:fastRPC技术
FastRPC主要用途:
在开始Hexagon DSP开发时,我们需要熟悉为此平台打造的FastRPC技术,为方便用户快速进行客户端的开发以及DSP的调试,FastRPC为用户提供了一种先进的远程调用方式,从而使得用户可以在本地客户端上实现远程的调用,在本篇中,我将着重介绍FastRPC的架构并在安卓系统上实现,以及演示如何使用ION内存分配来为FastRPC创建连续的缓存区域。
FastRPC在设计之初便致力于实现在aDSP与APPS(基于应用处理器)之间远程的调用,为实现其功能,程序需要如下的硬件实现:
1.aDSP与应用共享内存(不包括L2/L1缓存)
2.aDSP中能支持有限的物理映射
同时,为了支持高性能的计算,应用的内存必须被直接映射到aDSP中,原因在于aDSP中没有支持足够物理地址以及ION。
通过将缓存分成in以及rout区,驱动程序能够实现aDSP与应用之间的cache同步,使RPC的延迟降低了将近50%,此外”inrout”与”in and rout”模式并没有太大区别,因此inrout并没有被采纳。
考虑到如下原因,在aDSP中,FastRPC协议采用同步的设计:
1.用户并不需要繁琐的创建一个线程来添加异步行为
2.对于内核来说,添加异步调用将会导致额外的复杂度,导致内核对aDSP与应用之间的管理更加麻烦。
3.对于aDSP来说,由于性能主要是由cache的异步操作决定的,所以,内存的速度并不会因此受到影响,
从官方给出的图片我们可以看出该技术调用的方式:
可以看出,FastRPC由六个主要部分组成:
Client:用户模式下启动的远程调用程序
Stub:与Client连接,用于自动生成交互代码并编排参数
ADSPRPC 驱动:ADSPRPC核心驱动,用于接收远程调用,安排队列并等待客户端的响应
ADSPRPC 框架:ADSPRPC框架将指令出列并为其调度处理
Skel:为未编排的参数自动生成代码
Object:实现调用的方法
安卓环境下的FastRPC实现
在为安卓系统搭建FastRPC连接时需要如下的组件:
应用处理器端:
在aDSP端则需要如下的库:
通过如下步骤来设置FastRPC驱动程序(注意,当使用FastRPC调用远程文件系统时,FastRPC可以自动生成,所以不需要多余的启动步骤)
1. 检测adsprpc.ko驱动程序是否正在运行:
adb shell ls /dev/adsprpc-smd
2. 如果文件不存在那么输入如下命令
adb root adb wait-for-device adb shell insmod /system/lib/modules/adsprpc.ko
3. 如果模块不存在并且/dev/adsprpc-smd在设备中无法支持FastRPC,则重新检测adsprpc.ko的存在
adb shell ls /dev/adsprpc-smd
4. 通过如上的步骤,FastRPC就被配置成功了
建议选择性的检查adsprpcd是否在运行
logcat -s adsprpc
该守护进程可能位于/system/bin或/system/vendor/bin中,如果该程序正在运行且没有报错信息,则说明FastRPC已经成功使能并且可以进行操作了
反之,若守护进程没有运行,则启动守护进程并检查
locat -s adsprpc
之所以会出现这种情况,是因为在某些老版本中,守护进程还未初始化时所有的进程调用都会被屏蔽
使用ION分配器
aDSP的硬件架构对于非连续内存的访问非常低效,这种对内存的访问差不多等同于”malloc”指令。不过安卓系统为其提供了称为ION的连续内存分配器,对于更加深入的文献可参考http://lwn.net/Articles/480055.
不同版本的安卓可以为用户提供不同版本的ION实现,下文中将会演示两种版本下为ION的API
ICS与JB
ICS与JB的实现对于ion_alloc中的ioctl有不同的结构,举例如下:
ICS
struct ion_allocation_data { size_t len; size_t align; unsigned int flags; struct ion_handle *handle; };
同时为了确定堆栈的id,调用者需要设置标志位
alloc.flags = (0x1 << HEAP_ID);JB
struct ion_allocation_data { size_t len; size_t align; unsigned int heap_mask; unsigned int flags; struct ion_handle *handle; };
为了确定堆栈的id,调用者需要设置heap_mask
alloc.heap_mask = (0x1 << HEAP_ID);
在aDSP上创建persistent线程
在开发是,另外一个需要考虑的问题便是导入到aDSP中的模块需要被所有的处理过程共享,因此,如果一个对于高级操作系统来说,时刻保持特定的上下文从而跟踪需要时刻调用的数据是非常重要的。
因此我们可以通过HAP_pls.h来创建这样一个线程
struct thread { qurt_thread_t tid; qurt_sem_t sem; boolean running; void* stack; } static int thread_start(void* data) { struct thread* th = (struct thread*)data; while(th->running) { qurt_sem_down(&th->sem); } return 0; } static void thread_dtor(void* data) { struct thread* th = (struct thread*)data; if(th->tid) { int err; th->running = 0; qurt_sem_up(&th->sem); qurt_thread_join(th->tid, &err); } if(th->stack) { free(th->stack); qurt_sem_destroy(&th->sem); } } static int thread_ctor (void* ctx, void* data) { //this constructor may be called twice, but only one instance will survive qurt_thread_attr_t attr; struct thread* th = (struct thread*)data; int size = 1024*8; if(0 == (th->stack = malloc(size))) { goto error; } qurt_sem_init_val(&th->sem, 0); qurt_thread_attr_set_stack_size (&attr, size); qurt_thread_attr_set_stack_addr (&attr, (unsigned long long *)th->stack); qurt_thread_attr_set_name (&attr, "my thread"); th->running = 1; if(0 != qurt_thread_create(&th->tid, &attr, (void*)thread_start, th)) { goto error; } return 0; error: thread_dtor(th); return -1; } //the first time this function is called the thread will be constructed //all subsequent calls will return the same thread instance or non-zero failure //this function should only be called from a FastRPC provided thread. static int thread_instance(struct thread* th) { return HAP_pls_add_lookup((uint32)thread_ctor, //type, some unique address 0, //key, for different type instances sizeof(struct thread), //size of our struture thread_ctor, //structure ctor 0, //aditional user context for ctor thread_dtor, //desturctor &th); //result }至此,我们就介绍了使用FastRPC过程中的一些基本步骤以及快速上手代码,如果您还有更多的疑惑,可以查看官方提供的文献,作为一种可以实现远程实时开发的技术,FastRPC为我们提供了难以想象的方便,当开发安卓系统的时候,相信各位能有更好的体验。