《linux 操作系统》实验报告
年级、专业、班级
姓名
实验题目
Linux 内核编译与系统调用
实验时间 2014/4/16
实验地点
A 区主教学楼 0414
实验成绩
教师评价:
实验性质
□验证性 □设计性 □综合性
□算法/实验过程正确; □源程序/实验内容提交
□程序结构/实验步骤合理;
□实验结果正确;
□语法、语义正确;
□报告规范;
其他:
评价教师签名:
一、实验目的
(1)掌握 linux 内核编译过程
(2)掌握 fork()和 clone()创建进程
二、实验项目内容
1)Linux 内核编译;
2) 分别用 fork()和 clone()创建进程:列出子进程和父进程的进程号、进
程名和进程状态等信息;
三、实验过程或算法(源程序)
编译内核:
为何要重新编译内核?
Linux 作为一个自由软件,在广大爱好者的支持下,内核版本不断更新。新的内
核修订了旧内核的 bug,并增加了许多新的特性。如果用户想要使用这些新特性,或
想根据自己的系统度身定制一个更高效,更稳定的内核,就需要重新编译 Linux 内核。
通常,更新的内核会支持更多的硬件,具备更好的进程管理能力,运行速度更快、
更稳定,并且一般会修复老版本中发现的许多漏洞等,经常性地选择升级更新的系统
内核是 Linux 使用者的必要操作内容。
一、打开虚拟机, power on Ubuntu。
二、在登陆界面输入密码:123456
三、将 linux-3.4.38.tar.xz 包移动到“主文件夹”目录下
报告创建时间:
四、打开命令终端:ctrl+alt+t;
查看当前内核版本号:$ uname -a
获取 root 权限:
$ sudo su
提示输入密码:123456
五、解压源代码包:
# xz –d linux-3.4.38.tar.xz
# tar xvf linux-3.4.38.tar /usr/src/linux-3.4.38
六、编译内核喽
(一)清除当前目录下残留的.config 和.o 文件
在终端中进入刚刚的这个/usr/src/linux-3.4.38 文件夹,输入命令:
$ make mrproper
当然我们这里是第一次编译这个内核,所以不存在清理不清理,如果以后需要对这个
内核重新编译,这一步骤当然是十分必要的啦。
(二)安装 ncurses
作为操作系统的内核,其内容和功能必然非常繁杂,包括处理器调度,内存管理,
文件系统管理,进程通讯以及设备管理等等,而对于不同的硬件,其配置选项也不相
同,所以在编译源代码之前必须设置编译选项。其实我觉得这一步是升级内核整个过
程中最有技术含量的,因为要根据自己的需要正确选择 yes or no 需要对计算机方方
面面的知识都有所了解。但是这里的选项实在是太多了,大概有几百项之多,我以前
曾尝试着一项一项的选,但是最后还是放弃了,因为有很多选项不是很明白。
既然这样,难道没有什么简便的方法么?当然有!那就是 make menuconfig 或者
make xconfig。我使用的是 make menuconfig,但是前提条件是要装 ncurses。
对于下载好的这个 ncurses 包,我们把它放到/usr/local 下面;接着终端进入这个文件
夹:$ cd /usr/local。
解压缩并且释放文件包:$ tar zxvfncurses.tar.gz
按照你的系统环境制作安装配置文件:$ ./configure
编译源代码并且编译 NCURSES 库:$ make
切换到 root 用户环境: $ make install
这样我们的 ncurses 库就已经编译完成了。
(三)配置内核编译选项
在 make menuconfig 过程中也会有一些选项需要你来设置*, y, n 或者 m,选择*
表示选项中的内容被直接编入内核中,选择 m 表示选项中的内容编入内核,而只是
编成独立的 module,用到时才调用。
在当前文件路径下,输入命令:
makemenuconfig
这里就出现了一个配置选项的图形化界面,因为我们是用的虚拟机,所以一定要
选择把 SCSI 设备编译进去。最后保存为.config 文件并退出。
(四)编译内核
这步是时间最长的一个步骤,一般在 3 个小时左右。
编译内核只需在终端输入 make,然后等待编译的完成。
(五)编译和安装内核模块
输入 make modules_install,这步很快能完成。
(六)安装内核
输入 make install
(七)生成启动
依次输入:
sudomkinitramfs -o /boot/initrd.img-2.6.36
sudo update-initramfs -c -k 2.6.36
sudo update-grub2 //自动修改系统引导配置,产生 grub.cfg 启动文件。
用 fork() 和 clone()分别创建进程,并列出子进程和父进程的进程号、进程名和进程
状态。
Fork():
一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创
建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如
果初始参数或者传入的变量不同,两个进程也可以做不同的事。
fork()创建一个子进程,即 intpid = fork(), fork()进入内核,调用一次返回两次,
如果返回的 pid == 0 子进程先返回,如果 pid> 0(此时返回的是子进程的 pid),父进
程先返回。至于子进程和父进程哪个先返回,要看内核的调度算法。输出次序乱序,
都有可能。父进程和子进程是并发执行的。返回指的是这个函数 return pid;这个语句
被执行了两次。
因为创建了一个子进程后,那么子进程中的 fork 也要返回一次。
在 Linux 下如果内存没有被写的话,那么父子进程是共用内存空间的,所以内存
中的同一个 fork 函数会在两个进程中调用到。在父进程中返回的就是子进程 id,子
进程中返回的是 0.
Clone():
我们这里主要用带参数的 clone()函数来创建“轻量级的进程”—线程。线程的创
建和普通进程的创建类似,只不过在调用 clone()的时候需要传递一些参数标志来指
明需要共享的资源:
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
这里 fn 是函数指针,我们知道进程的 4 要素,这个就是指向程序的指针,就是
所谓的“剧本", child_stack 明显是为子进程分配系统堆栈空间(在 linux 下系统堆栈空
间是 2 页面,就是 8K 的内存,其中在这块内存中,低地址上放入了值,这个值就是
进程控制块 task_struct 的值),flags 就是标志用来描述你需要从父进程继承那些资源,
arg 就是传给子进程的参数)。下面是 flags 可以取的值
CLONE_PARENT 创建的子进程的父进程是调用者的父进程,新进程与创建
它的进程成了“兄弟”而不是“父子”
CLONE_FS
目录、umask
子进程与父进程共享相同的文件系统,包括 root、当前
CLONE_FILES
子进程与父进程共享相同的文件描述符(file descriptor)
CLONE_NEWNS 在新的 namespace 启动子进程,namespace 描述了进程的文
表
件 hierarchy
CLONE_SIGHAND 子进程与父进程共享相同的信号处理(signal handler)表
CLONE_PTRACE 若父进程被 trace,子进程也被 trace
CLONE_VFORK
CLONE_VM
CLONE_PID
子进程与父进程运行于相同的内存空间
子进程在创建时 PID 与父进程一致
父进程被挂起,直至子进程释放虚拟内存资源
CLONE_THREAD
Linux 2.4 中增加以支持 POSIX 线程标准,子进程与父进
程共享相同的线程群
fork.c
#include
#include
void main()
{
intpid;
pid = fork();
if(pid == -1)
printf(“Erro!\n”);
else if(pid == 0)
printf(“childprocess is running, parentpid is %d, childpid is %d.\n ”, getpid(), pid);
else
printf(“parentprocess is running, parentpid is %d, childpid is %d.\n ”, getpid(), pid”);
}
clone.c
#include
#include
#include
#include
#define FIBER_STACK 7900
void *stack;
int do_something()
{
printf("childthread is running, the pid is: %d\n", getpid());
free(stack);
sleep(5);
printf("childthread exits!!!\n");
exit(0);
}
int main()
{
stack = malloc(FIBER_STACK);
if(!stack)
printf("failed in creating thread!!!\n");
exit(0);
{
}
printf("creating childthread!!!\n");
clone(&do_something, (char *)stack+FIBER_STACK, CLONE_VM|CLONE_VFORK,
0);
printf("parentthread is running, the pid is: %d\n", getpid());
exit(1);
}
(注意:在编译时要用到 gcc 的-D_GNU_SOURCE 选项)
四、实验结果及分析和(或)源程序调试过程
(一)内核编译
1 查询原 linux 内核版本
2 解压源代码包
3 清除当前目录下残留的.config 和.o 文件
4 安装 ncurses
4.1 解压 ncurses