Exam02_实验2 添加系统调用
- 格式:doc
- 大小:63.50 KB
- 文档页数:4
实验一添加一个新的系统调用一、实验目的理解操作系统内核与应用程序的接口关系;加深对内核空间和用户空间的理解;学会增加新的系统调用。
二、实验内容与要求首先增加一个系统调用函数,然后连接新的系统调用,重建新的Linux内核,用新的内核启动系统,使用新的系统调用(2.4内核和2.6内核任选一个)三、实验指导(2.6版本)⑴获得源代码(本次实验的内核版本是2.6.22.5,必须是root用户)1.从教育在线上下载内核源代码到本地磁盘;保存在/usr/src目录下2.进入终端,输入命令cd /usr/src 进入/usr/src目录(可以输入ls命令会发现目录下有一个名为LINUX_2_6_22_5.TAR.BZ2的压缩文件)3.当前目录下(/usr/src)输入命令tar –xjvf LINUX_2_6_22_5.TAR.BZ2 解压缩源代码,命令执行完毕后,会出现/usr/src/linux-2.6.22.5文件夹4.修改文件夹下的3个文件第一,编辑/usr/src/linux-版本号/kernel/sys.c文件,添加函数:asmlinkage long sys_mycall(long number){printk(“call number is %d\n”,number);return number;}第二,修改/usr/src/linux-版本/include/asm-i386/unistd.h添加一行#define __NR_mycall 324 到当前的最大系统调用号之后,比如原来最大的是323,在323的这一行之后加上一行#define __NR_mycall 324修改#define NR_systemcalls 的值,改成原来的值+1,比如原来是324 改成325第三,编辑/usr/src/linux-版本/arch/i386/kernel/syscall_table.S,在文件最后加上一行:.long sys_mycall5.重新编译内核在终端输入命令,进入源代码文件夹,cd /usr/src/linux-2.6.22.5 依次执行如下命令:make mrpropermake cleanmake xconfig (自己配置内核,出现图形对话框后,直接点保存,关闭)make(耗时最长,大约20分钟)make modules_install (安装模块)以上命令执行完毕后,会在当前目录下生成一个名为System.map的文件,会在/usr/src/linux-版本号/arch/i386/boot/下生成一个bzImage文件。
一、构建基本的实验环境1.1基本实验环境与前提条件Windows7 、Word 2010、Vmware WorkStation 8.5、AdobeReaderReadHatLinux 9.0,gcc,viLinux内核[V2.4.18]1.2虚拟机的安装及使用1.3将Linux 内核源代码及配置文件传送给虚拟机上的Red Hat Linux V9.0 系统配置网络时遇到这个问题,Determining IP information for eth0... failed; no link present. Check cable?通过查找资料发现是系统的Bug,解决方法如下:到/etc/sysconfig/network-scripts/ifcfg-eth0在文件最后一行中加入check_link_down () {return 1;}另外如果存在/etc/sysconfig/networking/profiles/default/ifcfg-eth0 文件,则同样在其中加入这一段东西即可,然后重启系统。
设置网络为DHCP,重新启动就可以,啦,直接上图最后将内核代码下载到/root目录下二、Linux 内核编译、配置与调试2.1 内核配置与编译2.1.1、解压内核源代码文件tar -zxf linux-2.4.18.tar.gz2.1.2、解压后如下2.1.3、拷贝linux,命名为linux-2.4.18cp -r linux linux-2.4.182.1.4、移动config-2.4.18forMP.txt到linux-2.4.18根目录,替换掉.config2.1.5、进入linux-2.4.18目录,配置和编译内核模块make oldconfigmake depmake cleanmake bzImagemake modules2.2 内核安装与测试2.2.1安装内核映像文件cp arch/i386/boot/bzImage /boot/vmlinux-2.4.182.2.2拷贝和安装Linux系统映射文件System.map,并创建其与系统映射文件System.map之间的符号链接2.2.3执行命令make modules_install 以安装可动态加载的内核模块2.2.4添加启动项的配置利用vi编辑器,vi grub.conf查看/ 所在的位置,为/dev/sda32.2.5reboot重新启动系统,从自己创建的内核启动系统启动后查看内核分别用uname –r,和dmesg查看三、Linux 系统调用添加与实现3.1 在内核增加系统调用3.1.1结构体struct srz_rusage可声明如下:.struct srz_rusage {struct timeval ru_utime; /* user time used */struct timeval ru_stime; /* system time used */long ru_majflt; /* major page faults */long ru_minflt; /* minor page faults */long ru_nswap; /* swaps */};3.1.2添加到linux-2.4.18/include/linux下的resource.h中3.1.3添加的系统调用名称为:int get_process_usage(pid_t, struct srz_rusage*);参考的getrusage和sys_getrusage的代码在linux-2.4.18/linux/kernel/sys.c下面3.1.4分析getrusage()和sys_getrusage()的源代码1)数据结构rusage 在头文件resource.h中定义。
修改Linux内核增加系统调用一,修改内核增加系统调用只修改/usr/src/linux-2.4.29/include/asm-i386/unistd.h和arch/i386/kernel/entry.S,系统调用函数一般在kernel/sys.c中,这里把增加的系统调用代码也加入这个文件中。
1.修改kernel/sys.c文件,加入自己的系统调用代码,同参考文献(见文后地址)中,asmlinkage int sys_addtotal(int numdata){int i=0,enddata=0;while(i<=numdata)enddata+=i++;return enddata;}计算从0到numdata的累加值。
asmlinkage表示通过堆栈递参数。
2.然后把sys_addtotal(int )的入口地址添加到sys_call_table表中。
该表依次存储所有系统调用的入口地址。
修改前为:.long SYMBOL_NAME(sys_ni_syscall) /* sys_set_tid_address 这是第258个系统调用* /.rept NR_syscalls-(.-sys_call_table)/4.long SYMBOL_NAME(sys_ni_syscall)修改后:.long SYMBOL_NAME(sys_ni_syscall) /* sys_set_tid_address * /.long SYMBOL_NAME(sys_addtotal) /*这是增加的第259个系统调用*/.rept NR_syscalls-(.-sys_call_table)/4-1 /*这里重复次数减少1*/ .long SYMBOL_NAME(sys_ni_syscall)3. 把增加的sys_call_table 表项所对应的向量,在include/asm-i386/unistd.h 中进行必要申明,以供用户进程和其他系统进程查询或调用:#define __NR_exit_group 252#define __NR_addtotal 259 /*这是增加的第259个系统调用*/然后编译内核make bzImage,并用生成的新内核启动系统。
第1篇一、实验目的1. 了解系统调用的基本概念和作用。
2. 掌握在Linux内核中增加系统调用的方法。
3. 熟悉系统调用在用户空间和内核空间之间的交互过程。
4. 提高编程能力和系统理解能力。
二、实验环境1. 操作系统:Linux2. 编译器:gcc3. 开发工具:内核源代码、makefile三、实验原理系统调用是操作系统提供的一种服务,允许用户空间程序请求内核空间的服务。
在Linux内核中,系统调用通过系统调用表来实现。
增加系统调用需要修改内核源代码,并重新编译内核。
四、实验步骤1. 创建系统调用函数首先,我们需要创建一个系统调用函数,该函数将实现一个简单的功能,例如打印一条消息。
以下是一个简单的系统调用函数示例:```cinclude <linux/module.h>include <linux/kernel.h>include <linux/init.h>static int __init hello_init(void) {printk(KERN_INFO "Hello, World!\n");return 0;}static void __exit hello_exit(void) {printk(KERN_INFO "Goodbye, World!\n");}module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Your Name");MODULE_DESCRIPTION("A simple system call module");MODULE_VERSION("0.1");```2. 修改系统调用表接下来,我们需要修改内核源代码中的系统调用表,以注册我们创建的系统调用。
两种⽅法添加系统调⽤通过修改内核源代码添加系统调⽤通过以上分析linux系统调⽤的过程,将⾃⼰的系统调⽤加到内核中就是⼀件容易的事情。
下⾯介绍⼀个实际的系统调⽤,并把它加到内核中去。
要增加的系统调⽤是:inttestsyscall(),其功能是在控制终端屏幕上显⽰hello world,执⾏成功后返回0。
1编写inttestsyscall()系统调⽤编写⼀个系统调⽤意味着要给内核增加1个函数,将新函数放⼊⽂件kernel/sys.c中。
新函数代码如下:asmlingkage sys_testsyscall(){ console_print("hello world ");return 0;}2连接新的系统调⽤编写了新的系统调⽤过程后,下⼀项任务是使内核的其余部分知道这⼀程序的存在,然后重建包含新的系统调⽤的内核。
为了把新的函数连接到已有的内核中去,需要编辑2个⽂件:1).inculde/asm/unistd.h在这个⽂件中加⼊#define_NR_testsyscall 1912).are/i386/kernel/entry.s这个⽂件⽤来对指针数组初始化,在这个⽂件中增加⼀⾏:.long SYMBOL_NAME(_sys_tsetsycall)将.rept NR_syscalls-190改为NR_SYSCALLS-191,然后重新奖励和运⾏新内核。
3).使⽤新的系统调⽤在保证的C语⾔库中没有新的系统调⽤的程序段,必须⾃⼰建⽴其代码如下#inculde_syscall0(int,testsyscall)main(){tsetsyscall();}在这⾥使⽤了_syscall0()宏指令,宏指令本⾝在程序中将扩展成名为syscall()的函数,它在main()函数内部加以调⽤。
在testsyscall()函数中,预处理程序产⽣所有必要的机器指令代码,包括⽤系统调⽤参数值加载相应的cpu寄存器,然后执⾏int 0x80中断指令。
添加系统调用(模块添加法fedora10:2.6.27.5内核)
一、为什么要使用内核模块的方式添加系统调用?
⏹编译内核的方式费时间,一般的PC机都要两三个小时。
⏹不方便调试,一旦出现问题前面的工作都前功尽弃。
二、用内核模块的方式实现系统调用有个前提,就是系统必须导出sys_call_table 内核符号,但是在2.6内核和2.4.18以上版本中,sys_call_table不再导出。
也就是说模块中不能再通过简单的extern void *sys_call_table[];来获得系统调用
表地址。
但是,即使内核不导出sys_call_table,也可以在内存中找到它的地址,下面是它的实现方法:
第1步:编写syscall_my.c程序:
pptprogram/syscall/module_syscall/syscall_my.c
第2步:编写Makefie文件
见pptprogram/syscall/module_syscall/Makefile
第3步.执行make命令编译模块
第4步:执行insmod命令插入模块,此时会把自己编写的系统调用插入到系统调用表中。
操作系统《实验2》实验报告实验项目2:增加新的系统调用学号1209050123 姓名宋玉美课程号实验地点指导教师万少华时间2013.11评语:成绩教师签字万少华线性表链式存储(双向链表)插入、删除运算1、预备知识:Linux内核结构、Linux内核源码、Linux系统调用2、实验目的:增加新的系统调用3、实验内容及要求:(1)增加新的系统调用新增的系统调用名为get_proc_run_time,其功能是根据指定的进程pid,从该进程的进程描述符task_struct结构中提取出它的系统时间stime与用户时间utime (2)编译内核用编译内核的方法,将其增加到内核源码并编译内核(3)程序测试在用户空间编写测试程序测试该系统调用。
程序中调用此系统调用能准确的度量一个程序的时间效率,考虑是否还有别的方法比这更准确的学生信息,参数x, i,j从键盘输入(4)给出程序运行截图。
4、该文档的文件名不要修改,存入<学号><姓名> 命名的文件夹中5、该表中的数据只需填空,已有内容不要修改1.添加系统调用函数,修改文件/usr/src/linux—3.5/kernel/sys.c2. 添加系统调用号,修改文件/arch/x86/systemcalls/syscall_32.tbl3. 添加声明到头文件,修改文件,/include/linux/syscalls.h4. 重新编译内核(前几步只顾着运行忘记截图了,不好意思哈老师~)1)安装ncurses2)make menuconfig3)make dep 确定依赖性4)make clean 清理编译中间文件5)make bzImage 生成新内核6)make modules 生成modules 7)安装modules9)安装内核make install 10)配置grub引导程序13)重启。
重启系统,从grub菜单中选中新内核引导linux。
实验二增加新的系统调用
(2)完成系统调用函数
编写待添加的系统调用函数,函数名以sys_开头
如:mycall(int num),在/usr/src/linux-3.0.74/kernel/sys.c文件中添加代码:asmlinkage int sys_mycall(int number)
{
printk(“This is my experiment!”);
return number; //仅返回一个整型值
}
(4)添加系统调用号
文件unistd_32.h包含系统调用清单,给每个系统调用
分配一个唯一的号码,格式为:#define _NR_name NNN
如在/usr/src/linux-3.0.74/include/arch/x86/include/asm/unistd_32.h的结尾分配号码序列中下一个可用的系统调用号(不能与其它号码相同):
#define _NR_mycall 347
(6)清除内核及之前编译的内容
make clean//清除原有不需要的模块和文件
make mrproper//删除原来编译产生的垃圾
(8)编译内核
make或make zImage//编译内核, make zImage编译压缩形式的大内核
make modules_install// 编译后的模块移到系统标准位置
(9)启动项配置
mkinitrd –o /boot/initrd.img-3.0.74//创建内核的initrd映象
(10)编译新内核前linux系统本身的内核版本是
(11)测试调用函数。
中国地质大学(武汉)《操作系统原理》课程实验报告数据科学与大数据技术专业班级195182学生姓名钟欢任课教师康晓军完成时间2020年3月31日实验一——实现一个linux的系统调用一、实验目的1.加深对系统调用的理解,掌握增加与调用系统调用的方法。
2.掌握内核编译方法。
二、实验思路1.增加新的系统调用:新增的系统调用名为Hello,其功能是打印输出“This is ZhongHuan ’ s system call ! wo zhong yu cheng gong le !”2.编译内核:用编译内核的方法,将其增加进内核源码并编译内核。
3.测试:在用户控件编写测试程序测试该系统调用。
三、实验步骤1.系统调用的添加在Linux中添加新的系统调用,需执行多个步骤才能添加成功:(1)第一步完成系统调用函数在内核源码目录kernel/sys.c文件中编写待添加的系统调用函数。
该函数的名称应该是新的系统调用名称前面加上sys_标志。
新加的系统调用为hello(void),在kernel/sys.c文件中添加源代码:asmlinkage long sys_hello(void){printk("This is ZhongHuan's system call! wo zhong yu cheng gong le!");return 1;}(2)第二步在系统函数表中表项添加新的系统调用后,需要让Linux内核的其余部分知晓该程序的存在。
在内核源码目录arch/x86/entry/syscalls下修改文件syscall_64.tbl。
该文件用来对sys_call_table[]数组实行原始化,数组包含指向内核中每个系统调用的指针。
在该文件中的最后一行添加自己的系统调用表项:335 64 hello sys_hello(),这样就在数组中添加了新的内核函数指针。
(3)第三步添加系统调用号在内核源码目录arch/x86/include/asm下修改头文件unistd_32.h。
<系统调用>实验报告题目: 系统调用1、实验目的向现有Linux内核加入一个新的系统调用从而在内核空间中实现对用户空间的读写。
例如,设计并实现一个新的内核函数mycall( ),此函数通过一个引用参数的调用返回当前系统时间,功能上基本与gettimeofday( )相同。
也可以实现具有其它功能的系统调用。
2、实验内容与步骤1. 添加新调用的源代码在/usr/src/linux-3.16.6-10/kernel/sys.c中添加相应的调用代码asmlinkage int sys_mycall(struct timeval *tv){struct timeval ktv;MOD_INC_USE_COUNT;do_gettimeofday(&ktv);if (copy_to_user(tv,&ktv,sizeof(ktv))){MOD_DEC_USE_COUNT;return -EFAULT;}MOD_DEC_USE_COUNT;return 0;}2. 连接系统调用a、修改/usr/src/linux-3.16.6-10/include/asm-i386/unistd.h,在系统调用列表后面相应位置添加一行#define _NR_mycall 222新增加的调用号位222b、修改/usr/src/linux-3.16.6-10/arch/i386/kernel/entry.S在sys_call_table[]清单最后添加一行.long SYMBOL_NAME(sys_mycall)3. 重建新的Linux内核cd /usr/src/linux-3.16.6-10/make mrpropermake oldconfigmade depmake cleanmake bzImagemake modulesmake modules_installmake install保存配置文件, 开始编译内核:4. 重建引导信息a、在/boot/grub/grub.conf中自己添加一条新的启动选项,并使该选项指向vimlinuz-3.16.6-10customb、重新安装grub5. 重新引导从新的内核进入6. 修改/usr/lib/bcc/include/unistd.h,在系统调用列表后面相应位置添加一行#define _NR_mycall 2227.编写程序测试test.c:#include <linux/unistd.h>#include <linux/time.h>#include <stdio.h>_syscall1(int,mycall,struct timeval *,thetime)main(){struct timeval gettime;struct timeval mycalltime;gettimeofday(&gettime,NULL);mycall(&mycalltime);printf("gettimeofday:%d%d\n",_sec,_usec);printf("mycall:%d%d\n",_sec,_usec);}编译运行得到如下结果:参考程序当中有若干需要注意的地方, 在编译的时候遇到错误和警告, 提示没有声明NULL需要加上#include<stdio.h>头文件, 由程序的输出结果相同可知, mycall和gettimeofday具有相同的功能.实验结果正确.。
实验2:文件部分系统调用1、实验目的(1)文件部分系统调用;(2)通过系统调用实现:文件的顺序读取,文件顺序写入,文件的追加,文件随机存取。
2、实现设备一台装有Windows操作系统和Linux机系统的微机或服务器。
3、实验方法与注意事项1)由于是以root用户工作的,权力很大,请不要在系统内做对系统或对其他用户不安全的事情。
2)要求每个同学登录后系统后,要在自己的家目录内容以自己(汉语拼音)名字,使用mkdir命令创建一个子目录。
以后所有工作都要在自己的目录内进行。
3)认真编写实验报告并上传服务器。
4、实验过程示例1:#include <errno.h>#include <sys/stat.h>#include <sys/fcntl.h>main( ){ int fd, j;char *myc="This is my 1st program!!\n";tmpc[100];char *myf="myfile",if((fd=open(myf,O_RDWR|O_CREAT|O_APPEND,0644))==-1)}exit(errno);perror(myf);{if(write(fd,myc,strlen(myc))!=strlen(myc)){ perror(myc);}close(fd); exit(errno);j=-strlen(myc);lseek(fd,(long)j,SEEK_CUR);read(fd,tmpc,strlen(myc); //可能会越界printf(“%s\n”,tmpc); //可能会越界close(fd);}(1)调通以上程序,实现文件的创建、追加与顺序写入。
(2)修改以上程序,使用lseek、write、read等系统调用实现文件的随机访问。
示例2:图书管理品管理系统#include <stdio.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <sys/stat.h>#include <sys/fcntl.h>#define LBNO 10#define LBNM 20#define LBCT 5#define LBUF 100int sql(int ,char *, char *);char *book_file="book_file";typedef struct bk {b_bno[LBNO];charb_bnm[LBNM];charcharb_bct[LBCT];filler;charbk;}bk *b_k,book;char b_buf[LBUF];main(){fd;intif((fd=open(book_file,O_RDWR|O_CREAT|O_APPEND,0644))==-1){perror(book_file);exit(errno);}close(fd);while(1){system("clear");printf("\n\n\n\t1. Book Query 2.Book Input 3.Book_Len 4.Book_Ret 0.quit\n");printf("\t\t\tPlease get one:");switch(getchar()){case '1':bk_query();break;case '2':bk_input();break;case '3':bk_io(1);break;case '4':bk_io(-1);break;case '0':case 'q':case 'Q':exit(0);default :printf("\n");}}}bk_query(){bk_rec[LBNO+LBNM+LBCT+2];char bk_no[LBNO+1],fd,i;intNo:");scanf("%s",b_buf);printf("Booksprintf(bk_no,"%10s",b_buf);bk_no[LBNO]='\0';if((fd=open(book_file,O_RDWR))==-1){perror(book_file);getchar();-1;return}i=sql(fd,bk_no,bk_rec);if(i==0)%s",bk_no);find:fprintf(stderr,"Notelse if (i<=0) fprintf(stderr, "File: %s Read Error!\n",book_file);else {bk_rec[LBNO+LBNM+LBCT+1]='\0';printf("%s\n",bk_rec);getchar();}close(fd);getchar();}bk_get(char *bk_rec){char bk_nm[LBNM],bk_no[LBNO];bk_ct;intprintf(" Book No:");scanf("%s",b_buf);b_buf[LBNO]='\0';sprintf(bk_no,"%10s",b_buf);printf(" Book Nane:");scanf("%s",b_buf);//error occurs, when string with white spaceb_buf[LBNM]='\0';sprintf(bk_nm,"%20s",b_buf);Count:");scanf("%d",&bk_ct);printf("Booksprintf(bk_rec,"%10s%20s%5d\n",bk_no,bk_nm,bk_ct); }sql(int fd, char *b_no,char *bk_rec){i;intwhile(1){i=read(fd,bk_rec,sizeof(book));-1;returnif(i==-1)if(i==0) return 0; //not exist-2;if(i!=sizeof(book))returni=strncmp(b_no,bk_rec,LBNO);1; //existif(i==0)return}}bk_input(){intfd,i,j;char buff[LBNO+1];bk_get(b_buf);if((fd=open(book_file,O_RDWR))==-1){perror(book_file);getchar();return-1;}i=sql(fd,b_buf,buff);close(fd);if(i==0){if((fd=open(book_file,O_WRONLY|O_APPEND))==-1){ perror(book_file);getchar();-1;return}i=write(fd,b_buf,sizeof(book));if(i!=sizeof(book)){perror(book_file);close(fd);getchar();0;return}}else if(i<0) printf("File: %s read error!\n");else printf("Book:%s Exist!\n",buff);getchar();}bk_io(int lb){intfd,i,j;char b_no[sizeof(b_k->b_bno)+1],b_ct[sizeof(b_k->b_bct)]; printf("Book_NO:");scanf("%s",b_buf);sprintf(b_no,"%10s",b_buf);if((fd=open(book_file,O_RDWR))==-1){perror(book_file);getchar();-1;return}i=sql(fd,b_no,b_buf);if(i==0){fprintf(stderr,"No this book!\n");close(fd); return -1;}if(i<0){fprintf(stderr,"File: %s Read error!\n",book_file);-2;close(fd);return}*)b_buf;b_k=(bkstrncpy(b_ct,b_k->b_bct,sizeof(b_k->b_bct));b_ct[sizeof(b_k->b_bct)]='\0';i=atoi(b_ct);while(1){printf("There are %d books left, How many:",i);scanf("%d",&j);break;if(j==0)i=i-j*lb;sprintf(b_k->b_bct,"%5d\n",i);j=-(LBNM+LBNO+LBCT+1);)j,SEEK_CUR);lseek(fd,(longif(write(fd,b_buf,sizeof(book))!=sizeof(book)){writeerror!\n",book_file);%sprintf("File:close(fd);getchar();-1;return}{elseclose(fd);getchar();0;return}}getchar();}练习:试编写使用文件的电话号码簿,要求可以增加、删除和查询。
1、下载内核apt-get install linux-source在/usr/src下2、解压内核cd /usr/srctar -jxvf linux-source-2.6.35.tar.bz2将内核解压到/usr/src3、修改文件a)/usr/src/linux-source-2.6.35/kernel/sys.c 可以用右键管理员打开或者gedit /usr/src/linux-source-2.6.35/kernel/sys.c 在文件最后添加asmlinkage int sys_mycall(int number){printk("这是我添加的第一个系统调用"); return number;}asmlinkage int sys_addtotal(int number){int i=0,enddate=0;printk("这是我添加的第二个系统调用"); while(i<=number)enddate+=i++;return enddate;}asmlinkage int sys_three(){printk("这是我添加的第三个系统调用");return 0;}b)/usr/src/linux-source-2.6.35/arch/x86/kernel/syscall_table_32.S 可以用右键管理员打开或者gedit /usr/src/linux-source-2.6.35/arch/x86/kernel/syscall_table_32.S 然后在一列 .long sys_XXXX的下一行添加.long sys_mycall.long sys_addtotal.long sys_three并记住他们分别是第几个.long sys_XXXXc)/usr/src/linux-source-2.6.35/arch/x86/include/asm/unistd_32.h可以用右键管理员打开或者gedit /usr/src/linux-source-2.6.35/arch/x86/include/asm/unistd_32.h 在一列#define __NR_XXXX NNN后添加几行#define __NR_mycall 338#define __NR_addtotal 339#define __NR_three 340注意后面那个数字是接着上面那几行下来的d)/usr/src/linux-source-2.6.35/ubuntu/omnibook/Makefile如果在后面编译模块时出现ld: /ubuntu/omnibook/sections.lds: No such file: No such file or directorymake[2]: *** [ubuntu/omnibook/omnibook.o] Error 1make[1]: *** [ubuntu/omnibook] Error 2make: *** [ubuntu] Error 2以上错误,则需要修改,不过我建议还是先改了再说可以用右键管理员打开或者gedit /usr/src/linux-source-2.6.35/ubuntu/omnibook/Makefile找到下面两行#EXTRA_LDFLAGS += $(src)/sections.ldsEXTRA_LDFLAGS += $(PWD)/ubuntu/omnibook/sections.lds调换一下‘#’的位置,如下EXTRA_LDFLAGS += $(src)/sections.lds#EXTRA_LDFLAGS += $(PWD)/ubuntu/omnibook/sections.lds4、编译内核进入解压目录cd /usr/src/linux-source-2.6.35a)make mrproper //清除内核中不稳定的目标文件,附属文件及内核配置文件b)make clean //清除以前生成的目标文件和其他文件c)make oldconfig// 采用默认的内核配置,如果这里出现选项,选择默认的选项,就是方括号内的第一个字母,不过我这里没出现选项d)make bzImage //编译内核,大概需要半小时e)make modules //编译模块,大概需要两小时,如果出现错误,看看是不是因为上面的第3步的d)没做f)make modules_install// 安装模块,比较快5、复制内核首先查看一下编译好的内核版本,以便命名打开/lib/modules里面应该多了一个纯数字不带"generic"的文件夹,那就是新内核版本号,我的是2.6.35.11然后复制内核cp /usr/src/linux-source-x.x.x/arch/i386/boot/bzImage /boot/vmlinuz-x.x.x-mykernel 6、创建initrd文件在创建之前先安装必要的程序apt-get install bootcd-mkinitramfsmkinitramfs -o /boot/initrd.img-x.x.x耐心等待创建完成7、更新配置GRUB引导列表可以用右键管理员打开/boot/grub/grub.cfg或者gedit /boot/grub/grub.cfg找到下面这种结构menuentry 'Ubuntu, with Linux 2.6.35-27-generic' --class ubuntu --class gnu-linux--class gnu --class os {recordfailinsmod part_msdosinsmod ext2set root='(hd1,msdos1)'search --no-floppy --fs-uuid --set 71a50d19-caef-4dff-9a7a-57cb1bbfe0c2linux /boot/vmlinuz-2.6.35-27-genericroot=UUID=71a50d19-caef-4dff-9a7a-57cb1bbfe0c2 ro quiet splashinitrd /boot/initrd.img-2.6.35-27-generic}menuentry 'Ubuntu, with Linux 2.6.35-27-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os {recordfailinsmod part_msdosinsmod ext2set root='(hd1,msdos1)'search --no-floppy --fs-uuid --set 71a50d19-caef-4dff-9a7a-57cb1bbfe0c2echo 'Loading Linux 2.6.35-27-generic ...'linux /boot/vmlinuz-2.6.35-27-genericroot=UUID=71a50d19-caef-4dff-9a7a-57cb1bbfe0c2 ro singleecho 'Loading initial ramdisk ...'initrd /boot/initrd.img-2.6.35-27-generic}复制一份在这些结构前粘贴,注意必须贴在### BEGIN /etc/grub.d/10_linux ###……### END /etc/grub.d/10_linux ###里面,并将粘贴出来的结构中的linux /boot/vmlinuz-2.6.35-27-genericinitrd /boot/initrd.img-2.6.35-27-generic改成你的内核文件地址和initrd文件地址linux /boot/vmlinuz-2.6.35.11-mykernelinitrd /boot/initrd.img-2.6.35.11最好把其他所有版本相关信息号改成2.6.35.11(新编译的版本号),以便在Grub菜单选择比如改成menuentry 'Ubuntu, with Linux 2.6.35.11' --class ubuntu --class gnu-linux --class gnu --class os {recordfailinsmod part_msdosinsmod ext2set root='(hd1,msdos1)'search --no-floppy --fs-uuid --set 71a50d19-caef-4dff-9a7a-57cb1bbfe0c2linux /boot/vmlinuz-2.6.35.11-mykernelroot=UUID=71a50d19-caef-4dff-9a7a-57cb1bbfe0c2 ro quiet splashinitrd /boot/initrd.img-2.6.35.11}menuentry 'Ubuntu, with Linux 2.6.35.11 (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os {recordfailinsmod part_msdosinsmod ext2set root='(hd1,msdos1)'search --no-floppy --fs-uuid --set 71a50d19-caef-4dff-9a7a-57cb1bbfe0c2echo 'Loading Linux 2.6.35.11 ...'linux /boot/vmlinuz-2.6.35.11-mykernelroot=UUID=71a50d19-caef-4dff-9a7a-57cb1bbfe0c2 ro singleecho 'Loading initial ramdisk ...'initrd /boot/initrd.img-2.6.35.11}注意检查一下/boot/目录下是否存在上面这两个文件,如果没有,证明上面的几部还没成功还有就是这些menuentry的顺序,有些系统启动引导时会直接进入第一个menuentry,如果第一个menuentry不是你想进的内核,则需要在开机时按Shift进入GRUB引导菜单选择内核。
实验二:系统调用实验实验学时:2学时一、实验目的(1)设计程序,实现结果的不可再现性;使用同步原语,实现结果的可再现性(2)设计程序,使用递归及非递归方法对系统的进程数目进行压力测试,对运行时间进行监控二、实验基本原理简单的系统调用(1)fork();当fork()函数返回值为0时,子进程成功创建,以下为子进程作用域(2)syscall(SYS_getpid);调用系统函数syscall(),返回当前进程号(3)getpid();调用系统函数getpid(),返回当前进程号(4)execl( );头文件:#include <unistd.h>原型:int execl(const char *path, const char *arg, ...);函数说明:execl()用来执行参数path字符串所代表的文件路径,接下来的参数代表执行该文件时传递的argv[0],argv[1].....是后一个参数必须用空指针NULL作结束返回值:成功则不返回值,失败返回-1,失败原因存于errno中三、参考程序1.程序的不可再现性:$cat tc_print.c#include<stdio.h>int main(int argc,char *argv[]){printf("Thead %s has start!\n ",argv[1]);sleep(3);printf("Thread %s ended\n",argv[1]);return(0);}$cat ecp1.c#include<stdio.h>#include<unistd.h>#include<sys/wait.h>#include<sys/types.h>void main(){int *status;char *f="./tc_print"; /*要执行的程序名*/char *argv1[3],*argv2[3],*argv3[3],*argv4[3]; /*执行程序所需的参数*/argv1[0]="./tc_print";argv1[1]="1";argv1[2]=0; /*参数结束的标志*/argv2[0]="./tc_print";argv2[1]="2";argv2[2]=0;argv3[0]="./tc_print";argv3[1]="3";argv3[2]=0;argv4[0]="./tc_print";argv4[1]="4";argv4[2]=0;if(fork()==0) /*创建进程*/{execvp(f,argv1); /*执行程序*/sleep(1);execvp(f,argv2);sleep(1);}else{if(fork()==0) /*创建进程*/{execvp(f,argv3);sleep(1);}else{ wait(status);execvp(f,argv3);sleep(1);}}printf("End program!\n");}2.实现程序结果的可再现性(1)用wait()函数实现程序可再现性#include<stdio.h>int n=0;int status; //改动地点void pp(){n++;if(n>=10)return;wait(&status); //改动地点wait系统调用会使父进程阻塞直到一个子进程结束if(fork()==0){printf("%d",n);for(n=0;n<1000000;n++);}else pp();}int main(){int i;for(i=0;i<3;i++){ pp();printf("\n");}return 0;}(2)用信号量semget()、semctl()、semop()实现进程同步#include<sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>#include<errno.h>#include<stdlib.h>#include<stdio.h>#include<fcntl.h>#include<unistd.h>#define SEMKEY (key_t)0x200typedef union _senum{int val;struct semid_ds *buf;ushort *array;}semun;static int semid;struct sembuf p1={0,-1,0};//第一个是索引量,第二个-1是p操作,1是v操作。
实验二增加系统调用实验报告一修改系统文件1.在系统调用表中添加相应表项# cd /usr/src/linux-2.4/arch/i386/kernel# vi entry.S添加.long SYMBOL_NAME(sys_pedagogictime)见图1:2. 添加系统调用号# cd /usr/src/linux-2.4/include/asm# vi unistd.h添加# define __NR_pedagogictime 259见图2:3. 在文件最后添加一个系统服务例程# cd /usr/src/linux-2.4/kernel# vi sys.c添加sys_pedagogictime见图三二.编译内核# cd /usr/src/linux-2.4# make mrproper# make xconfigBlock devices → Loopback device support 选YSCSI support → SCSI low-level drivers → BusLogic SCSI support 选YFile systems → Ext3 journallingfile system support 选YNTFS file system support 选YSave and exit# make dep# make clean# make bzImage# make modules# make modules_install从/etc/modules.conf中删除BusLogic的加载项#alias scsi_hostadapter BusLogic /* 否则make install时总报错:No module BusLogic found for kernel 2.4.22 */ # cd /usr/src/linux-2.4# make install三将新内核和System.map拷贝到/boot目录下# cp /usr/src/linux-2.4/arch/i386/boot/bzImage /boot/vmlinux-2.4.20# cp /usr/src/linux-2.4/ System.map /boot/System.map-2.4.20# cd /boot# rm –f System.map# ln –s System.map-2.4.20 System.map四修改Grub启动管理器# cd /boot/grub# vi menu.lst修改menu.lst文件,将Red Hat Linux (2.4.20-8custom)部分中的root=LABEL=/改为root=/dev/sda2修改前见图四修改后见图五五重启系统:# reboot重启后显示如图六:选择Red Hat Linux (2.4.20-8custom),回车六编辑用户空间的测试程序:.运行结果:。
系统调用是如何实现的及添加一个新的系统调用系统调用裸板开发 uart_init uart_gets uart_puts假如应用程序不去调用,就没有意义驱动程序是为应用程序服务的,是要应用程序调用的2.1 什么是系统调用用于空间程序调用内核空间程序的一种方式2.2 系统调用是如何实现的应用程序首先使用适当的值填充寄存器然后调用特殊的指令,导致跳转到内核的某个固定位置执行在该位置根据应用程序所填充的寄存器值来找到相应的内核函数并执行执行完毕后原路返回。
open("a.txt", ....)c库中的相关函数适当的值:如果是open 值就是5arch/arm/include/asm/unistd.h寄存器:对于ARM处理器 r7特殊指令:arm ----> swix86 ----> int固定的位置:ARM硬件做4件事entry-common.Svector_swi:sys_call_table[r7]calls.S有数组的定义2.3 添加一个新的系统调用1)在内核中增加一个新的函数cd kernelvi arch/arm/kernel/sys_arm.casmlinkage int sys_add(int x, int y){printk("enter %s\n", __func__);return x+y;}2)更新系统调用表vi arch/arm/kernel/calls.S390 CALL(sys_add)3)更新unistd.hvi arch/arm/include/asm/unistd.h407 #define __NR_add (__NR_SYSCALL_BASE+378)4) 编译内核make uImage5) 开发板使用新的内核cp arch/arm/boot/uImage /tftpboottftp 48000000 uImagebootm 480000006) 编写测试程序验证cp /opt/arm-cortex_a9-eabi-4.7-eglibc-2.18/arm-cortex_a9-linux-gnueabi/sysroot/lib/libgcc_s.so* ../rootfs/lib/ -aarm-cortex_a9-linux-gnueabi-gcc test.c -o testcp test ../../rootfs/。
操作系统实验报告编译后,运行调用。
但由于系统安装了还原保护,计算机不能重新命名1.OSVERSIONINFO:微软的Win32应用程序编程接口(API)包含了描述当前操作系统配置的函数。
2.typedef struct _OSVERSIONINFO{DWORD dwOSVersionInfoSize; //指定该数据结构的字节大小DWORD dwMajorVersion; //操作系统的主版本号DWORD dwMinorVersion; //操作系统的副版本号DWORD dwBuildNumber; //操作系统的创建号DWORD dwPlatformId; //操作系统的ID号TCHAR szCSDVersion[ 128 ];} OSVERSIONINFO;3.如果需要获取计算机名、版本号、重设计算机名,则使用以下三个函数GetComputerName();GetVersionEx();SetComputerName();1).GetComputerName():取得这台计算机的名称;[参数表]lpBuffer ------- String,随同计算机名载入的字串缓冲区nSize ---------- Long,缓冲区的长度。
这个变量随同返回计算机名的实际长度载入[返回值]Long,TRUE(非零)表示成功,否则返回零。
会设置GetLastError2).GetVersionEx():用于获得当前运行的操作系统的版本扩展信息。
[参数表]参数 lpVersionInformation返回值Long,非零表示成功,零表示失败3).SetComputerName():函数设置计算机名,系统下次启动时将使用该名称。
参数:lpszName 指向一个以NULL结束的字符串,该字符串指定新的计算机名。
该名称不得比MAX_COMPUTERNAME_LENGTH个字符长。
返回值:若该函数成功,返回值为TURE;否则,返回值为FALSE。
添加系统调用
实验目的
学习Linux内核的系统调用,理解、掌握Linux系统调用的实现框架、用户界面、参数传递、进入/返回过程。
实验内容
本实验分两步走。
第一步,在系统中添加一个不用传递参数的系统调用;执行这个系统调用,使用户的uid等于0。
显然,这不是一个有实际意义的系统调用。
我们的目的并不是实用不实用,而是通过最简单的例子,帮助熟悉对系统调用的添加过程,为下面我们添加更加复杂的系统调用打好基础。
第二步,用kernel module机制,实现系统调用gettimeofday的简化版,返回调用时刻的日期和时间。
实验指导
2.1一个简单的例子
在我们开始学习系统调用这一章之前,让我们先来看一个简单的例子。
就好像哪个经典的编程书上都会使用到的例子一样:
1: int main(){
2: printf(“Hello World!\n”);
3: }
我们也准备了一个例子给你:
1: #include <linux/unistd.h> /* all system calls need this header */
2: int main(){
3: int i = getuid();
4: printf(“Hello World! This is my uid: %d\n”, i);
5: }
这就是一个最简单的系统调用的例子。
与上面那个传统的例子相比,在这个例子中多了
2行,他们的作用分别是:
第一行:包括unistd.h这个头文件。
所有用到系统调用的程序都需要包括它,因为系统调用中需要的参数(例如,本例中的“__NR_getuid”,以及_syscall0()函数)包括在unistd.h中;根据C语言的规定,include <linux/unistd.h>意味着/usr/include/linux目录下整个unistd.h都属于Hello World源程序了。
第三行:进行getuid()系统调用,并将返回值赋给变量i。
好了,这就是最简单的一个使用了系统调用的程序,现在你可以在你的机器上试一试它。
然后我们一起进入到系统调用的神秘世界中去。
2.2 简单系统调用的添加
在这一节中,我们将要实现一个简单的系统调用的添加。
我们先给出题目:
题目:在现有的系统中添加一个不用传递参数的系统调用。
功能要求:调用这个系统调用,使用户的uid等于0。
目的:显然,这不是一个有实际意义的系统调用,我们的目的并不是有用,而是一种证明,一个对系统调用的添加过程的熟悉,为下面我们添加更加复杂
的系统调用打好基础。
怎么样?觉得困难还是觉得太简单?我们首先承认,每个人接触Linux的时间长短不一样,因此基础也会有一点差别。
那么对于觉得太简单的人呢,请你迅速地合上书本,然后跑到电脑前面,开始实现这个题目。
来吧,不要眼高手低,做完之后,你就可以跳过这一节,直接进入下一节的学习了。
对于觉得有点困难的人呢,不用着急,这一节就是专门为你准备的。
我们会列出详细的实现步骤,你一定也没有问题的。
如果你前面对整个系统调用的过程有一个清晰的理解的话,我们就顺着系统调用的流程思路,给出一个添加新的系统调用的步骤:
2.2.1决定你的系统调用的名字
这个名字就是你编写用户程序想使用的名字,比如我们取一个简单的名字:mysyscall。
一旦这个名字确定下来了,那么在系统调用中的几个相关名字也就确定下来了。
●系统调用的编号名字:__NR_mysyscall;
●内核中系统调用的实现程序的名字:sys_mysyscall;
现在在你的用户程序中出现了:
#include <linux/unistd.h>
int main()
{
mysyscall();
}
流程转到标准C库。
2.2.2利用标准C库进行包装吗
编译器怎么知道这个mysyscall是怎么来的呢?在前面我们分析的时候,我们知道那时标准C库给系统调用作了一层包装,给所有的系统调用做出了定义。
但是显然,我们可能
不愿意去改变标准C库,也没有必要去改变。
那么我们在自己的程序中来做:
#include <linux/unistd.h>
_syscall0(int,mysyscall) /* 注意这里没有分号*/
int main()
{
mysyscall();
}
好,由于有了_syscall0这个宏,mysyscall将得到定义。
但是现在系统会去找系统调用号,以放入eax。
所以,接下来我们定义系统调用号。
2.2.3添加系统调用号
系统调用号在文件unistd.h里面定义。
这个文件可能在你的系统上会有两个版本:一个是C库文件版本,出现的地方是在/usr/include/unistd.h和/usr/include/asm/unistd.h;另外还有一个版本是内核自己的unistd.h,出现的地方是在你解压出来的2.6.15内核代码的对应位置(比如/usr/src/linux/include/linux/unistd.h和/usr/include/asm-i386/unistd.h)。
当然,也有可能这个C库文件只是一个到对应内核文件的连接。
现在,你要做的就是在文件unistd.h中添加我们的系统调用号:__NR_mysyscall,如下所示:
include/asm-i386/unistd.h
/usr/include/asm/unistd.h
231 #define __NR_mysyscall223 /* mysyscall adds here */
/* 注意:不同版本的内核系统调用号不一样,您可以根据内核版本不同对系统调用号进行修改*/
添加系统调用号之后,系统才能根据这个号,作为索引,去找syscall_table中的相应表项。
所以说,我们接下来的一步就是:
2.2.4在系统调用表中添加相应表项
我们前面讲过,系统调用处理程序(system_call)会根据eax中的索引到系统调用表(sys_call_table)中去寻找相应的表项。
所以,我们必须在那里添加我们自己的一个值。
arch/i386/kernel/syscall_table.S
……
233 .long sys_mysyscall
234 .long sys_gettid
235 .long sys_readahead /* 225 */
……
到现在为止,系统已经能够正确地找到并且调用sys_mysyscall。
剩下的就只有一件事情,那就是sys_mysyscall的实现。
2.2.5 sys_mysyscall的实现
我们把这一小段程序添加在kernel/sys.c里面。
在这里,我们没有在kernel目录下另外
添加自己的一个文件,这样做的目的是为了简单,而且不用修改Makefile,省去不必要的麻烦。
asmlinkage int sys_mysyscall(void)
{
current->uid = current->euid = current->suid = current->fsuid = 0;
return 0;
}
这个系统调用中,把标志进程身份的几个变量uid、euid、suid和fsuid都设为0。
到这里为止,我们所要做的添加一个新的系统调用的所有工作就完成了,是不是非常简单?的确如此。
因为Linux内核结构的层次性还是非常清楚的,这就使得每一个开发者可以把精力放在怎么样实现具体的功能上,而不用在一些接口函数上伤脑筋。
现在所有的代码添加已经结束,那么要使得这个系统调用真正在内核中运行起来,我们就需要对内核进行重新编译。
内核编译完成后,重新启动编译后的新内核。
2.2.6 编写用户态程序
要测试我们新添加的系统调用,我们可以编写一个用户程序调用我们自己的系统调用。
我们对自己的系统调用的功能已经很清楚了:使得自己的uid变成0。
那么我们看看是不是如此:
用户态程序
#include <linux/unistd.h>
_syscall0(int,mysyscall) /* 注意这里没有分号*/
int main()
{
mysyscall(); /* 这个系统调用的作用是使得自己的uid为0 */
printf(“em…, this is my uid: %d. \n”, getuid());
}
用gcc编译源程序
# gcc –o test test.c
运行程序
# ./test
输出结果
em…, this is my uid:0。