1添加一个系统调用设计说明书学院名称:计算机与信息工程学院班级名称:网工111班学生姓名:学号:题目:添加一个系统调用指导教师姓名:起止日期:2013-6-3——2013-6-302第一部分:正文部分一、选题背景本设计是专业基础课《计算机操作系统》的课程设计。由于操作系统课的学时有限,没有安排实验。为了理论联系实际,加强分析问题、解决问题能力的培养,,加深理解和更好地掌握操作系统的基本概念、原理、技术和方法。特安排操作系统课程设计。它是操作系统课程的实践环节。由于具体的操作系统相当复杂,在短短的一周之内,不可能对所有管理系统进行详细地分析。因此,选择了操作系统中的对Linux系统添加一个新的函数,作为本设计的任务,使学生在使用系统调用的同时,进一步了解系统内部是如何实现系统调用的全过程,使学生在更深层次上对操作系统有所了解。二、设计思路2.1课程设计题目添加一个系统调用2.2课程设计任务⑴根据redhat9平台下系统调用的调用方式,在系统中添加一个功能为打印一句话“hello,systemcall”的系统调用。⑵按照要求撰写课程设计报告。2.3课程设计思想操作系统的主要功能是为应用程序的运行创建良好的环境,为了达到这个目的,内核提供一系列具备预定功能的多内核函数,通过一组称为系统调用(systemcall)的接口呈现给用户。系统调用把应用程序的请求传给内核,调用相应的的内核函数完成所需的处理,将处理结果返回给应用程序,如果没有系统调用和内核函数,用户将不能编写大型应用程序。Linux系统调用,包含了大部分常用系统调用和由系统调用派生出的的函数。2.4软硬件运行环境计算机一台、WindowsXP系统、VMwareWorkstaion软件、redhat9软件2.5开发工具Linux、gcc编译器、vi编辑器3三、过程论述3.1流程图图3.1添加系统调用流程图3.2实验方法⑴每个系统调用都是通过一个单一的入口点多路传入内核。eax寄存器用来标识应当调用的某个系统调用,这在C库中做了指定(来自用户空间应用程序的每个调用)。⑵当加载了系统的C库调用索引和参数时,就会调用一个软件中断(0x80中断),它将执行system_call函数(通过中断处理程序),这个函数会按照eax内容中的标识处理所有的系统调用。⑶在经过几个简单测试之后,使用system_call_table和eax中包含的索引来执行真正的系统调用了。从系统调用中返回后,最终执行syscall_exit,并调用resume_userspace返回用户空间。然后继续在C库中执行,它将返回到用户应用程序中。3.3实验原理4图3.2使用中断方法的系统调用的简化流程图图3.3系统调用表和各种链接53.4程序结构框图图3.4程序结构框图3.5操作步骤第一步:编译前准备⑴下载一份内核源代码,例如linux-2.4.tar.gz⑵检查redhat中是否已有模块工具软件module-init-tools:#rpm–qmodutils安装:#rpm–ivhmodutils-2.4.21-23.src.rpm⑶将linux-2.4.tar.gz拷贝到目录/usr/src/下,解压源码:#tar–zxvflinux-2.4.tar.gz生成源码文件子目录/usr/src/linux-2.4,进入此目录:#cdlinux-2.4.第二步:编译新内核⑴配置内核,有三种方式配置内核:#makeconfig命令行界面#makemenuconfig字符菜单界面#makexconfig图形界面虽然选择图形界面比较方便,但配置过程很繁琐,可将现有的配置文件拷贝过来使用(/usr/src/linux-2.4/.config)。⑵编译生成新内核:#cd/usr/src/linux-2.46#makedep创建代码依赖文件(.depend),每次重新配置后都必须做这一步。#makebzImage开始编译系统内核(不包括带M选项的模块),生成的压缩文件bzImage在./arch/i386/boot/下。同时生成未压缩的内核执行文件(vmlinux)和内核符号表(System.map)。#makemodules开始编译外挂模块。以后重新编译内核时,可省去这一步。#makemodules_install将外挂模块放在系统模块安装目录(/lib/modules/2.4/)下,以便核心在需要时加载它们。同时在此目录下产生模块依赖文件(modules.dep)。#makeinstall将bzImage和System.map拷贝到/boot/下,并建立相应的符号链接(vmlinuz和System.map);生成/dev/initrd映象文件(initrd-2.4.img);在/etc/下的启动配置文件lilo.conf或grub.conf中添加相应项。第三步:运行新内核#reboot,选择启动新内核:需要注意的是,如果编译的内核版本号(在Makefile中定义)与正在运行的内核一样,就会覆盖现有内核的文件。为了防止新内核影响原内核,让新内核有一个不同的版本号。最好将编译内核前的虚拟机备份,以便在发生新内核导致系统无法正常运行时使用备份的系统。第四步:启动新内核后启动新内核后需要重新运行VMwaretools的配置程序/usr/bin/vmware-config-tools.pl,以使网络界面正常和共享windows主机的文件夹。四、结果分析4.1添加新函数⑴先用uname–a命令获取当前linux内核版本。⑵在/usr/src/linux-2.4/kernel/sys.c中,添加一个系统调用函数内核,命令如图4.1所示。7图4.1初始界面4.2更新头文件在/usr/src/linux-2.4/include/asm-i386更新头文件,如图4.3所示。8图4.3命令界面图4.4更新操作4.3针对这个新函数更新系统调用表在/usr/src/linux-2.4/arch/i386/kernel/entry.S中,添加一个系统调用,如图4.5所示。9图4.5命令界面图4.6更新系统调用表4.4重新编译内核⑴将/usr/src/linux-2.4/Makefile中的版本号里的customer去掉,防止内核版本冲突。如图4.8所示。10图4.7命令界面⑵编译内核makemrproper;/*文件归位。清除上次编译内核的文件*/makexconfig;必选的配置选项如下:SCSIdevicesupport---SCSIlow-leveldrivers---*BusLogicSCSI11support。如图4.9所示。图4.9SCSIsupport界面FusionMPTdevicesupport---MFusionMPT(base+ScsiHost)drivers和MFusionMPTmiscdevice(ioctl)driver。如图4.10所示。图4.10FusionMPTdevicesupport界面Networkdevicessupport---Ethernet(10or100Mbit)---*AMDPCnet32PCIsupport。如图4.11所示。12图4.11Networkdevicesupport界面blockdevices---*RAMdisksupport和*Initialdisk(initrd)support。如图4.12所示。图4.12Blockdevice界面Filesystems---Mext3journallingsystemsupport。如图4.13所示。13图4.13Filesystems界面⑶执行以下语句makedep;//作用:配置内核代码前需要进行配置;根据用户的配置设置源代码的相关性makebzImage;//内核编译,生成一个新内核映像文件bzImage,即编译好的可以被cpu直接执行的二进制机器码。makemodules;makemodules_install;//配置的内核有模块支持makeinstall;//将内核安装在系统中这之后新内核已经'安装'在了系统中,我们只需重启一下系统。五、结论⑴在重新启动并成功进入系统之后,我们需要对之前增加的系统调用进行测试,测试代码如图5.1所示。14图5.1测试代码部分⑵击保存并退出gedit界面⑶对C语言程序进行编译,最终得到文件名为test的可执行文件,在终端中输入。如图5.2所示。图5.2命令代码通过观察实验结果可得知,新添加的系统函数调用成功。15第二部分:参考文献[1]汤小丹,等.计算机操作系统.第三版.西安:西安电子科技大学出版社,2007[2]张尧学,史美林.计算机操作系统教程.北京:清华大学出版社,2000[3]尤晋元.UNIX操作系统教程.西安:西安电子科技大学出版社,1995学生签名:填表日期:年月日第三部分:指导教师评语第四部分:成绩评定指导教师签名:填表日期:年月日附录解释调用系统调用的过程:测试程序test.c中的_syscall1是定义在include/asm-i386/unistd.h中的宏:16#define_syscall1(type,name,type1,arg1)\typename(type1arg1)\{\long__res;\__asm__volatile(int$0x80\:=a(__res)\:0(__NR_##name),b((long)(arg1)));\__syscall_return(type,__res);\}其中__syscall_return也定义在该文件中#define__syscall_return(type,res)\do{\if((unsignedlong)(res)=(unsignedlong)(-125)){\errno=-(res);\res=-1;\}\return(type)(res);\}while(0)所以test.c中_syscall1(int,addtotal,int,num)展开后即:intaddtotal(intnum){long__res;__asm__volatile(“int$0x80”:”=a”(__res):”0”(__NR_addtotal),”b”((long)(num)));do{if((unsignedlong)(__res)=(unsignedlong)(-125)){errno=-(__res);__res=-1;}return(int)(__res);}while(0)}通过软中断int$0x80,其中系统调用号为eax中的__NR_##name,这里也17就是__NR_addtotal,在上面的步骤3中有#define__NR_addtotal259,即259号系统调用。寄存器ebx中存第一个参数num。IDT中第0x80个门(其类型为15,即陷阱门)为系统启动(init/main.c中start_kernel调用i386/kernel/traps.c中trap_init)时设置的,trap_init中set_system_gate(0x80,&system_call);故int$0x80指令通过该系统门后转到内核的system_call处执行。system_call定义在arch/i386/kernel/entry.S中:ENTRY(system_call)//转到此处执行pushl%eax#saveorig_eaxSAVE_ALL//把寄存器压入堆栈GET_CURRENT(%ebx)testb$0x02,tsk_ptrace(%ebx)#PT_TRACESYSjnetracesyscmpl$(NR_syscalls),%eaxjaebadsyscall*SYMBOL_NAME(sys_call_table)(,%eax,4)//此时eax=系统调用号=__NR_addtotal=259movl%eax,EAX(%esp)#savethereturnvalueENTRY(r