Linux内核分析之四——系统调用的工作机制

作者:姚开健

原创作品转载请注明出处

《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()):

Linux内核分析之四——系统调用的工作机制_第1张图片

这可以简化为系统调用三层皮: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;
}
这是编译运行后程序一的执行结果:

Linux内核分析之四——系统调用的工作机制_第2张图片

程序二:

# 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;
}
编译运行程序二的执行结果:

Linux内核分析之四——系统调用的工作机制_第3张图片

由程序二里汇编代码:

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。

你可能感兴趣的:(Linux内核分析之四——系统调用的工作机制)