1.内核态和用户态
对于一台计算机来说,有些硬件的控制直接影响到计算机是否可以稳定的运行。操作系统为了维持计算机的可持续的工作,要对这些可能影响到系统稳定的因素进行严格控制,不能让用户随意进行操作。这就是操作系统区分内核态和用户态的思想。对于一些可能影响系统稳定的函数,操作系统将它们放到内核态当中,在用户态中用户只能调用一些用户态的函数,而对于一些对内核函数,用户只能通过中断进入内核态中进行调用。下图简略的说明了用户态和内核态的程序调用关系(截图来自ppt)
上图中xyz()是用户态的函数,这个函数需要调用一个内核态的函数sys_xyz()来完成它的功能。这里可以看出它首先通过int 0x80进行中断,然后对进入内核态System call handler中进行相关的寄存器的保存工作,然后调用系统调用sys_xyz()。当sys_xyz()完成后返回System call handler进行相关寄存器的复原,过程类似于程序之间调用的过程。随后再返回xyz()中。
除了程序分为用户态和内核态之外,内存的访问也区分内核态和用户态,以下是32位地址的内核态和用户态的访问控制。
0xc00000000以上地址在内核态访问
0x00000000-0xbfffffff 地址内核态和用户态都可以访问(这里注意并不是仅仅只有用户态可以访问)
2.实验
下边利用C语言嵌入式汇编代码的方式实现对fork函数的系统调用,linux中系统调用号可以查看以下网址,这里可以看出fork的系统调用号是2.
http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/syscalls/syscall_32.tbl
实验代码如下,很简单
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 #include<sys/stat.h> 5 6 int main(void) 7 { 8 pid_t n; 9 asm volatile( 10 "mov $0x2,%%eax " 11 "int $0x80 " 12 "mov %%eax,%0 " 13 :"=m"(n) 14 ); 15 if(0 == n) 16 { 17 printf("this is child process "); 18 } 19 else 20 { 21 printf("this is parents process "); 22 } 23 return 0; 24 }
实验结果
通过实验结果,我们可以看出,我们成功的通过这种嵌入式汇编的方式,实现了fork的系统调用。这里主要分析下上图中的9-14行的代码:
因为系统主要通过eax判定系统调用,而fork的系统调用为2,所以10行中将2传递给eax。又由于fork无需传入参数,所以这里不需要对ebx进行设置。
紧接着11行就通过int 进入内核态执行fork的系统调用。由于系统的传入参数是通过eax传出的,所以12行中将eax传入到,%0,%0是指下边的n。-m表示n存储在内存当中。
3.总结
这一节主要通过实验,了解了系统如何进行系统调用的,加深了对内核态和用户态的理解。操作系统平常工作都在用户态当中,当用户态需要操作一些内核数据时,才会通过系统调用进入内核态,而这些系统调用都是操作系统本身实现了,用户无法修改。可以看出通过这种方式,操作系统实现了对关键数据的保护,从而维护了系统的高效稳定运行。