ImageVerifierCode 换一换
格式:DOCX , 页数:17 ,大小:404.25KB ,
资源ID:30017423      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/30017423.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(linux011系统调用原理及实验总结.docx)为本站会员(b****8)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

linux011系统调用原理及实验总结.docx

1、linux011系统调用原理及实验总结Linux0.11系统调用原理及实验总结1系统调用的原理1.1概述系统调用是一个软中断,中断号是0x80,它是上层应用程序与Linux系统内核进行交互通信的唯一接口。通过int 0x80,就可使用内核资源。不过,通常应用程序都是使用具有标准接口定义的C函数库间接的使用内核的系统调用,即应用程序调用C函数库中的函数,C函数库中再通过int 0x80进行系统调用。所以,系统调用过程是这样的:应用程序调用libc中的函数libc中的函数引用系统调用宏系统调用宏中使用int 0x80完成系统调用并返回。 另外一种访问内核的方式是直接添加一个系统调用,供自己的应用程

2、序使用,这样就不再使用库函数了,变得更为直接,效率也会更高。1.2相关的数据结构在说具体的调用过程之前,这里先要说几个数据结构。1.2.1系统调用函数表系统调用函数表sys_call_table是在sys.h中定义的,它是一个函数指针数组,每个元素是一个函数指针,它的值是各个系统提供的供上层调用的系统函数的入口地址。也就是说通过查询这个表就可以调用软中断0x80所有的系统函数处理函数。1.2.2函数指针偏移宏这是一系列宏,它们的定义在unistd.h中,基本形式为define _NR_name value,name为系统函数名字,value是一个整数值,是name所对应的系统函数指针在sys_

3、call_table中的偏移量。1.2.3系统调用宏系统调用宏_syscalln(type,name)在内核的unistd.h文件中定义的,对它展开就是:type name(参数列表) 调用过程;其中,n为参数个数,type为函数返回值类型,name为所要调用的系统函数的名字。在unistd.h中共定义了4个这样的宏(n从0到3),也就是说,0.11核中系统调用最多可带3个参数。那么下面就说这个宏干了什么,也就是说上面的那个“调用过程”是怎么样的呢?在这个宏中嵌入了汇编代码,做的工作就是int 0x80,其中将字符串“_NR_”和name连接,组成一个宏并将这个宏的值,也就是被调用的系统函数在

4、sys_call_table中偏移量送到eax寄存器中;同时指明系统函数将来的返回值放到eax中。1.3系统调用处理过程下面我再说一下系统调用的核心软中断int 0x80具体干了什么。这条指令会引起CPU的软件中断,cpu会根据中断号找到中断处理程序。这个中断处理程序是在System_call.s中。在中断处理程序的工作过程大致是这样的:1.3.1将寄存器ds,es,fs以及存有参数的edx,ecx,ebx入栈,再ds,es,指向内核段,fs指向用户段。1.3.2根据eax中的偏移值,在函数表sys_call_table中找到对应的系统函数指针(函数的入口地址)。并利用call指令调用系统函数

5、,返回后,程序把返回值加入堆栈。1.3.3检查执行本次系统调用的进程的状态,如果发现由于某种原因原进程没处在就绪状态或者时间片到了,就会执行进程调度函数schedule()。1.3.4通过执行这次调用的程序的代码选择符判断它是不是普通用户程序,如果是就调用信号处理函数。若不是就直接弹出栈内容,并返回2添加一个系统调用的实验2.1实验内容在linux0.11版本中添加两个系统调用,并编写一个简单的应用程序测试它们。第一个系统调用是iam(),其原型为:int iam(const char * name);完成的功能是将字符串参数name的内容拷贝到内核中保存下来。要求name的长度不能超过23个

6、字符。返回值是拷贝的字符数。如果name的字符个数超过了23,则返回“-1”,并置errno为EINVAL。第二个系统调用是whoami(),其原型为:int whoami(char* name, unsigned int size);它将内核中由iam()保存的名字拷贝到name指向的用户地址空间中,同时确保不会对name越界访存(name的大小由size说明)。返回值是拷贝的字符数。如果size小于需要的空间,则返回“-1”,并置errno为EINVAL。2.2代码添加修改步骤2.2.1在unistd.h中添加系统调用接口#define _NR_whoami 72#define _NR_i

7、am 73int whoami(void);int aim(void);2.2.2在exit.c文件中添加系统调用处理函数的实现系统调用的函数可以在其他.c文件中添加或在新建文件中添加,只要编辑进image都是可以的,这里为了调试方便就在exit.c文件中添加了。#define MAX 23char N_MAX26;int sys_whoami(char* name, unsigned int size) if(strlen(N_MAX)size) return -EINVAL; int i; for(i=0;N_MAXi!=0;i+) put_fs_byte(N_MAXi,&namei);

8、return strlen(N_MAX);int sys_iam(char *name) char c; char str100; memset(str,0,sizeof(str); int i; for(i = 0; i MAX) return -EINVAL; memset(N_MAX,0,sizeof(N_MAX); for(i=0;stri!=0;i+) N_MAXi=stri; return i;2.2.3在system_call.s汇编代码中修改系统调用的个数#If 0nr_system_calls = 72#else nr_system_calls = 74#endif 2.2.

9、4测试代码的编写 test.c的代码如下:#define _LIBRARY_#include _syscall1(int,iam,char*,name)_syscall2(int,whoami,char*,name, unsigned int,size)int main()int a = 0;char bb26 = champion;char cc26 = ;a = iam(bb);printf(a=%d, a);a = whoami(cc,8);printf(iam=%sn,cc);return(1);3系统调用相关代码的分析3.1初始化软件中断门。3.1.1函数调用层次初始化软件中断门,就

10、是把0x80软件中断的处理函数 system_call挂载到中断向量表idt中,以确保发生软件中断时会运行system_call函数,这个函数在system_call.s实现。初始化的流程如下:main() sched_init () set_system_gate (0x80, &system_call) _set_gate3.1.2初始化宏_set_gate的原型/*传入的四个参数说明如下:gate_addr =&idt0x80 软件中断门的地址。type= 15 type为门类型dpl = 3 dpl为请求特权级addr = &system_call = 0x7119 通过查找syste

11、m.map可以查到中断处理程序的地址*/#define _set_gate(gate_addr,type,dpl,addr) _asm_ (movw %dx,%axnt movw %0,%dxnt movl %eax,%1nt movl %edx,%2nt : : i (short) (0x8000+(dpl13)+(type8), o (*(char *) (gate_addr), o (*(4+(char *) (gate_addr), d (char *) (addr),a (0x00080000)3.1.3分析初始化宏_set_gate的实现_asm_格式为嵌入式汇编的格式,分析可知代

12、码有5个传入的参数%0,%1,%2,%3,%4如下:%0,立即数i (short) (0x8000+(dpl13)+(type8)这样,%edx中高16位为addr的高16位,而低16位的P位为1(因为是0x8000),DPL位段为DPL(因为dpl3),而D位加上类型位段则为type(因为type= 0) return (type) _res; errno = -_res; return -1; 传入参数说明:其中type表示系统调用的返回值类型,name表示该系统调用的名称,atype、a分别表示第1个参数的类型和名称;可以有n个系统调用的传入参数,它们的数目和_syscall后面的数字一

13、样大。调用接口宏含义说明:它先将宏_NR_#name存入EAX,将参数fd存入EBX,然后进行0x80中断调用。调用返回后,从EAX取出返回值,存入_res,再通过对_res的判断决定传给API的调用者什么样的返回值。_NR_#name就是系统调用的编号,在include/unistd.h中定义;在上面的例子中,我们添加了两个自己的系统调用接口,如下:#define _NR_whoami 72#define _NR_iam 733.3对_system_call函数的分析处理流程图处理流程分析_system_call:cmpl $nr_system_calls-1,%eax # 调用号如果超出范

14、围的话就在eax 中置-1 并退出。ja bad_sys_callpush %ds # 保存原段寄存器值。push %espush %fspushl %edx # ebx,ecx,edx 中放着系统调用相应的C 语言函数的调用参数。pushl %ecx # push %ebx,%ecx,%edx as parameterspushl %ebx # to the system callmovl $0x10, %edx # set up ds,es to kernel spacemov %dx,%ds # ds,es 指向内核数据段(全局描述符表中数据段描述符)。mov %dx,%esmovl $

15、0x17,%edx # fs points to local data spacemov %dx,%fs # fs 指向局部数据段(局部描述符表中数据段描述符)。# 下面这句操作数的含义是:调用地址 = _sys_call_table + %eax * 4。参见列表后的说明。# 对应的C 程序中的sys_call_table 在include/linux/sys.h 中,其中定义了一个包括72 个# 系统调用C 处理函数的地址数组表。call _sys_call_table(,%eax,4)pushl %eax # 把系统调用号入栈。movl _current,%eax # 取当前任务(进程)

16、数据结构地址。# 下面查看当前任务的运行状态。如果不在就绪状态(state 不等于0) 就去执行调度程序。# 如果该任务在就绪状态但counter值等于0,则也去执行调度程序。cmpl $0,state(%eax) # statejne reschedulecmpl $0,counter(%eax) # counterje reschedule3.4用户态和内核态之间的传递数据在内核中主要提供了四个函数实现内核态和用户态的数据传递: copy_to_user(),copy_from_user(),get_fs_byte(),put_fs_byte();上面测试用例中使用的是对字节的操作get_

17、fs_byte(),put_fs_byte()。4通过bochs环境如何验证系统调用1.1Bochs+Linux0.11调试环境建立。可以分为两个部分的工作:搭建调试环境和Bochs命令的使用;这两部分网上资料较多,就不在此描述了。1.2测试程序的修改和添加方法。1.2.1使用mount命令访问文件系统hdc-0.11.img要想把测试程序test.c运行起来,一定要放入文件系统才行,也就是一定要把test.c程序放进hdc-0.11.img中去才行,可以用如下的方法打开文件系统:losetup /dev/loop1 hdc-0.11.img losetup -d /dev/loop1 los

18、etup -o 512 /dev/loop1 hdc-0.11.img mkdir /mnt/tempdirmount -t minix /dev/loop1 /mnt/tempdir 说明:用losetup的-d选项把hdc-0.11.img文件与loop1的关联解除,用losetup的-o选项,该选项指明关联的起始字节偏移位置。由上面分区信息可知,这里第1个分区的起始偏移位置是1 * 512 字节。在把第1个分区与loop1重新关联后,我们就可以使用mount命令来访问其中的文件了。在对分区中文件系统访问结束后,最后请卸载和解除关联。 umount /dev/loop1 losetup -

19、d /dev/loop11.2.2编译test.c测试程序把测试程序放到/mnt/tempdir/user/root目录下,这样就可以任意修改test.c文件的内容,并可以把修改的内容保存到hdc-0.11.img文件系统中去了。1.3通过bochs调试观察,是如何把0x80的中断函数system_call的地址挂载上去的。 1.3.1通过添加do_nothing()函数,然后在此函数设置断点,可以查看0x80中断处理函数是如何放到中断向量表中去的。加入调试辅助代码如下:因为set_system_gate是一个宏,没有办法添加断点,所以就添加了一个函数do_nothing(),在此处设置断点,

20、以方便观察后面宏的运行情况;并且加入了几个nop命令,以方便观察运行情况。void sched_init(void)do_nothing(); set_system_gate(0x80,&system_call);1.3.2修改_set_gate宏如下,加入了nop命令,以便调试观察。#define _set_gate(gate_addr,type,dpl,addr) _asm_ (nop nt nop nt nop nt nop nt movw %dx,%axnt movw %0,%dxnt nop nt nop nt movl %eax,%1nt movl %edx,%2nt nop nt

21、 nop nt nop nt nop : : i (short) (0x8000+(dpl13)+(type8), o (*(char *) (gate_addr), o (*(4+(char *) (gate_addr), d (char *) (addr),a (0x00080000)1.3.3可以看到运行的效果如下:在system.map中,看到do_nothing的线性地址是0x6c1d,可以在此处设置断点。Figure 1 设置断点Figure 2 程序运行到_set_gate宏Figure 3 查看此时寄存器中的数值1.4调试测试程序 test.c和sys_whoami和sys_i

22、am函数1.4.1调试系统调用处理函数sys_whoami和sys_iam.在system.map文件中,找到编译kernel后,函数sys_whoami和sys_iam所在的线性地址。如下所示:00008dba T sys_iam00008e57 T sys_whoami在bochs启动kernel后,在0x8dba和0x8e57处设置断点,然后运行test程序,就会进入系统调用处理函数,运行到设置的断点处,后面可以单步运行,以调试sys_whoami和sys_iam,达到测试系统调用的目的。如下图所示:1.4.2在linux0.11的系统中编译运行test程序在运行起来的linux0.11的系统中,通过make test命令编译test.c文件,使生产test应用。运行test程序后,iam()函数就会通过get_fs_byte()函数把“champion”字符串存入到内存中;whoami()函数就会把前面存入到内存中的字符串取出,并且通过put_fs_byte()内核函数返回到用户层;并且打印出来。如下所示:

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1