Linux内核分析 —— 【实验四:系统调用 】
“计算机科学领域的任何问题都可以通过增加一个中间层来解决。”这句名言几乎概括了整个计算机体系架构的设计要点。我们都知道计算机由硬件系统和软件系统构成,后者是建立在前者的基础上。硬件的性能在制造产商生产出来的时候就已经确定了,而软件系统却有着更大的可操作性。为了更加便利,高效,有条不紊地利用硬件系统,我们增加了一层——操作系统。此外,操作系统之上还有应用软件,操作系统除了要管理硬件资源,还要为应用程序开发人员提供良好的环境来使应用软件程序具有更好的兼容性,为了达到这个目的,系统内核提供一系列具备预定功能的多内核函数,通过一组称为系统调用(system call)的接口呈现给用户。系统调用把应用程序的请求传给内核,调用相应的的内核函数完成所需的处理,将处理结果返回给应用程序。
一 系统调用概念
系统调用(System Call)是操作系统为在用户态运行的进程与硬件设备(如CPU、磁盘、打印机等)进行交互提供的一组接口。
二 系统调用的意义
(1)把用户从底层的硬件编程中解放出来
(2)极大的提高了系统的安全性
(3)使用户程序具有可移植性
三 系统调用机制
当用户进程需要发生系统调用时,CPU 通过软中断切换到内核态开始执行内核系统调用函数。Linux 下有三种发生系统调用的方法:
(1)通过 glibc 提供的库函数
(2)使用 syscall 函数直接调用
(3)通过 int 0x80指令陷入
总的来说,前两种最终都会通过int 0x80指令陷入进入中断处理程序。而系统调用也需要输入输出参数,例如实际的值,用户态进程地址空间的变量的地址,甚至是包含指向用户态函数的指针的数据结构的地址等。
system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号,其他参数依次由ebx,ecx,edx,esi,edi,ebp传入。
• 寄存器传递参数具有如下限制:
• 1)每个参数的长度不能超过寄存器的长度,即32位
• 2)在系统调用号(eax)之外,参数的个数不能超过6个(ebx,
ecx,edx,esi,edi,ebp)
注意:如果超过六个怎么办呢?可以传入一个地址,地址所在地存放多个参数。
四 系统调用与应用编程接口
应用编程接口 (application program interface, API)只是一个函数定义,系统调用通过软中断向内核发出一个明确的请求,而gLibc库定义的一些API引用了封装例程(wrapper routine,唯一目的就是发布系统调用),一般每个系统调用对应一个封装例程,库再用这些封装例程定义出给用户的API。如下图
系统调用的三个层次依次是:xyz函数(API)、system_ call(中断向量)和 sys_ xyz(中断服务程序)。
五 例子
(1)通过API调用打开一个文件,并且写入一串字符串:
syscall.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(){
int fd; //文件描述符
char *file="./test"; //文件路径
char *buf="this is a string!"; //写入字符串
fd=open(file,O_CREAT|O_RDWR); //打开文件,创建并读写的权限
write(fd,buf,strlen(buf)); //写入字符串
close(fd); //关闭文件
return 0;
}
运行结果:
(2)通过int 0x80陷入
syscallasm.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
int main(){
int fd,len;
char *file="./test";
char *buf="this is a new string!";
len=strlen(buf);
asm volatile(
"movl %1,%%ebx\n\t" //将第一个参数file赋给ebx
"movl $102,%%ecx\n\t" //将立即数102赋值给ecx,代表读写文件
"movl $5,%%eax\n\t" //open函数的系统调用号是5,eax=5
"int $0x80\n\t"
"movl %%eax,%0\n\t" // fd=eax ,保存文件描述符
"movl %0,%%ebx\n\t" //ebx=fd
"movl %2,%%ecx\n\t" // ecx=buf
"movl %3,%%edx\n\t" // edx=len
"movl $4,%%eax\n\t" //eax=4,write函数的系统调用号为4
"int $0x80\n\t"
"movl %0,%%ebx\n\t" //ebx=0
"movl $6,%%eax\n\t" //eax=6 ,close函数
"int $0x80\n\t"
:"=m"(fd)
:"m"(file),"m"(buf),"m"(len)
);
return 0;
}
运行结果:
=========== 王杰 原创作品转载请注明出处==============
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”