系统调用实现
1.Ubuntu和Linux 0.11之间的文件交换
oslab下的hdc-0.11-new.img是0.11内核启动后的根文件系统镜像文件,相当于在bochs虚拟机里装载的硬盘。在Ubuntu上访问其内容的方法是(大家使用sudo时,password是shiyanlou):$ sudo ./mount-hdc
之后,hdc目录下就是和0.11内核一模一样的文件系统了,可以读写任何文件(可能有些文件要用sudo才能访问)。读写完毕,不要忘了卸载这个文件系统:
$ sudo umount hdc
经过sudo ./mount-hdc这样处理以后,我们可以在Ubuntu的hdc目录下创建一个xxx.c文件,然后利用Ubuntu上的编辑工具(如gedit等)实现对xxx.c文件的编辑工作,在编辑保存以后。执行sudo umount hdc后,再进入Linux 0.11(即run启动bochs以后)就会看到这个xxx.c(即如下图所示),这样就避免了在Linux 0.11上进行编辑xxx.c的麻烦,因为Linux 0.11作为一个很小的操作系统,其上的编辑工具只有vi,使用起来非常不便。
来源: 实验楼
链接: https://www.shiyanlou.com/courses/115
由于需要在linux-0.11上编写应用程序,所以需要在ubuntu上挂载文件系统,此时hdc目录下就是linux0.11的文件系统 在oslab目录下使用 sudo ./mount-hdc
/hdc/usr/root中的文件即为bochs中操作系统文件
2.修改/hdc/usr/include下的unistd.h
添加新建系统调用的名字的*** #define __NR_iam 72 #define __NR_iam 73
unistd.h中还有函数的宏定义,作用是向库函数中添加一个新函数.进入内核态的中断服务函数system_call
3.进入中断服务函数,修改系统调用总个数
nr_system_calls =74;
system_call:
cmpl $nr_system_calls-1,%eax
ja bad_sys_call
push %ds
push %es
push %fs
pushl %edx
pushl %ecx # push %ebx,%ecx,%edx as parameters
pushl %ebx # to the system call
movl $0x10,%edx # set up ds,es to kernel space
mov %dx,%ds
mov %dx,%es
movl $0x17,%edx # fs points to local data space
mov %dx,%fs
call sys_call_table(,%eax,4) //调用系统调用真正的子程序 eax中存的是前面名字define的值
pushl %eax
movl current,%eax
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
中断服务函数的任务是调用sys_call_table()函数用于调用系统调用真正的子函数。
系统调用是用eax、ebx、ecx、edx寄存器来传递参数的。其中eax传递了系统调用号,而ebx、ecx、edx是用来传递函数的参数的,其中ebx对应第一个参数,ecx对应第二个参数,依此类推。如open所传递的文件名指针是由ebx传递的,也即进入内核后,通过ebx取出文件名字符串。open的ebx指向的数据在用户空间,而当前执行的是内核空间的代码,如何在用户态和核心态之间传递数据?接下来我们继续看看open的处理:处理方法就很显然了:用get_fs_byte()获得一个字节的用户空间中的数据。所以,在实现iam()时,调用get_fs_byte()即可。
4.修改/hdc/usr/include/linux的sys.h
添加系统调用子程序(实现函数)声明,在系统调用表中添加他的名字。
5.创建who.c 编写系统调用函数sys_iam()和sys_whoami()
//who.c
#include <errno.h>
#include <asm/segment.h>
char buf[24];
int sys_iam(const char *name)
{
int count;
int i;
char temp[24];
for (count = 0; count < 24; count++)
{
temp[count] = get_fs_byte(name + count);
if (temp[count] == '\0')
break;
}
if (count == 24)
return -EINVAL;
for (i = 0; i <= count; i++)
buf[i] = temp[i];
return count;
}
int sys_whoami(char *name, unsigned int size)
{
int count = 0;
int i = 0;
while (buf[count] != '\0')
count++;
if (size < count)
return -EINVAL;
while (i < count)
{
put_fs_byte(buf[i], &name[i]);
i++;
}
return count;
}
6.修改./linux-0.11/makefile ,使其能把who.c链接到一起
添加who.o
dependencies中添加这句.
在oslab目录下输入命令 make all 就编译好内核了.
至此操作系统的系统调用就添加完毕.
系统调用接口测试
1.编写用户程序
// iam.c
#define __LIBRARY__
#include <unistd.h>
_syscall1(int, iam, const char*, name)
int main(int argc, char *argv[])
{
iam(argv[1]);
return 0;
}
//whoami.c
#define __LIBRARY__
#include <unistd.h>
_syscall2(int, whoami,char*,name,unsigned int,size);
int main()
{
char s[24];
whoami(s,24);
printf("%s",s);
return 0;
}
接下来在oslab目录下输出./run 就运行linux-0.11,之后编译链接用户程序,完成系统调用接口测试.
总结
- 应用程序调用库函数(API);
- API将系统调用号存入EAX,然后通过中断调用使系统进入内核态;
- 内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
- 系统调用完成相应功能,将返回值存入EAX,返回到中断处理函数;
- 中断处理函数返回到API中;
-
API将EAX返回给应用程序。
来源: 实验楼
链接: https://www.shiyanlou.com/courses/115