资讯详情

操作系统leb6实验报告

实验名称:实验6:编译内核和增加Linux系统调用

实验目的

  • 1、熟悉Linux编译内核的过程和方法
  • 2.熟悉系统调用的流程

实验内容

本实验由两部分组成。 第一部分仅仅要求编译一个干净的内核且加载成功,并不需要对内核修改。 第二部分是修改编译成功的核心,为用户提供新的系统调用,扩大系统服务。 实现系统调用psta,获取过程中的一些信息。原型如下:int psta(struct pinfo *buf); 参数buf用于存储过程信息的缓冲区。 pinfo定义如下:

struct pinfo { 
         int nice;/*进程的nice值*/ pid_t pid;/*进程ID*/ uid_t uid;/*用户的过程所有者ID*/ };  

系统调用成功时返回值为0,系统调用失败时返回错误码EFAULT,表示 buf 指向非法地址空间。误码定义见核头文件asm-generic/errnoasm-generic/errno-base.h。

实验环境

  1. VMware
  2. Fedora7

实验作业

一、Fedora下内核编译

第一步,下载内核

首先,我们应该决定编译哪个版本的核心。一般来说,待编译的核心版本(以下所称新核心)不低于当前正在运行的核心版本。如果两个版本之间的差距很大,则可能需要更新 gcc,binutils和 modutils编译工具等。我们只需要采用Linux发行版对应的内核版本可以使所有内核编译工具现成,无需更新,无疑减少了不必要的麻烦。首先查看当前环境下的核版本号: ![](https://img-blog.csdnimg.cn/img_convert/9232f05668fae0631b2a3df82c7564a4.png#from=url&id=Agorb&margin=[object Object]&originHeight=252&originWidth=426&originalType=binary&ratio=1&status=done&style=none) 可以看出,本机内核版本号为2.6.23.17,在http://www.kernel/org/pub/linux/kernel/v2.6下载相应的内核压缩包。 ![](https://img-blog.csdnimg.cn/img_convert/7af125224064188bcb4a59534d47fca8.png#from=url&id=eh0jx&margin=[object Object]&originHeight=858&originWidth=914&originalType=binary&ratio=1&status=done&style=none)本实验采用2.6.21.tar.gz,下载到桌面后,移动到/usr/src,并解压。 ![](https://img-blog.csdnimg.cn/img_convert/55d0bf3b8cb9a3f30fe5ce3181c2dcc3.png#from=url&id=LWser&margin=[object Object]&originHeight=192&originWidth=376&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/d5cecb85aa1167612ee2fce8f08fe9bc.png#from=url&id=YAdVI&margin=[object Object]&originHeight=483&originWidth=631&originalType=binary&ratio=1&status=done&style=none) 解压完成后,进入目录观察: ![](https://img-blog.csdnimg.cn/img_convert/5f8cb83b051073f7e6752d2fb320e6e0.png#from=url&id=jQvNu&margin=[object Object]&originHeight=307&originWidth=605&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/5dd4d0d73ddd3de4e212a6177d5c0e48.png#from=url&id=bd5js&margin=[object Object]&originHeight=577&originWidth=779&originalType=binary&ratio=1&status=done&style=none)

第二步是生成内核配置文件.config

Linux.核心代码非常大,适用于许多系统结构,包括大量的驱动程序。用户在生成核心时应根据实际情况进行配置。所有配置都将保存在核心代码树的顶级目录中.config 在配置文件中。在核编译过程中,生成正确的配置文件是至关重要的一步。 配置文件可以从零开始生成,但没有必要。由于目前正在运行的核心已经有了相应的配置文件,该文件在/boot在目录中,使用它作为新内核配置文件的模板无疑是更好的方法。因此,我们将配置文件复制到/usr/src/linux-2.6.21.7目录下,命令如下:

make mrproper cp /boot/config-`uname -r` ./.config  
介绍:`uname -r` 

uname 可显示电脑以及操作系统的相关信息。 -r或–release 显示操作系统的发行编号。 方便我们进入相关目录。 第一个命令make mrproper用来保证内核树干净。如果内核树已经编译,则该命令有效。如果内核树是第一次编译,则可以省略该命令。 ![](https://img-blog.csdnimg.cn/img_convert/12e0f4b3e7c5401f98f1ca856aee2c3e.png#from=url&id=d3Lw3&margin=[object Object]&originHeight=83&originWidth=554&originalType=binary&ratio=1&status=done&style=none) 虽然现在有了模板,但是.config文件的配置不一定包括新内核的所有编译选项(因为新内核可能是更新版本,比如2.6.可使用以下命令:

make oldconfig 

![](https://img-blog.csdnimg.cn/img_convert/0d533aff28cd1514e144c9f4e5b67d72.png#from=url&idFazep&margin=[object Object]&originHeight=61&originWidth=639&originalType=binary&ratio=1&status=done&style=none) 该命令读取.config文件并根据新内核版本更新它。具体过程是这样的,该命令输出新内核所有的配置项,如果配置项已经在.config中有设置,则输出设置值;如果是新项,程序会停下来要求用户输入设置值,值的具体含义见附录C。用户输入值后程序继续运行直到所有配置项处理完毕。用户也可以使用如下命令:

make silentoldconfig

该命令的功能和“make oldconfig”相似,不过它不输出信息,除非是新选项需要用户输入的时候。到现在为止,生成的.config 文件就可以使用了,进入第3步。 ![](https://img-blog.csdnimg.cn/img_convert/360bdb38bbe78f6f2c0af50d33091de7.png#from=url&id=JdMlz&margin=[object Object]&originHeight=80&originWidth=423&originalType=binary&ratio=1&status=done&style=none)

第三步、编译和安装新内核

在编译内核之前,还可以定义用户自己的内核版本号,这样做是为了便于识别。在内核代码树的根目录下有文件Makefile ![](https://img-blog.csdnimg.cn/img_convert/32c52c6cb75f7b91e7b5140a83824d65.png#from=url&id=noRtX&margin=[object Object]&originHeight=425&originWidth=636&originalType=binary&ratio=1&status=done&style=none) 在编译内核之前,还可以定义用户自己的内核版本号,这样做是为了便于识别。在内核代码树的根目录下有文件Makefile,它的前4行是:

VERSION =2
PATCHLEVEL=6
SUBLEVEL =21
EXTRAVERSION = 

把第4行改成EXTRAVERSION = .7-ywl,这样新内核版本号就是2.6.21.7-ywl。 ![](https://img-blog.csdnimg.cn/img_convert/f05694b07d4c2dad21ea71ab3a2780ae.png#from=url&id=aKUzW&margin=[object Object]&originHeight=147&originWidth=285&originalType=binary&ratio=1&status=done&style=none) ​

然后执行下面三个命令:

make all
make modules_install
make install

make all将生成期望的内核映像及模块 ![](https://img-blog.csdnimg.cn/img_convert/e361e5a219b03da456252a2c9141c471.png#from=url&id=kXP7z&margin=[object Object]&originHeight=66&originWidth=363&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/089781f578a4ab3f5ffb20149769a34a.png#from=url&id=kWw7J&margin=[object Object]&originHeight=332&originWidth=350&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/7c360b48fea6a2e7833084c3c927e77a.png#from=url&id=HWVCU&margin=[object Object]&originHeight=136&originWidth=500&originalType=binary&ratio=1&status=done&style=none) make modules_install将安装模块到“默认目录/lib/modules/<内核版本号>”下面。 ![](https://img-blog.csdnimg.cn/img_convert/073db802e68276d797d570b89b58e59b.png#from=url&id=NGRlk&margin=[object Object]&originHeight=174&originWidth=671&originalType=binary&ratio=1&status=done&style=none) make install最终将内核映像等几个文件复制到“/boot”目录,并修改引导程序的配置以启用该新内核。 ![](https://img-blog.csdnimg.cn/img_convert/f4168f83b6b13c5fc31b93a308bd853e.png#from=url&id=zLK9d&margin=[object Object]&originHeight=115&originWidth=635&originalType=binary&ratio=1&status=done&style=none) ​

如果上述三个命令均执行成功,可以观察到引导程序grub的配置文件/boot/grub/menu.Ist的文件内容: ![](https://img-blog.csdnimg.cn/img_convert/a0dfb58450c16a4ffc917570f28cd7a5.png#from=url&id=IOk1Y&margin=[object Object]&originHeight=444&originWidth=626&originalType=binary&ratio=1&status=done&style=none) 为了以后能直接操作菜单,把 hiddenmenu那一行注释掉(行的最前面加一个“#”字符即可)或删除,为了方便反应,我将菜单出现的时间调整为20。 ![](https://img-blog.csdnimg.cn/img_convert/a90e8303114eed4fad5dd5b06459caa4.png#from=url&id=e2x7P&margin=[object Object]&originHeight=407&originWidth=603&originalType=binary&ratio=1&status=done&style=none) 重启,启动新内核 ![](https://img-blog.csdnimg.cn/img_convert/f3247a780969a28e0aa474bef934d2e3.png#from=url&id=Qp81w&margin=[object Object]&originHeight=83&originWidth=494&originalType=binary&ratio=1&status=done&style=none)

发现新内核在其中,选择并启用 ![](https://img-blog.csdnimg.cn/img_convert/9a4dc67dc2dd0a2e02f9a39d5364b4bd.png#from=url&id=YDqsH&margin=[object Object]&originHeight=282&originWidth=598&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/a542b655842d095558ba355f97e97356.png#from=url&id=EW5ft&margin=[object Object]&originHeight=566&originWidth=778&originalType=binary&ratio=1&status=done&style=none) 启动完毕,正常打开 ![](https://img-blog.csdnimg.cn/img_convert/e25aaa5c0381e5e5ce873e9f9028cad0.png#from=url&id=kbfyO&margin=[object Object]&originHeight=435&originWidth=638&originalType=binary&ratio=1&status=done&style=none)

二、添加past系统调用

若系统调用名为xxx,则内核对应的实现函数名一般为sys_xxx,据此我们把 psta系统调用对应的内核函数命名为 sys_psta。下面假定当前工作目录是/usr/src/linux-2.6.21.7,给出添加系统调用psta的基本过程。

(1)调整arch/i386/kernel/syscall_table.S

在文件 arch/i386/kernel/syscall_table.S 的尾部加上要新增的系统调用函数名称,如阴影行所示,注释中320表示它的系统调用号。 进入目录: ![](https://img-blog.csdnimg.cn/img_convert/6f598f9eb8b9571ce6163f1d53eb88e8.png#from=url&id=XIGiJ&margin=[object Object]&originHeight=184&originWidth=573&originalType=binary&ratio=1&status=done&style=none) 打开文件: ![](https://img-blog.csdnimg.cn/img_convert/6d243b995c929c01a456d2f23dc61cf1.png#from=url&id=vDggh&margin=[object Object]&originHeight=49&originWidth=507&originalType=binary&ratio=1&status=done&style=none) 进行修改 ​

![](https://img-blog.csdnimg.cn/img_convert/86c1c823af4d0d6a894ae57f4907ee1e.png#from=url&id=oEwLT&margin=[object Object]&originHeight=204&originWidth=476&originalType=binary&ratio=1&status=done&style=none)

(2)在include/linux目录下添加头文件psta.h

#ifndef _LINUX_PSTA_H
#define _LINUX_PSTA_H
struct pinfo
{ 
        
	int nice;
	pid_t pid;
	uid_t uid;
};
#endif

![](https://img-blog.csdnimg.cn/img_convert/a107a581736fc11e9b8b0e8563dc593b.png#from=url&id=q0WHP&margin=[object Object]&originHeight=124&originWidth=623&originalType=binary&ratio=1&status=done&style=none)

(3)在kernel目录下新建文件psta.c

在该文件中实现sys_psta函数

#include <linux/linkage.h>
#include <linux/psta.h>
#include <linux/unistd.h>
#include <linux/types.h>
#include <linux/kernel.h>

asmlinkage int sys_psta( struct pinfo* buf)
{ 
        
	buf->pid=current->pid;
	buf->uid=current->uid;
	buf->nice=10;
	return 0; 
}


asmlinkage定义在文件 linux/linkage.h中,表示函数的参数通过栈传递,而不是通过寄存器,所有系统调用都遵循这种参数传递方式。 sys_psta的实现非常简单,无非就是把task_struct结构中的几个成员复制到用户态空间。值得一提的是,nice表示进程的优先级,取值范围为[-20,19],数值越低表示优先级越高。内核没有直接存储nice值,而是通过一个简单的变换后将它存放在 task_struct结构的static_prio成员中。两者之间的转换关系见下面几个宏(位于文件 kernel/sched.c中),其中MAX_RT_PRIO值为100。

#define NICE_TO_PRIO(nice) (MAX_RT_PRIO +(nice)+ 20)
#define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20)
#define TASK_NICE(p) PRIO_TO_NICE((p)->static_prio)

(4)修改文件 kernel/Makefile

使psta.c能在内核编译时可见。Makefile 中有如下一行:

obj-y= sched.o fork.o exec_domain.o panic.o printk.o profile.o\

添加psta.o到该行中:

obj-y = psta.o sched.o fork.o exec_domain.o panic.o printk.o profile.o\

![](https://img-blog.csdnimg.cn/img_convert/eabc97705dc62d2804a1199c497ffd93.png#from=url&id=yJuFb&margin=[object Object]&originHeight=506&originWidth=669&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/c0aff5a636a16356705383d57e8bf136.png#from=url&id=ZWvxa&margin=[object Object]&originHeight=466&originWidth=631&originalType=binary&ratio=1&status=done&style=none) 值得说明的是,第2步中sys_psta的实现不一定要放在一个新文件中,例如文件kernel/sys.c也许就是添加sys_psta系统调用的合适位置,这样第3步就没有必要了。 ​

(5)修改include/asm-i386/unistd.h

在include/asm-i386/unistd.h里面加上系统调用号的宏定义,在该文件中有如下几行:

#define_NR_epoll_pwait319
#ifdef _KERNEL__
#define NR_syscalls 320

可以看到,按照惯例系统调用号的宏名以“_NR_”开头,而其后跟着的数值则是系统调用号。此外,NR_syscalls表示的值应该是最大的系统调用号加一。所以修改后的内容如下: ![](https://img-blog.csdnimg.cn/img_convert/ecf7abec1f76ac900732a47b8a6479a7.png#from=url&id=iWfbK&margin=[object Object]&originHeight=50&originWidth=331&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/05488f2d9260c42146bff2603783aa26.png#from=url&id=vCdUp&margin=[object Object]&originHeight=452&originWidth=672&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/2857bcb238deef621997429bd61dbe84.png#from=url&id=QhZbF&margin=[object Object]&originHeight=491&originWidth=666&originalType=binary&ratio=1&status=done&style=none)

(6)修改include/linux/syscalls.h

加上函数sys_psta的声明。在该文件的首部添加一行: #include <linux/psta.h> 在该文件的最后一行“#endif”之前添加一行: asmlinkage int sys_psta(struct pinfo *buf); ![](https://img-blog.csdnimg.cn/img_convert/0aad66e28bb94e7370617d2f796949f4.png#from=url&id=qzCET&margin=[object Object]&originHeight=78&originWidth=314&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/4af8d9631bf430c99b17ed3787ea95fb.png#from=url&id=KCejt&margin=[object Object]&originHeight=235&originWidth=336&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/5fcab0a9cf6c6fd21e119557f83b8cb9.png#from=url&id=x9xvY&margin=[object Object]&originHeight=346&originWidth=521&originalType=binary&ratio=1&status=done&style=none)

(7)重新编译内核。

这里特别要提醒初学者, 因为我们已经有了正确的.config,最好备份一份到别的目录下以防被删除。上一次编译内核时已经在内核目录下生成了许多中间文件,所以本次编译内核之前要删除这些文件。这可以使用如下命令:

make mrproper

该命令连.config文件都会删除,所以等命令执行完后需要把备份的.config文件复制回来,然后执行前面的第3步就可以了。因为新内核包含了用户自己的代码,所以很可能会在“ makeall”时因编译出错而停止,根据错误提示信息修改错误后,可以重复本步骤。 清空上次编译的中间文件: ![](https://img-blog.csdnimg.cn/img_convert/d349c61bf18add90f26298710fb943eb.png#from=url&id=AhRKc&margin=[object Object]&originHeight=402&originWidth=616&originalType=binary&ratio=1&status=done&style=none) ​

![](https://img-blog.csdnimg.cn/img_convert/04cf352197ea13297b9a3b42cd3a7365.png#from=url&id=llD8j&margin=[object Object]&originHeight=362&originWidth=525&originalType=binary&ratio=1&status=done&style=none) ​

三、简化实现

测试一

调用系统函数输出hello, 操作与之前一样: ​

测试程序:

#include<unistd.h>
#include<sys/syscall.h>
#include<assert.h>
#include<errno.h>

struct pinfo
{ 
        
	int nice;
	pid_t pid;
	uid_t uid;
};

int main(void)
{ 
        
	assert(6==syscall(__NR_write,1,"hello",6));
	return 0;
}

运行图: ![](https://img-blog.csdnimg.cn/img_convert/2aa882f7f944465c75c3925f5c4f6f56.png#from=url&id=wF60J&margin=[object Object]&originHeight=354&originWidth=580&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/a0cf3d5c0a711b0b852f1a210815af1d.png#from=url&id=FCWdy&margin=[object Object]&originHeight=102&originWidth=311&originalType=binary&ratio=1&status=done&style=none)

测试二

添加的系统调用会输出hello world, 操作与之前一样: 头文件:

#ifndef _LINUX_PSTA_H
#define _LINUX_PSTA_H
struct pinfo
{ 
        
	int nice;
	pid_t pid;
	uid_t uid;
};
#endif

.c文件

#include <linux/linkage.h>
#include <linux/psta.h>
#include <linux/unistd.h>
#include <linux/types.h>
#include <linux/kernel.h>

asmlinkage int psta( struct pinfo* buf)
{ 
        
	//buf->pid=current->pid;
	//buf->uid=current->uid;
	//buf->nice=10;
    printf("hello world");
	return 0; 
}

测试程序:

#include<unistd.h>
#include<sys/syscall.h>
#include<assert.h>
#include<errno.h>

struct pinfo
{ 
        
	int nice;
	pid_t pid;
	uid_t uid;
};

int main(void)
{ 
        
	struct pinfo info;
	int ret;
	ret=syscall(320,&info);
	return 0;
}

运行图:

![](https://img-blog.csdnimg.cn/img_convert/c58773bbf6acc94fd5914a4384cfb58b.png#from=url&id=kt7W7&margin=[object Object]&originHeight=82&originWidth=330&originalType=binary&ratio=1&status=done&style=none)​

实验结果

实现系统调用psta,获取进程的若干信息。 实验代码: 头文件与之前相同 .c文件

#include <linux/linkage.h>
#include <linux/psta.h>
#include <linux/unistd.h>
#include <linux/types.h>
#include <linux/kernel.h>

asmlinkage int sys_psta( struct pinfo* buf)
{ 
        
	buf->pid=current->pid;
	buf->uid=current->uid;
	buf->nice=10;
	return 0; 
}


测试程序:
#include<unistd.h>
#include<sys/syscall.h>
#include<assert.h>
#include<error.h>
#include<stdio.h>

struct pinfo{ 
        
	int nice;
	pid_t pid;
	uid_t uid;
};

int main(void)
{ 
        
	struct pinfo info;
	int ret;
	ret=syscall(320,&info);
	printf("%d\n%d\n%d\n",info.pid,info.uid,info.nice);
	return 0;

}

运行图: ![](https://img-blog.csdnimg.cn/img_convert/e111860349e67401ca53649d4eb19312.png#from=url&id=gtbbG&margin=[object Object]&originHeight=417&originWidth=603&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/61f95b377bc89bea703dcfc77a764888.png#from=url&id=eqKhZ&margin=[object Object]&originHeight=282&originWidth=519&originalType=binary&ratio=1&status=done&style=none) ![](https://img-blog.csdnimg.cn/img_convert/51209368266571a2617f91656927241a.png#from=url&id=Urk6I&margin=[object Object]&originHeight=401&originWidth=579&originalType=binary&ratio=1&status=done&style=none) 测试结果如下: ![](https://img-blog.csdnimg.cn/img_convert/e51b157c933245ec84911dfc3f0b46b4.png#from=url&id=OnNyT&margin=[object Object]&originHeight=214&originWidth=424&originalType=binary&ratio=1&status=done&style=none) 至此实验结束.

实验总结

这是操作系统的最后一次必选实验了,通过这次实验课我对操作系统的了解更深刻一步,在前几次的基础上我掌握了Linux文件系统的基本原理、结构和实现方法,并且掌握了Linux文件系统中文件的建立、打开、读/写、执行、属性等系统调用的使用,学会设计简单的文件系统并实现一组操作。学会利用系统调用完自己需要的功能。但还有很多需要改进的地方,比如写入文件的大小限制,还有容错性检验等等,还有很多不足之处。

标签: 二极管b360b

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台