在原始的RTOS中,赛灵思只用系统调用用于线程的切换,只有系统调用号0在用,今天仿照Linux系统调用实现在Zynq上的FreeRtos的系统调用实现。
1、首先实现系统调用表,用汇编编写如下:
sys_call_tlb:
.long _sys_write
.long _sys_open
很简单里面装了两个系统调用wirte和open,当然这个要我们自己实现,这里就简单实现一下__sys_open,如下所示:
int _sys_open(char* a, int b, int c){
xil_printf("file name: %s, flag %d, mode %d\n", a, b, c);
//return do_sys_open();
return 111;
}
很简单,打印传入的参数,返回一个111,那么如何传入由用户空间传输这些参数到内核空间呢?
寄存器R0-R5传输最多六个参数,R7记录系统调用号,下面看一下提供给用户的接口open函数:
int __open(char *f, int a, int b){
int flag = 0 ;
__asm volatile ( "SWI 1" ::: "memory" );
__asm__ ("mov %[ret], r0"
: [ret]"=r"(flag)
);
return flag;
}
也比较简单,传入了字符串,int a,b,然后调用了软中断号1,即open系统调用,上面文章我们讲过Arm用R0-R3传入不大于4个的系统参数,大于四个用堆栈,而在系统调用中我们一般小于四个,保存在R0-R3中,R0作为返回值,这里也是为什么我们用汇编把R0保存在Flag中返回。
下面看软中断处理函数:
FreeRTOS_SWI_Handler:
stmdb sp!,{r1-r3,r6-r7,r12,lr} /* state save from compiled code */
//由于R0-R3保存着系统参数,所以不能更改
tst r7, #0x20 /* check the T bit */
ldrneh r7, [lr,#-2] /* Thumb mode */
bicne r7, r7, #0xff00 /* Thumb mode */
ldreq r7, [lr,#-4] /* ARM mode */
biceq r7, r7, #0xff000000 /* ARM mode */
cmp r7, #0
beq _rtos_context_switch /* SWInterrupt: call C function here */
ldr r6, =sys_call_tlb //导入系统调用表
add r7, r6, r7, lsl #2 //获取系统地址
mov lr, pc//保存返回地址
ldr pc, [r7]//系统调用!
//bl do_sys_call//恢复现场
ldmia sp!,{r1-r3,r6-r7,r12,lr} /* state restore from compiled code */
movs pc, lr /*return to the next instruction after the SWI instruction */
_rtos_context_switch:
ldmia sp!,{r1-r3,r6-r7,r12,lr} /* state restore from compiled code */
/* Save the context of the current task and select a new task to run. */
portSAVE_CONTEXT
LDR R0, vTaskSwitchContextConst
BLX R0
portRESTORE_CONTEXT
测试:
在任务中添加Open函数,如下所示,然后打印返回值:
xil_printf("open ret: %d\n", __open("hello txt", 1, 2));
串口输出如下:
sys call init start!
Hello from Freertos example main
file name: hello txt, flag 1, mode 2
open ret: 111
Rx task received string from Tx task: Hello World
file name: hello txt, flag 1, mode 2
open ret: 111
Rx task received string from Tx task: Hello World
file name: hello txt, flag 1, mode 2
系统调用实现成功!