编译Linux内核

1.1实验目的

掌握系统调用的添加过程,为以后添加更复杂的系统调用奠定基础。

1.2实验内容

设计一个系统调用,功能为在终端输出CPU信息,内存信息,和运行的进程信息.

1.3实验思想(或流程图)

1. 对于CPU信息和内存信息

利用vfs_read()函数读取文件/proc/cpuinfo和/proc/meminfo中的内容,将读出的内容报错到字符数组buff1和buff2中,由于此时buff1和buff2仍属于内核态,所以使用copy_to_user()函数将这两个字符串拷贝到用户态.

2.对于进程信息

由于进程信息存储在结构体task_struct的链表中,所以采用遍历链表的方式输出目前运行中的进程的信息,由于无法像读取CPU信息那样将内容拷贝到字符串,所以使用sprintf()函数将进程信息输出到一片内存中,然后再使用tty->ops->writes()函数将这个字符串直接输出到终端.

1.4实验步骤

1.配置实验所需的环境

由于各个内核版本之间存在较大的差异,所以首先要选择合适版本的内核,可以先查看自己虚拟机中Ununtu的内核版本,自己选择的内核最好不要超过这个版本,在本次实验中,使用的是Linux-4.7.1,虚拟机平台为VMware 15.5.0 build-14665864,虚拟机版本为Ubuntu16.04.11,Windos版本为Win10 1903.同时还需要安装一些编译所必须的程序,不然会在编译的过程中报错.

2.内核裁剪

按照PPT上的步骤,完成新内核的编译和安装,对于此过程中的error和wraning应及时解决,不然会严重影响下一步的操作.其中相关的指令如下

make mrproper //清除历史编译及配置文件

make menconfig //配置新内核

make clean //清除历史编译

make bzImage //生成映像文件

make modules //编译模块

make modules_install //将内核安装到对应目录下

make install //安装新内核

update-grub //更新grub

以后的编译过程也会经常用到这些命令,应该熟记这些指令.

3.添加hello_world系统调用

a) 为了方便后续实验的进行,可以先练习添加一个简单的系统调用.PPT上给出了两种添加系统调用的方法.在本次实验中选用的是第一种实验方法.具体步骤如下:

b) 在系统调用表/usr/src/linux-source-4.15.0/arch/x86/entry/syscalls/syscall_64_tbl)中定义了系统调用号与系统调用函数的映射。新的系统调用需要在系统调用表中添加新的一项,以便用户通过系统调用号进行系统调用.一般选择在末尾追加一个就好,最好不要随便的选择,避免不必要的麻烦.例如: 329 64 hello sys_hello,注意这里并非是在整个文件的末尾,而是64bit system部分添加,文件最末尾的几个系统调用好像是32位程序专用的系统调用号,因此在添加的时候应该特别注意.

c) 在头文件/usr/src/linux-source-4.15.0/arch/x86/include/asm/syscalls.h中声明新的系统调用系统.在该文件的倒数第二行添加以下语句: asmlinkage long sys_hello(int);注意这里的函数参数应该与实际实现的函数一致,不然会出现编译失败的情况.

调用函数体写在/usr/src/linux-4.15.0/kernel/sys.c中。前两步只是声明了这个系统调用,这个系统调用的具体功能需要在这个文件中声明.

d) 在确保以上步骤都没有问题之后,可以开始新一轮的内核编译.在编译的过程中,应该注意观察终端的输出信息,如果有error和warning因及时记录,因为error和warning可能会被其他输出信息覆盖.待终端输出结束停止之后,在查找相关资料寻找解决办法,然后再执行上一条指令.

e) 待编译成功之后进入新内核,编写测试程序查看系统调用是否成功实现.

4.实现本次实验所要求的系统调用

由于第一种添加系统调用的方式成功了,所以在添加第二种的时候我尝试使用第二种添加方式.其中大部分与添加hello_world部分一致,这里只说明不一样的地方.

a) 在linux-4.7.1文件夹中创建新的文件夹info.在该文件夹中创建info.c文件和info.h文件,和Makefile文件.

b) 同时还需要修改linux-4.7.1文件中的Makefile文件,在文件中找到:core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ ,在后面添加info/

其他的一些步骤与hello_world中一致,这里不再赘述.

c) 待以上步骤完成后,再次编译内核,进入新内核后编写测试程序测试系统调用是否添加成功.

1.5测试数据设计

本实验并未用到测试数据,这里将在附件中给出测试该系统调用的测试代码.

1.6程序运行初值及运行结果分析

1) 进程信息

实验一运行结果截图

其中红框中的进程就是测试程序.

2) CPU信息

3) 内存信息

1.7实验总结

1.7.1实验中的问题与解决过程

问题1:

在使用linux-5.3.2内核版本的时候,出现了函数能够正常执行,但是函数返回值是一个随机数的问题,后来通过在网上查阅相关信息后发现可能是内核版本的问题,并参照官方的文档进行了修改,但是最终修改后好像并灭有生效,遂决定使用内核4.7.1版本.

问题2:

在使用tty->ops->write()函数输出CPU信息和内存信息的时候,出现了以下格式问题,就是这个函数好像不能很好的处理字符串中的空格和回车的问题.

解决方案:

第一次尝试是使用tty->ops->write()函数强制的回车换行,虽然结果有以下改善,但是还是不尽人意,最终改用向函数传参和使用copy_to_user()的方法.

问题3:

在读取进程信息的时候,不知道如何将链表中的各项的值保存到字符串中

解决办法:

通过查阅相关资料,使用饿sprintf()函数,将各项信息输出到指定的内存区域中

 

问题4:

在读取进程信息的过程中,发现只能输出一部分进程信息

解决方案:

不再使用一个字符串存储所有的进程信息,而是读取到一个进程信息就之直接输出,然后重复的覆盖这片内存区域,直到到达链表的尾部.

1.7.2实验收获

通过这次实验,我掌握了向linux添加一个简单系统调用的步骤,同时叶实现叶自己的系统调用.加深了自己对内核态和用户态的理解.在这期间遇到的各种问题也提高了自己独立解决问题的能力.在修改代码的过程中,一定要谨慎细心,同时需要明白每一步操作的作用.这样才会提高自己的效率,节约时间.

1.7.3意见与建议

1) 在每次编译内核之前,一定要认真细致的检查每一步设置是否正确.因为如果一旦某一在最初的某一步中除了问题,后期是很难排查和纠正的.

2) 如果在编译过程中,因时刻注意终端输出的信息,因为一些error和warning可能会被淹没在其他的编译信息中,一旦发现错误,应当立即截图,待输出结束后再修改,然后再执行出错前执行的那一条指令.

3) 不必每次编译都从make mrproper开始,对于只修改了函数的编译,可以直接从make bzImage指令开始,同时Makfile会只编译修改过的部分,这样就可以大大的节约编译的时间.

4) 注意Makefile文件中的缩进是用tab键来控制的.

5) 当进入新内核的时候,如果出现无法开机的情况,可以尝试提高虚拟机的性能.比如加大内存等.

1.8附件

 

Leave a Reply

发表评论

邮箱地址不会被公开。 必填项已用*标注

本站所有文章均为原创,若需转载,请注明出处©twn29004 | 陕ICP备 20000896 网站备案号

总访问量:9605279    今日访问量:349    您是今天第:349 个访问者