按照我之前给出的MPU地址空间表,Unprivileged似乎只能对内存段的RWUSER里进行写操作,而这个RWUSER段也是必须事前通过宏USER_RW_ACCESS前缀来定义的静态变量。那用户任务运行必定需要堆栈,堆栈如果不允许写,那如何运行程序呢?其实,我之前介绍的只是MQX中静态定义Unprivileged权限地址段的一种方法,而真正在使用中MQX使用的最多的还是动态的创建一个Unprivileged可读写的内存空间用来提供用户任务的相关操作。
首先我要补充一下MQX的动态内存分配机制,MQX的动态内存机制其实是基于内存池的分配机制。在系统初始化的时候会建立第一级内存池,包括系统的整个内存空间。如果任务没有明确指出要求分配内存空间的内存池,就是默认从这个一级内存池中分配。而MQX可以从这个一级内存池中分配出一块区域,建立二级内存池。如果任务指明了要求分配的内存池,就从该内存池对于的内存空间里利用block机制动态分配内存。而在同样在MQX的初始化过程中,在函数_bsp_enable_card里,MQX创建了一个叫做KD_USER_POOL
的二级内存池:
-
// create user heap automaticaly, we have specified only size of heap (end of heap is zero, start of heap mean size)
-
LWMEM_POOL_STRUCT_PTR lwmem_pool_ptr;
-
uchar_ptr start;
-
-
//start = _lwmem_alloc((char*)kernel_data->INIT.END_OF_USER_HEAP - (char*)kernel_data->INIT.START_OF_USER_HEAP + sizeof(LWMEM_POOL_STRUCT));
-
start = _lwmem_alloc((uint_32)kernel_data->INIT.START_OF_USER_HEAP + sizeof(LWMEM_POOL_STRUCT));
-
lwmem_pool_ptr = (LWMEM_POOL_STRUCT_PTR)start;
-
start = (pointer)((uchar_ptr)start + sizeof(LWMEM_POOL_STRUCT));
-
_lwmem_create_pool(lwmem_pool_ptr, start, (uint_32)kernel_data->INIT.START_OF_USER_HEAP);
-
_mem_set_pool_access(lwmem_pool_ptr, POOL_USER_RW_ACCESS);
-
-
kernel_data->KD_USER_POOL = lwmem_pool_ptr;
这个内存池的大小是16K,而MPU将其实Unprivileged权限设置为了RW。于是从这块内存池里分配出的内存都是可以被用户任务读写的。
现在回到开头我们提到的用户任务堆栈的问题,在分配任务堆栈函数_task_alloc_td_internal中:
-
...
-
#if MQX_ENABLE_USER_MODE
-
if (user)
-
{
-
new_td_ptr->STACK_ALLOC_BLOCK = (TD_STRUCT_PTR)_mem_alloc_from(kernel_data->KD_USER_POOL, stack_size);
-
}
-
else
-
{
-
new_td_ptr->STACK_ALLOC_BLOCK = (TD_STRUCT_PTR)_mem_alloc(stack_size);
-
}
-
#else
-
new_td_ptr->STACK_ALLOC_BLOCK = (TD_STRUCT_PTR)_mem_alloc(stack_size);
-
#endif /* MQX_ENABLE_USER_MODE */
-
....
这个user标志是由任务模板的属性是否含有MQX_USER_TASK来决定的,果然一个任务是用户任务,那堆栈就从KD_USER_POOL这个二级内存池中分配。通过这样用户任务就拥有了可以读写自己堆栈的能力。
当然KD_USER_POOL
内存池大小有限,一直从它里面分配会导致它容量不足。所以我们也可以根据自己的具体需要来创建一个用户内存池,像usermode例程里,它就是自己创建的内存池。
-
mem_pool_start = _mem_alloc(1024);
-
mem_pool_id = _lwmem_create_pool(&mem_pool, mem_pool_start, 1024);
-
_mem_set_pool_access(mem_pool_id, POOL_USER_RO_ACCESS);
虽然用户任务可以使用用户内存池里的内存,但用户任务并不能直接从用户内存池里分配内存,道理很简单_mem_set_pool_access只是将内存池里的内存空间赋予了Unprivileged的读写权限,但没有修改内存池本身结构体的权限。分配内存必定要修改对于的内存池结构体,因此用户任务是不能直接分配内存的,还是要老老实实的靠系统调用。
-
_mqx_uint _mem_set_pool_access
-
(
-
_lwmem_pool_id mem_pool_id,
-
uint_32 access
-
)
-
{
-
_mqx_uint res = MQX_LWMEM_POOL_INVALID;
-
LWMEM_POOL_STRUCT_PTR mem_pool_ptr = (_lwmem_pool_id)mem_pool_id;
-
-
if (LWMEM_POOL_VALID == mem_pool_ptr->VALID)
-
{
-
res = _psp_mpu_add_region(mem_pool_ptr->POOL_ALLOC_START_PTR, mem_pool_ptr->POOL_ALLOC_END_PTR, access);
-
}
-
-
return res;
-
}
对于一个用户任务而言,动态分配用户空间往往适用于于分配较大内存的场合,对于一些零散的变量还是通过静态定义的方式完成,毕竟系统调用和block模式分配都是耗资源和时间的。
到这里用户模式就介绍的差不多,总而言之用户任务的限制条件还是相当多,涉及底层或是内存的操作都要交由系统调用来完成。而相对而言,MQX关于底层驱动的系统调用接口还不是很丰富,如果是一个任务和底层驱动密切相关的话,是非常不建议使用用户模式的,当然如果能够针对性的编写相应的系统调用的也是可以的。对于个人开发者而言,这个用户模式是完全没有必要的,而对于大型的项目开发,由于本人没有实际的开发经验,也不是很清楚,既然freescale提出了一个功能,总是能起到作用的吧。
阅读(2755) | 评论(4) | 转发(1) |