x86使用GDT表实现系统调用--用户调用系统功能

本文详细介绍了系统调用的工作原理,包括如何通过GDT表设置权限,调用门的使用以及参数的传递方式。着重展示了在特权级控制下的系统调用过程,如通过`int$0x80`中断和`sys_show`函数的实例。
摘要由CSDN通过智能技术生成

系统调用

视频讲解可以看这一个课程
GDT表相关知识

原理

注册

允许应用调用操作系统的一些函数, 主要是由于权限, 需要在特区级下面运行一些操作

页表相关设置的时候有一个设置是PDE_U位, 这时候用户就可以访问这一段地址, 否则就是需要系统操作级来进行操作

实现系统调用的话使用的是系统调用门, 也是GDT表里面的一项

image-20240207134441751

image.png

这里填写的是选择子, 指向一个代码段, 也是注册在GDT表里面

  1. 调用门描述符给出了代码段的选择子,有了段选择子,就可以访问GDT或者LDT得到代码段的基地址, 需要选择内核代码段
  2. 指的是偏移量, 实际指向的就是要运行的函数
  3. TYPE字段用于标识门的类型,1100表示调用门。
  4. P: 描述符中的P位是有效位,通常是1。当它为0时,调用这样的门会导致处理器产生异常。
  5. DPL字段指明调用门的特权级,从而指定通过调用门访问特定过程所要求的特权级。
  6. 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;

image-20240222115654303

参数传递

image-20240222115814460

栈里面的参数的传递, 参数的个数需要在GDT表里面登记一下

权限

需要的权限

image-20240222120542064

需要代码段的权限, 以及实际访问的时候使用的段选择子的权限比调用门的权限高
image-20240222120318424

image-20240222120338844

示例: 在屏幕显示信息

原理

void task_0(void)
{
    uint8_t color = 0;
	//这一块是显存的位置
    unsigned short *dest = (unsigned short *)0xb8000;
    dest [0] = 'a' | 0x3500;//在这个地址写入一个'a'字符,后面的数字是颜色

    for(;;){
        color++;
    }
}

显示一个字符, 这一块是内存的位置, 这是一个80列25行的显示区域

image-20230921161254909

有80列25行

实现一个系统调用

//系统调用, 使用func记录是第几个系统调用
//str和color是显示实际使用的参数
void do_syscall(int func, char * str, char color)
{
    static int row=1;
    if(func==2)
    {
        //显示一个字符串
        //实际的代码实现可使用上面的显示
    }
    
}

实现系统调用的参数传递

在使用的时候需要人工传递一下参数

image-20230921165941136

会按照之前设置的参数, 会自动从栈里面取出来三个参数到系统的栈里面, 这个三是系统调用设置的那一个

之后人工把这三个值复制到系统栈最前面

//这一个函数给用户使用
//使用内敛汇编实现
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 %ds
	pusha
	//使用内核数据段
	mov $KERNEL_DATA_SEG, %ax
	mov %ax, %ds
	//获取传进来的参数, 之后再次入栈,这是因为栈会使用最下面的几个作为C语言的参数
	mov %esp, %ebp
	push 13*4(%ebp)
	push 12*4(%ebp)
	push 11*4(%ebp)

	call do_syscall
	//把之前的三个参数取出来
	add $(3*4), %esp
	popa
	pop %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的方式进行调用一个中断

image-20240222150221562

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值