哈工大操作系统 实验楼实验三 系统调用
实验楼实验三 系统调用
操作系统实现系统调用的基本过程
- 应用程序调用库函数(API);
- API 将系统调用号存入 EAX,然后通过中断调用使系统进入内核态;
- 内核中的中断处理函数根据EAX中的系统调用号,调用对应的内核函数(系统调用);
函数(API)展开含有
int指令的代码,此时CPL=3,0x80处的中断描述符(系统门)的DPL也为3,改变cs:ip(cp)[CPL变成0]进入内核的系统调用代码处
- 系统调用完成相应功能,将返回值存入 EAX,返回到- 中断处理函数;
- 中断处理函数返回到 API 中;
- API 将 EAX 返回给应用程序。
linux0.11的系统调用(3个参数)的源码

这里还有一个点需要注意一下,实验要求添加的两个系统调用在满足条件时返回
拷贝的字符数,不满足条件时返回 “-1”,并置 errno 为 EINVAL,从上面这个系统调用(3个参数)源码中可以看出来,在实现sys_xxxx时只需要返回-EINVAL即可,这里会对返回值进行判断,若__res>=0则返回__res,反之则置errno=-__res,并返回-1。
记录过程:
lib/xxx.c->include/unistd.h->kernel/system_call.s->include/linux/sys.h->(sys_xxx的实现)
int 0x80的系统调用实现
先是init/main.c下,内核初始化时调用了sched_init()
| |
在kernel/sched.c中,调用了set_system_gate
| |
set_system_gate 是个宏,在 include/asm/system.h 中定义为:
| |
同时该头文件下_set_gate 的定义是:
| |
_set_gate主要就是填充IDT(中断描述符表),将 system_call 函数地址写到 0x80 对应的中断描述符中,也就是在中断 0x80 发生后,自动调用函数 system_call。
system_call定义在kernel/system_call.s
| |
此处我们只需要关注call sys_call_table(,%eax,4)这一句即可,前面的压栈和后面的一些调度相关的先不关心。
call sys_call_table(,%eax,4)实际上就是call sys_call_table + 4 * %eax,其中 eax就是之前设置的系统调用号,即 __NR_xxxxxx(定义在include/unistd.h)。
其中sys_call_table 是一个函数指针数组的起始地址,它定义在 include/linux/sys.h 中
| |
添加iam和whoami两个系统调用
在 include/linux/sys.h文件下作如下修改
| |
注意添加了这两个系统调用后需要到
kernel/system_call.s下修改nr_system_calls=72为nr_system_calls=74
内核中实现函数 sys_iam() 和 sys_whoami()
我们在kernel目录下新增who.c文件,实现这两个函数即可
,具体代码如下:
| |
修改 Makefile
我们还需要修改Makefile文件使得我们新增加的文件可以被编译链接
我们要修改kernel/Makefile文件的两处地方:
| |
改成:
| |
| |
改成
| |
编译linux源码
修改后需要进行编译
切换到linux0.11目录下执行以下命令
| |
一切顺利的话,会在当前目录下生成Image镜像文件

挂载文件系统镜像进行修改
在 oslab目录下,运行
| |
之后切换到oslab目录下的hdc目录下,该目录下就是挂载的文件系统,也就是linux0.11使用的文件系统。
切换到hdc后
修改unistd.h
我们需要在usr/include/unistd.h头文件中添加
| |
之后我们切换到usr/root目录下
添加程序验证系统调用
新增两个文件iam.c和whoami.c
iam.c内容如下:
| |
whoami.c内容如下:
| |
- 以上两个文件开头为什么要添加宏
#define __LIBRARY__?
我们可以打开
unistd.h文件,其中的定义系统调用的位置的地方有ifdef __LIBRARY__,所以我们需要在使用这个unistd.h中的这部分宏定义的地方前添加#define __LIBRARY宏,以使得宏定义生效

弄完后记得卸载这个文件系统
| |
运行bochs验证系统调用
首先在oslab目录下,执行以下命令
| |
运行截图


在/usr/root目录下,执行以下命令
| |

问题回答
- 从 Linux 0.11 现在的机制看,它的系统调用最多能传递几个参数?你能想出办法来扩大这个限制吗?
答:最多传递三个参数。
linux 0.11通过bx、cx、dx寄存器传递(ax作为系统调用号),这种方式受限于通用寄存器的数量。 解决办法:通过使用一个寄存器保存指向进程的用户态栈中的一块内存区域的地址,该内存中保存参数的值。
- 用文字简要描述向 Linux 0.11 添加一个系统调用 foo() 的步骤。
答:
- 需在
hdc/usr/include/unistd.h中添加__NR_foo对应的索引下标。- 首先可以在
kernel目录下编写sys_foo.c,里面包含系统调用的实现。- 还需要在
include/linux/sys.h文件中添加sys_foo函数声明和添加到系统调用指针数组- 并修改
kernel/system_call.s中的nr_system_calls(系统调用总数),即原来的值加1- 然后需要修改
kernel/Makefile- 编译源码生成系统镜像文件
- 之后可以在系统类库中编写
foo.c,调用系统调用sys_foo来实现系统调用库函数