系统调用
视频讲解可以看这一个课程
GDT表相关知识
原理
注册
允许应用调用操作系统的一些函数, 主要是由于权限, 需要在特区级下面运行一些操作
页表相关设置的时候有一个设置是PDE_U位, 这时候用户就可以访问这一段地址, 否则就是需要系统操作级来进行操作
实现系统调用的话使用的是系统调用门, 也是GDT表里面的一项
这里填写的是选择子, 指向一个代码段, 也是注册在GDT表里面
- 调用门描述符给出了代码段的选择子,有了段选择子,就可以访问GDT或者LDT得到代码段的基地址, 需要选择内核代码段
- 指的是偏移量, 实际指向的就是要运行的函数
- TYPE字段用于标识门的类型,1100表示调用门。
- P: 描述符中的P位是有效位,通常是1。当它为0时,调用这样的门会导致处理器产生异常。
- DPL字段指明调用门的特权级,从而指定通过调用门访问特定过程所要求的特权级。
- Param Count: 参数个数字段指明在发生堆栈切换时从调用者堆栈复制到新堆栈中的参数个数。
//这里是系统调用,首先不初始化任务的函数地址, 之后是系统代码段, 权限设置为3, 使用三个参数 [SYSCALL_SEG / 8] = {0x0000, KERNEL_CODE_SEG, 0xec03, 0},//设置系统调用函数的地址 gdt_table[SYSCALL_SEG / 8].limit_l = (uint16_t)(uint32_t)syscall_handler;
参数传递
栈里面的参数的传递, 参数的个数需要在GDT表里面登记一下
权限
需要的权限
需要代码段的权限, 以及实际访问的时候使用的段选择子的权限比调用门的权限高
示例: 在屏幕显示信息
原理
void task_0(void)
{uint8_t color = 0;//这一块是显存的位置unsigned short *dest = (unsigned short *)0xb8000;dest [0] = 'a' | 0x3500;//在这个地址写入一个'a'字符,后面的数字是颜色for(;;){color++;}
}
显示一个字符, 这一块是内存的位置, 这是一个80列25行的显示区域
有80列25行
实现一个系统调用
//系统调用, 使用func记录是第几个系统调用
//str和color是显示实际使用的参数
void do_syscall(int func, char * str, char color)
{static int row=1;if(func==2){//显示一个字符串//实际的代码实现可使用上面的显示}}
实现系统调用的参数传递
在使用的时候需要人工传递一下参数
会按照之前设置的参数, 会自动从栈里面取出来三个参数到系统的栈里面, 这个三是系统调用设置的那一个
之后人工把这三个值复制到系统栈最前面
//这一个函数给用户使用
//使用内敛汇编实现
void sys_show(char *str, char color)
{//这个是调用门的跳转位置, 偏移量不需要, 需要一个系统调用门的GDT偏移uint32_t addr[] = {0, SYSCALL_SEG};//把这几个参数记录一下, 然后调用系统调用门__asm__ __volatile__("push %[color];push %[str];push %[id];lcalll *(%[a])"::[a]"r"(addr), [color]"m"(color), [str]"m"(str), [id]"r"(2));
}
传入使用的三个参数, 之后跳转到对应的GDT对应的位置
//设置系统调用函数的地址gdt_table[SYSCALL_SEG / 8].limit_l = (uint16_t)(uint32_t)syscall_handler;
这个函数是在汇编文件里面实现的
之后再由汇编到C的时候传递参数使用的栈
//这里已经是特权级0了
syscall_handler://对寄存器进行保护push %dspusha//使用内核数据段mov $KERNEL_DATA_SEG, %axmov %ax, %ds//获取传进来的参数, 之后再次入栈,这是因为栈会使用最下面的几个作为C语言的参数mov %esp, %ebppush 13*4(%ebp)push 12*4(%ebp)push 11*4(%ebp)call do_syscall//把之前的三个参数取出来add $(3*4), %esppopapop %ds//由于这时候使用的特权级的栈, 返回的时候用使用这一个, 后面的参数是需要从栈里面取出来的参数个数(用户栈里面的参数需要取出来)retf $(3*4)
//这是系统调用在高权限的时候执行的函数
void do_syscall(int func, char * str, char color)
{static int row=1;if(func==2){//实际的处理代码}}
//任务1
void task_0(void)
{char * str = "task1 a:1234";uint8_t color = 0;for(;;){//在这里可以调用系统接口sys_show(str, color++);}
}
另一种系统调用int $0x80
可以使用命令int $num的方式进行调用一个中断