Xv6 的系统调用相对来说还是比较简单的,只需要修改几个文件就可以实现自定义系统调用了。
创建一个系统调用
- 确定函数签名
首先需要确定的是函数签名。按 Xv6 的规范,如果没有特殊情况,其返回值应该是 int
类型,一切顺利返回 0,否则返回一个用于标记错误类型的负数。
- 创建系统调用接口
确定好函数签名之后,在 user/user.h
进行声明。对于函数,我们需要声明并实现才可以正确编译和使用。而系统调用和函数调用最为重要的一个区别就是,执行系统调用会陷入内核态,这就导致系统调用接口的实现方式跟普通函数存在区别。
对于声明完的系统调用,其实现还不需要自己去实现。现在只需要在 user/usys.pl
中,仿照预设好的系统调用,添加一个 Perl 函数调用即可。这段脚本的作用就是在构建的时候去生成系统调用接口在用户态的实现。
- 创建系统调用号
用户态的系统调用接口的作用就是使进程陷入内核态,触发真正的函数。对于每个系统调用,都需要在 kernel/syscall.h
中分配一个独一无二的系统调用号。这里还需要仿照预设好的系统调用进行名称的设定。
- 完成实现逻辑
具体的系统调用逻辑按需要在不同的文件中去实现,此时的命名也可以按需要去确定,不需要跟系统调用保持一致。要注意的是,所有的实现函数的返回值都是 uint64
,参数都是 void
。
- 建立系统调用号和实现的联系
实现完系统调用逻辑后,需要在 kernel/syscall.c
将系统调用号和实现函数建立绑定。首先需要使用 extern
将实现函数引入该文件,而后按系统调用号大小顺序在 syscalls
填入实现函数即可。
细节详解
通过上述的操作,就可以自定义出任意的系统调用用于满足应用需求了。但还有很多细节需要进一步探讨。
系统调用用户态实现
用户态的实现细节全都在 user/usys.pl
所定义的 entry
中。
函数第一行会输出 .global $name
,这条指令在全局符号表定义了一个符号,符号的名字就是系统调用名。这就是编译的时候不会报 unresolved externals
错误的关键了。函数名本质上就是一个符号,所以一些语言会要求函数名保持唯一性,不然会报 redefinition
的错误。
函数第二行的输出是创建一个 Labels
。这个就是汇编的函数接口了。
函数第三行的输出比较关键。内核依靠系统调用号来得知进程调用了哪个系统调用,这也是在上一节说到的,系统调用号必须保持唯一性的原因。当进程通过系统调用陷入内核态的时候,内核通过 a7
这个寄存器来获取具体的系统调用号。所以这里会把系统调用号加载到 a7
这个寄存器中。这里也可以看到系统调用号的名称的设定方式。
函数的第四行输出只有一个简单的指令,这个指令是进程陷入内核态的关键。该指令会触发一个中断,提升系统的特权级,同时将程序计数器(program counter)调整到预先定义好的入口处,也就是 kernel/syscall.c
的 syscall
。这时候系统就会进入内核态,执行入口处的代码。
最后一句输出则是一个简单的返回。
寄存器作用和参数传递
系统调用号会被直接存放于 a7
这个寄存器中,所有的参数会被依次放置于 a0
开始的寄存器上。当参数的数量过多的时候,会有一套不一样的方式来传递,暂时可以不考虑。此外,在放置参数到寄存器上时,采用的是从右到左的方式,最右边的参数放置的寄存器编号越大。系统调用执行完毕后,返回值则会被放置于 a0
。
参数通过寄存器来传递可以节约大量的访存操作,这也是为什么在实现系统调函的函数时候,参数可以设置为 void
的原因。
系统调用参数获取
当一切顺利,程序正确运行到内核的时候,还需要考虑的一个问题就是参数的获取。参数涉及到了内核态和用户态的数据交换,处理起来会比较复杂。特别是像 exec
这种带有指针的参数,会额外带来两个问题。其一是安全问题,如何防范用户程序刻意传入的非法指针。其二是地址转变问题,在操作系统中,所有的程序使用的都是虚拟地址,有各自的页表。用户进程和内核进程是两套不一样的地址,如何在其中进行数据的交换。
好在内核代码已经实现了全部的参数获取接口。我们可以通过 argint
,argaddr
,argstr
这三个函数来获取到某一个编码的寄存器所存储的数据,并将其转化为整型,指针和字符串。这三个函数都是封装了 argraw
来实现功能。对于结构体,则可以采用 copyout
和 copyin
来进行数据的传递。
具体的实现现在暂时不必考虑,之后可以再做了解。
- 本文标题:解析系统调用
- 本文作者:Jacksing
- 创建时间:2022-02-12 17:20:44
- 本文链接:https://wzzzx.github.io/6-S081/analyze-system-call/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!