系统调用实现 

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

HIT-OS 系统调用

/hdc/usr/root中的文件即为bochs中操作系统文件


2.修改/hdc/usr/include下的unistd.h

HIT-OS 系统调用

添加新建系统调用的名字的***  #define __NR_iam 72     #define __NR_iam  73

HIT-OS 系统调用

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

HIT-OS 系统调用

添加系统调用子程序(实现函数)声明,在系统调用表中添加他的名字。


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链接到一起

HIT-OS 系统调用

添加who.o

HIT-OS 系统调用

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

相关文章: