作者:姚开健
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
学过计算机操作系统的都知道,CPU工作时有两种状态,一种是用户态,一种是内核态,用户态意味着代码访问的范围会受到限制,在32位X86的机器上,4G的内存里,在用户态的时候,只能访问0x00000000-0xbfffffff的地址空间。而内核态则不受限制,可以访问任意内存地址。当程序在用户态运行时,如果需要进入内核态执行,则会产生一个中断,然后系统进行中断处理,先是保存当前运行现场,将用户态栈顶指针,状态字,cs:eip值压栈:
然后进行中断处理程序,等处理程序运行结束后,则需要恢复现场:
当我们进行编程时,可以利用系统封装好的API来间接进行调用系统调用。一个API里面可能对应一个系统调用,也可能一个也没有,封装例程会将系统调用封装好,一个例程往往对应一个系统调用,当用户程序在执行API(xyz())调用时,将会进入封装例程(xyz())中,由封装例程里的中断汇编代码调用系统调用处理程序(system_call),然后系统调用处理程序调用系统调用服务程序(sys_xyz()):
这可以简化为系统调用三层皮:API,中断向量所指向的系统调用处理程序System_Call,中断服务程序Sys_xyz()。
进行系统调用时,如跟函数调用一般,也需要进行参数传递。系统调用至少有一个参数,就是系统调用号,通常会把它 传入eax寄存器,其他参数则传入其他寄存器,最多为6个参数,如果参数超过6个,则把最后一个寄存器该为指向一块剩余参数内存的指针。
下面通过两个程序来演示系统调用的工作机制:
程序一是直接调用sysinfo系统调用,返回系统信息,程序二则通过汇编代码调用系统调用,展示参数传递过程。
# include <stdio.h> # include <sys/sysinfo.h> int main(int argc, char *argv[]) { struct sysinfo *info; error = sysinfo(info); printf("\n\ncode error = %d\n", error); printf("uptime: %d\n" "tatal ram: %d\n" "free ram: %d\n" "shared ram: %d\n" "buffer ram: %d\n" "tatal swap: %d\n" "free swap: %d\n" "process num: %d\n" "tatal high: %d\n" "free high: %d\n" "mem_unit: %d\n", sys->uptime, sys->tatalram, sys->freeram, sys->sharedram, sys->bufferram, sys->tatalswap, sys->freeswap, sys->procs, sys->tatalhigh, sys->freehigh, sys->mem_unit ); return 0; }这是编译运行后程序一的执行结果:
程序二:
# include <stdio.h> # include <sys/sysinfo.h> int main(int argc, char *argv[]) { struct sysinfo *info; int error; asm volatile( "mov %1, %%ebx\n\t" "mov $0x74, %%eax\n\t" "int $0x80\n\t" "mov %%eax, %0\n\t" : "=m" (error) : "b" (info) ); printf("\n\ncode error = %d\n", error); printf("uptime: %d\n" "tatal ram: %d\n" "free ram: %d\n" "shared ram: %d\n" "buffer ram: %d\n" "tatal swap: %d\n" "free swap: %d\n" "process num: %d\n" "tatal high: %d\n" "free high: %d\n" "mem_unit: %d\n", sys->uptime, sys->tatalram, sys->freeram, sys->sharedram, sys->bufferram, sys->tatalswap, sys->freeswap, sys->procs, sys->tatalhigh, sys->freehigh, sys->mem_unit ); return 0; }编译运行程序二的执行结果:
由程序二里汇编代码:
asm volatile( "mov %1, %%ebx\n\t" "mov $0x74, %%eax\n\t" "int $0x80\n\t" "mov %%eax, %0\n\t" : "=m" (error) : "b" (info) );首先是把sysinfo系统调用的结构体指针参数传入ebx寄存器,接着把系统调用号0x74传入eax寄存器,接着进中断Int $0x80,最后把系统调用返回结果给error变量。可以看出,没有程序一的sysinfo(info),程序二一样成功调用系统调用sysinfo。