第10章linux系统调用接口ppt课件_第1页
第10章linux系统调用接口ppt课件_第2页
第10章linux系统调用接口ppt课件_第3页
第10章linux系统调用接口ppt课件_第4页
第10章linux系统调用接口ppt课件_第5页
已阅读5页,还剩32页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、 第第1010章章 系统调用接口系统调用接口系统调用接口的功能系统调用接口的功能内核为用户与硬件设备内核为用户与硬件设备(例如:例如:CPU,磁,磁盘,打印机等盘,打印机等)交互提供了一个接口。该交互提供了一个接口。该接口被称为系统调用接口。它的功能是:接口被称为系统调用接口。它的功能是:v运用户编程更加容易,把用户从学习硬件运用户编程更加容易,把用户从学习硬件设备的低级编程特性中解放出来。设备的低级编程特性中解放出来。v可以极大提高系统的平安性,由于内核接可以极大提高系统的平安性,由于内核接纳用户恳求之前,可以检查其合法性。纳用户恳求之前,可以检查其合法性。v运用系统调用接口使得程序具有良好

2、的可运用系统调用接口使得程序具有良好的可移植性。移植性。10.1 API10.1 API和系统调用和系统调用UNIX操作系统为编程员提供了运用编操作系统为编程员提供了运用编程接口程接口(API)。在在API提供的提供的libc规范函数库中,一部分规范函数库中,一部分是用户态的库函数,另一部分是系统调是用户态的库函数,另一部分是系统调用。库函数和系统调用的区别是:用。库函数和系统调用的区别是:库函数是一个函数定义,阐明如何获得一个给库函数是一个函数定义,阐明如何获得一个给定的效力,库函数代码不属于内核。定的效力,库函数代码不属于内核。系统调用是经过软件中断系统调用是经过软件中断(int指令指令)

3、向内核发出向内核发出的一个明确效力恳求,提供效力的代码属于内的一个明确效力恳求,提供效力的代码属于内核代码。核代码。为了区别库函数和系统调用,为了区别库函数和系统调用,libc规范规范C库库中,每个系统调用都有一个封装例程中,每个系统调用都有一个封装例程(wrapper routine)。运用程序经过这个封装例程来援用运用程序经过这个封装例程来援用API函函数库中的系统调用。数库中的系统调用。用户执行一个系统调用时,内核经过用户执行一个系统调用时,内核经过 (int 0 x80)软件中断或调用门从用户空间进入内软件中断或调用门从用户空间进入内核空间,这就是所谓的方式转换。核空间,这就是所谓的方

4、式转换。CPU切换到内核态开场执行与系统调用相切换到内核态开场执行与系统调用相对应内核函数。执行终了后内核将执行结对应内核函数。执行终了后内核将执行结果和控制权还给用户进程。果和控制权还给用户进程。图图10-6给出调用系统调用的表示图。给出调用系统调用的表示图。调用一个系统调用表示图调用一个系统调用表示图 printf() 在运用程序中在运用程序中调用系统调用调用系统调用printf() int 0 x80 在在libclibc库中库中的封装例程的封装例程用户态用户态system_call: sys_printf() ret_from_sys_call iretsys_printf() 内核态

5、内核态系统调用途置系统调用途置机制机制系统调用效系统调用效力例程力例程图图10-6 系统调用表示图系统调用表示图10.2 10.2 方式转换的硬件处方式转换的硬件处置置在在i386中,完成系统调用接口方式转换中,完成系统调用接口方式转换的硬件是圈套门和调用门,软件是的硬件是圈套门和调用门,软件是int 0 x80指令或调用指令指令或调用指令下面分析圈套门技术。下面分析圈套门技术。注:调用门技术请读者见教材注:调用门技术请读者见教材P227由由于通常情况下,在于通常情况下,在Linux内核没有运用调内核没有运用调用门这里不做引见。用门这里不做引见。10.2.1 10.2.1 圈套门方式转换圈套门

6、方式转换系统调用属于软件中断。系统调用属于软件中断。i386维护方式下的软件中断运用圈套门描维护方式下的软件中断运用圈套门描画符。画符。运用圈套门执行软件中断,不会影响运用圈套门执行软件中断,不会影响硬件中断恳求。硬件中断恳求。Linux为系统调用设置的圈套门向量号是为系统调用设置的圈套门向量号是128,即,即16进制的进制的“80H。i386运用的圈套运用的圈套(trap)指令是指令是int $0 x80。图图10-1给出圈套门进展方式转换的表示图。给出圈套门进展方式转换的表示图。系统调用的效力例程系统调用的效力例程 system_call()处置程序处置程序 中断描画符表中断描画符表 of

7、fset dpl 01111 selector offset GATE 0baseaddr limitdpl bseaddr baseaddr limit IDT的基址的基址 限长限长selector base addr limit中断描画符存放器中断描画符存放器IDTRCS(代码段存放器代码段存放器)代码段描画符高速缓存代码段描画符高速缓存 system_call()起始地址起始地址 物理存储器物理存储器一个门描一个门描画符占画符占8B。在在GDT或或LDT中的中的代码段描代码段描画符。画符。圈套门圈套门 80H图图10-1 圈套门方式转换图圈套门方式转换图10.3 10.3 系统调用接口系

8、统调用接口i386维护方式下,系统调用接口由软件维护方式下,系统调用接口由软件和硬件共同协作完成。和硬件共同协作完成。下面讲解系统调用途置程序下面讲解系统调用途置程序system_call的任务原理。的任务原理。10.3.1 10.3.1 初始化系统调初始化系统调用用系统启动内核初始化期间调用系统启动内核初始化期间调用trap_init函数建立函数建立IDT表中向量号为表中向量号为128对应的表对应的表项项(8个字节的圈套门描画符个字节的圈套门描画符)。代码如下:。代码如下:set_system_gate(SYSCALL_VECTOR,&system_call);#define SYSCALL

9、_VECTOR0 x80上面两行代码等效于下面的语句:上面两行代码等效于下面的语句:set_system_gate(0 x80, &system_call);set_system_gate()初始化初始化80H开场的开场的8个字个字节的圈套门描画符。步骤如下:节的圈套门描画符。步骤如下:将内核代码段选择符将内核代码段选择符_KERNEL_CS装入装入80H圈套门的圈套门的2、3两个字节。两个字节。将将system_call()可执行代码的第一条指令可执行代码的第一条指令偏移量装入偏移量装入80H圈套门的圈套门的0、1、6和和7共共4个字节中。个字节中。将将15添入添入80H圈套门类型号字段,阐

10、明这圈套门类型号字段,阐明这是一个圈套门。是一个圈套门。将门描画符将门描画符DPL字段设置为字段设置为3,允许用户,允许用户进程调用进程调用system_call()程序。程序。10.3.2 10.3.2 系统调用执行流程系统调用执行流程 在封装例程中含有在封装例程中含有int $0 x80汇编指令代码汇编指令代码 int $0 x80使该调用进入内核:使该调用进入内核:system_call() 该系统调用被执行,并前往执行结果该系统调用被执行,并前往执行结果 该系统调用由规范该系统调用由规范C库的封装例程宏来引导库的封装例程宏来引导 用户发出一个系统调用的恳求用户发出一个系统调用的恳求 由

11、由system_call找到指定的系统调用函数找到指定的系统调用函数图图10-4 系统调用的执行流程系统调用的执行流程10.3.3 10.3.3 封装例程封装例程system_call()是系统调用入口点。是系统调用入口点。系统调用主要供用户编程运用,但也可系统调用主要供用户编程运用,但也可以被内核态线程调用。以被内核态线程调用。为了简化系统调用的调用过程,为了简化系统调用的调用过程,Linux提提供的封装例程是一组预处置宏。共定义供的封装例程是一组预处置宏。共定义了了6个宏:个宏:v 从从_syscall0到到_syscall5#define _syscallN(type,name,type

12、1,arg1,type2,arg2,type3,arg3,. . .) type name(type1 arg1,type2 arg2,type3 arg3,. . .) long _res; _asm_ volatile (int $0 x80 : =a (_res) : 0 (_NR_#name),b (long)(arg1),c (long)(arg2), d (long)(arg3); . . . . . ._syscall_return(type,_res); 程序清单程序清单10-1 封装例程:封装例程:程序清单程序清单10-1给出封装例程的定义:给出封装例程的定义:封装例程的解释

13、封装例程的解释_syscallN中的中的“N是系统调用的参数个是系统调用的参数个数。数。前两个字符串指明系统调用的前往值类型前两个字符串指明系统调用的前往值类型和名字;和名字;紧随其后的每一对参数指明该系统调用所紧随其后的每一对参数指明该系统调用所需求的其他参数的类型和名字。需求的其他参数的类型和名字。fork()系统调用无参数,它的封装例程是:系统调用无参数,它的封装例程是: 系统调用名字系统调用名字调用参数个调用参数个数数前往类型值前往类型值_syscall0(int,fork)write()系统调用有系统调用有3个参数,其封装例程个参数,其封装例程宏指令格式是:宏指令格式是:可以按照程序

14、清单可以按照程序清单10-1把把_syscall3(int,write,)宏展开成下面的代码:宏展开成下面的代码:_syscall3(int,write,int,fd, const char*, buf, unsigned int,count)参数个数参数个数其中,兰色字符串是其中,兰色字符串是write()系统调用需系统调用需求的求的3对参数。对参数。int write(int fd, const char* buf, unsigned int count) long _res; _asm_ volatile (int $0 x80 : =a (_res) : 0 (_NR_write),b

15、 (long)fd),c (long)buf), d (long)count);if (unsigned long)_ _res =(unsigned long)-125) errno = -_ _res; _ _res = -1;return(int) _ _res;它是系统调用号,来自它是系统调用号,来自_syscall3()的第的第2个参数个参数对这个函数进展编译,生成的汇编代码如对这个函数进展编译,生成的汇编代码如下:下:write: pushl %ebx ;将将ebx内容进栈内容进栈 movl 8(%esp) , %ebx ;将第一个参数放入将第一个参数放入ebx movl 12(%

16、esp), %ecx ;将第二个参数放入将第二个参数放入ecx movl 16(%esp), %edx ;将第三个参数放入将第三个参数放入edx movl $4 , %eax ;将将_ _NR_write放入放入eax int $0 x80 ;执行系统调用执行系统调用 cmpl $-125,%eax ;检测前往码检测前往码 jbe .L1 ;如无错跳转如无错跳转 negl %eax ;求求eax的补码的补码 movl %eax, errno ;将结果放入将结果放入errno movl $-1, %eax ;将将eax置为置为-1 .L1: popl %ebx ;从堆栈弹出从堆栈弹出ebx re

17、t ;前往调用程序前往调用程序从汇编代码中可知传送给从汇编代码中可知传送给write()的参数的参数是在执行是在执行int 0 x80指令之前就被装入到指令之前就被装入到CPU各个存放器中。各个存放器中。假设假设eax中的前往值为中的前往值为-1到到-125之间,将之间,将被解释为错误码。否那么前往被解释为错误码。否那么前往eax中的值,中的值,阐明调用胜利。阐明调用胜利。10.3.410.3.4系统调用号与系统调用系统调用号与系统调用表表system_call()函数是内核中一切系统调用函数是内核中一切系统调用的独一入口点,因此内核要为每个系统的独一入口点,因此内核要为每个系统调用编一个序号

18、,这个序号叫做系统调调用编一个序号,这个序号叫做系统调用号。用号。从上一节的封装例程代码中可以看到,从上一节的封装例程代码中可以看到,执行执行int 0 x80之前,系统调用号已被放在之前,系统调用号已被放在eax中。中。程序清单程序清单10-2给出了部分系统调用号。给出了部分系统调用号。#define _NR_exit 1#define _NR_fork 2#define _NR_read 3#define _NR_write 4#define _NR_open 5#define _NR_close 6 #define _NR_ removexattr 235 #define _NR_lre

19、movexattr 236 #define _NR_fremovexattr 237程序清单程序清单10-2 系统调用号系统调用号系统调用表系统调用表系统调用表是指向各系统调用函数指针组系统调用表是指向各系统调用函数指针组成的表。成的表。系统调用号是系统调用表中各表项的相对系统调用号是系统调用表中各表项的相对偏移量。偏移量。执行执行system_call()时,根据时,根据eax中的系统调中的系统调用号,定位该系统调用函数在系统调用表用号,定位该系统调用函数在系统调用表(sys_call_table)中的准确位置。中的准确位置。程序清单程序清单10-3给出了部分系统调用表。给出了部分系统调用表

20、。 .dataENTRY(sys_call_table) .long SYMBOL_NAME(sys_ni_syscall) / 0-old setup() / system call.long SYMBOL_NAME(sys_exit).long SYMBOL_NAME(sys_fork).long SYMBOL_NAME(sys_read).long SYMBOL_NAME(sys_write).long SYMBOL_NAME(sys_open) / 5 程序清单程序清单10-3 系统调用表系统调用表 10.3.5system_call 10.3.5system_call 的执行过程的执

21、行过程中心栈中心栈 old SS old ESP old EFLAGS old CS old EIP 031SS:ESP(从从TSS中获取中心栈堆栈指针中获取中心栈堆栈指针)栈的扩展方向栈的扩展方向SS:ESP最新中心栈的栈指针最新中心栈的栈指针图图10.5 运用圈套门有特权级改动上下文切换后的堆栈运用圈套门有特权级改动上下文切换后的堆栈 系统执行系统执行int $0 x80指令进展方式转换时,指令进展方式转换时,中心栈的规划如图中心栈的规划如图10-5所示。所示。ENTRY(system_call)pushl %eax / save orig_eax ,首先保管首先保管EAX存放器的内容。存

22、放器的内容。SAVE_ALL / 保管各通用存放器的内容。保管各通用存放器的内容。GET_CURRENT(%ebx) / 获取调用进程的获取调用进程的task_struct构造的指针。构造的指针。 cmpl $(NR_syscalls),%eax / 检测系统调用能否合法系统调用。检测系统调用能否合法系统调用。jae badsys / 假设该系统调用号越界那么跳转到标号假设该系统调用号越界那么跳转到标号badsys处。处。testb $0 x20,flags(%ebx) / 检测能否设置了检测能否设置了PF_TRACESYS标志。标志。jne tracesys /假设设置了假设设置了PF_TR

23、ACESYS标志那么跳转到标号标志那么跳转到标号tracesys处。处。call *SYMBOL_NAME(sys_call_table)(,%eax,4) /这里是调用并执行该这里是调用并执行该 /系统调用函数的地方系统调用函数的地方movl %eax,EAX(%esp) /save the return value,系统调用前往,系统调用前往, / 前往值进栈保管。前往值进栈保管。ret_from_sys_call:函数函数system_call()的源代码如下:的源代码如下:函数函数system_call()是一切系统调用的入口点。是一切系统调用的入口点。在源代码中的调用语句是:在源代码

24、中的调用语句是:call *SYMBOL_NAME(sys_call_table)(0,%eax,4)该语句调用与系统调用号该语句调用与系统调用号(在在eax中中)相对应的相对应的效力例程。效力例程。系统调用表的每一个表项占系统调用表的每一个表项占4个字节,因此个字节,因此把系统调用号乘以把系统调用号乘以4,再加上系统调用表的,再加上系统调用表的基址,就找到了指向该效力例程的地址,也基址,就找到了指向该效力例程的地址,也就找到了要执行的系统调用函数。就找到了要执行的系统调用函数。里边是系统调用里边是系统调用号号当效力例程执行终了时,可以从当效力例程执行终了时,可以从eax中获中获得前往值,并把

25、这个前往值进栈保管。得前往值,并把这个前往值进栈保管。当进程恢复到本人的用户态执行时,就当进程恢复到本人的用户态执行时,就可以在可以在eax中找到该系统调用的前往值。中找到该系统调用的前往值。注:由于注:由于lcall7代码很少运用,所以不在代码很少运用,所以不在PPT中讲解。中讲解。10.5 10.5 添加新系统调用实添加新系统调用实例例经过学习向内核添加一个新系统调用的过经过学习向内核添加一个新系统调用的过程,进一步了解系统调用的根本原理。程,进一步了解系统调用的根本原理。实例:新系统调用的功能是获取系统当前实例:新系统调用的功能是获取系统当前系统时间并显示在屏幕上。步骤如下:系统时间并显

26、示在屏幕上。步骤如下:编写新系统调用源程序,新系统调用的称编写新系统调用源程序,新系统调用的称号为:号为: int ptimeofday(struct timeval *tv, struct timezone*tz); 新系统调用的源程序如下:新系统调用的源程序如下:#include extern struct timezone sys_tz;asmlinkage int sys_ptimeofday(struct timeval *tv, struct timezone *tz) if (tv) struct timeval ktv; do_gettimeofday(&ktv); if (c

27、opy_to_user(tv, &ktv, sizeof(ktv) return -EFAULT; printk(Date:%09d%09d, ktv.tv_sec, ktv.tv_usec); if (tz) if (copy_to_user(tz, &sys_tz, sizeof(sys_tz) return -EFAULT; printk(Thetimeis: %09d%09d,sys_tz.tz_minuteswest,sys_tz.tz_dsttime); return 0; ptimeofday.c程序程序 衔接新的系统调用,将衔接新的系统调用,将ptimeofday.c代代码添加

28、到码添加到/usr/linux/kernel/sys.c文件中,文件中,同时需求重新编辑同时需求重新编辑2个文件个文件: 在文件在文件/usr/src/linux/include/asm/unistd.h中中为新系统调用分配一个系统调用号为为新系统调用分配一个系统调用号为239。即在表即在表10-2中的最后再添加一行:中的最后再添加一行: # define _NR_ptimeofday 239在文件在文件/usr/src/linux/arch/asm/kernel/entry.S中,即在表中,即在表10-3中的最后再添加一行:中的最后再添加一行: .long SYMBOL_NAME(sys_p

29、timeofday)编译新的编译新的Linux内核。编译内核要以超内核。编译内核要以超级用户身份登录,命令行如下:级用户身份登录,命令行如下:# make xconfig# make dep# make bzImage编译终了后,系统将产生一个新紧缩的内编译终了后,系统将产生一个新紧缩的内核映像文件:核映像文件: # usr/src/linux/arch/boot/bzImage为了安装新系统内核,应该将上面产生的为了安装新系统内核,应该将上面产生的新内核映像文件拷贝到新内核映像文件拷贝到/boot/目录下:目录下: # cp usr/src/linux/arch/boot/bzImage /

30、boot/bzImage-new启动新的内核操作系统:启动新的内核操作系统:假设他的机器运用假设他的机器运用grub加载加载OS,可参照,可参照教材表教材表8-2内容修正内容修正/etc目录下的目录下的grub.conf文件。文件。假设运用假设运用lilo加载加载OS,需求在已有的系统,需求在已有的系统中修正中修正/etc/lilo.conf文件。可参照表文件。可参照表10-1原有的原有的/etc/lilo.conf文件文件 修正后的修正后的/etc/lilo.conf文件文件 prompt prompt timeout=50 timeout=50default=linux default=l

31、inuxboot=/dev/had7 boot=/dev/had7map=/boot/map map=/boot/mapinstall=/boot/boot.b install=/boot/boot.bmassage=/boot/message massage=/boot/messagelba32 lba32image=/boot/vmlinuz-2.4.18 image=/boot/bzImage-newlabel=linux label=linux-newinitrd=/boot/initrd-2.4.18.img image=/boot/vmlinuz-2.4.18read-only label=linuxroot=/dev/hdb8 initrd=/boot/initrd-2.4.18.imgother=/dev/hda1 read_onlyoptional root=/dev/hdb8la

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论