fork()这个系统调用是有两个返回值的,在子进程中的返回值是0,在父进程中的返回值是PID,如下
关于0x80中断和特权级检查
在mian函数的sched_init()函数中调用宏:set_system_gate(0x80,&system_call);将0x80号中断安装到中断表里。可见将0x80中断安装进了head.s中定义的IDT表中的第0x80项,将中断函数入口偏移地址设置为system_call,将特权级设置为3。至于为什么没有看见可以自定义段描述符的地方,因为set_system_gate这个宏的作用是用来安装系统调用中断,而系统调用函数是内核函数,内核所在的代码段描述符值是0x8,这个是已经定死了的。安装描述符时不需要修改。
IDT表项安装好后,一旦用户程序执行0x80中断,则首先进行特权检查,用户程序的CPL为3,0x80中断的DPL也为3,因此可以执行该中断,中断将跳入内核函数中执行,硬件会自动得将0x8写入CS,将偏移地址写入EIP,从而执行systemcall,且CPL此时也已被修改为0,从而完成了特权级切换。
当使用jumpi指令进行段间跳转的时候会对比CS中的CPL和目标GDT中的代码段的DPL,段内跳转不对比;当使用mov的指令进行段间内存访问会对比DS中的CPL和目标GDT中的代码段的DPL,段内内存访问不对比,使用中断跳到中断服务程序的时候也会发生段间跳转,此时会对比CS中的CPL和IDT表项中的DPL。
使用bochs进行实验时,需要关注两个文件,一个是源码文件夹linux-0.11下的image文件,一个是oslab文件夹下的hdc-0.11-new.img 文件,源码文件夹下的image文件每次编译源码时都会重新生成,是内核代码文件;而.img是一个根文件系统镜像,他与/linux-0.11文件夹下的源码无关。
Bochs会虚拟出一个软盘和一个硬盘,软盘中放image,硬盘中放.img,bochs在启动是会从软盘启动,从而将image中的之前讲的bootset 、setup、 head和 sys内核程序按照之前讲过的顺序边读入内存边执行。当执行到main()中的init()函数时,这个函数的第一句话 setup((void *) &drive_info); 就是调用setup系统调用,安装根目录文件系统,如果此时没有.img文件,则可以在虚拟机页面中看到loading system…之后便报错,报错的语句是printk("Unable to read partition table of drive %dnr", 可以在代码中搜到,即在执行setup系统调用时报错。
文件系统加载完成后,就可以在shell中用ls查看各种文件和目录。当然也可以不启动linux0.11,而是在Ubuntu中将.img文件mount到某个目录下,则点进去看到的东西跟启动linux0.11后的根文件目录ls后看到的一样。
李志军老师的系统调用试验新增三个文件:who.c 、iam.c和whoami.c三个文件。其中who.c是新增的内核文件,写好放在源码的kernel文件夹下,至于为什么要用get_fs_byte来实现内存的访问,因为内核中的ds描述符默认是内核数据段,而我们实际也需要访问用户数据段,需要用到fs描述符,因此要加一层封装,这点在李老师书里详细说明。
who.c 写好后需要重新编译内核然后bochs加载进入shell,在shell中新建iam.c和whoami.c然后进行编译。编译出来的是用户程序,这两个c文件在编译的时候需要 编译一个_syscall2(int, whoami, char*, name, unsigned int, size);这种宏,宏中有一个变量是在unistd.h中定义的,要修改这个文件不应该在linux0.11的源码中找,而应该需要在文件系统中的/usr/include文件夹中找到并修改,因为源码此时已经编译成内核转载软驱载入内存中在跑,修改源码的源文件无意义。在shell的环境中找到include的文件,并进行修改。
#include <string.h>
#include <errno.h>
#include <asm/segment.h>char msg[24];int sys_iam(const char * name)
{char tep[26];int i = 0;for(; i < 26; i++){tep[i] = get_fs_byte(name+i);if(tep[i] == '0') break;}if (i > 23) return -(EINVAL);strcpy(msg, tep);return i;
}int sys_whoami(char * name, unsigned int size)
{int len = 0;for (;msg[len] != '0'; len++);if (len > size) {return -(EINVAL);}int i = 0;for(i = 0; i < size; i++){put_fs_byte(msg[i], name+i);if(msg[i] == '0') break;}return i;
}
图 who.c代码