20135125陈智威
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
系统调用:库函数封装了系统调用,通过库函数和系统调用打交道
用户态:低级别执行状态,代码的掌控范围会受到限制。
内核态:高执行级别,代码可移植性特权指令,访问任意物理地址
为什么划分级别:如果全部特权,系统容易崩溃
系统调用的三层皮:xyz,system_call,sys_xyz。即:API,中断向量,服务程序。
xyz()函数是系统调用对应的API,这个应用程序编程接口里面封装了一个系统调用,这个系统调用会触发一个int0x80的中
断,产生向量为128的编译异常,0x80这个中断向量对应着system_call这个内核代码的起点,这个内核代码里面会
有SAVE_ALL,然后执行到sys_xyz()中断服务程序,进入程序里面处理,在中断服务程序执行完之后会ret_from_sys_call,在return的过程
中可能会发生进程调度,如果没有进程调度,就会iret,回到用户态接着执行。
内核实现了很多不同的系统调用, 进程必须指明需要哪个系统调用,这需要传递一个名为系统调用号的参数
system_call是Linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号。具体过程如下:
1.一个应用程序调用fork()封装例程,那么在执行int $0x80之前就把eax寄存器的值置为2(即__NR_fork)。 2.这个寄存器的设置是libc库中的封装例程进行的
,因此用户一般不关心系统调用号 3.进入sys_call之后,立即将eax的值压入内核
堆栈
寄存器传递参数也有相应的限制:
1.每个参数长度不能超过寄存器的长度(32位)
2.在eax外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp),超过6个就使用指针调用某一个块的内存。
3、使用库函数API和C代码中嵌入汇编代码触发同一个系统调用:
使用库函数获取系统当前的时间:
命令:
编写time程序:vi time.c
编译:gcc time.c -o time -m32
执行:./time
实验总结:
我认为系统调用的工作机制是:当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数,由API、中断向量和中断处理程序协调完成。
而在系统调用的过程中,系统调用号使用eax寄存器传递参数。