基于Redhat 9.0实现Linux 0.01编译与运行

2011-01-23 03:52于江涛
通化师范学院学报 2011年6期
关键词:镜像文件源代码命令

于江涛,曲 波

(1.通化师范学院 计算机科学系,吉林 通化 134002;2.南京晓庄学院 数学与信息技术学院,江苏 南京 211171)

作为一种开源软件,Linux操作系统自问世以来发展迅速,成为主流操作系统之一.由于其开放性,被国内外众多大学作为操作系统课程的教学平台及实验平台.然而随着Linux版本的不断增长,其代码量也飞速膨胀,当前Linux操作系统代码量已达数百万行之多.对如此庞大的源代码作分析研究,对于普通工程技术人员及本科计算机专业学生而言显然是无能为力的.

Linux 0.01是Linux操作系统创始人Linus最早实现的一个操作系统版本,其全部代码总共只有9000行左右.尽管代码量不是很大,却实现了操作系统所需要的基本功能[1].

首先,它是一个真正的多任务系统,具有内核模态和用户模态,通过进程实现系统的多任务功能.其次,它具备了多用户系统的基本功能;尽管没有多用户登录功能,但在很多方面,如文件的访问权限、进程的信号处理等,都已经体现出多用户的管理机制.第三,它具备了现代操作系统的四个主要部分:进程管理、设备管理、内存管理及文件系统.第四,它是一个真正的操作系统,能够在X86保护模式硬件环境下独立启动,而不是一个寄生在其它操作系统环境下的模拟系统.第五,它可以在Shell环境下运行,执行常用的Linux基本命令.

总之,Linux 0.01结构清晰、代码规模较小,比较适合于初学者分析学习.

由于Linux 0.01是Linux的早期版本,因此其编译环境及运行环境与当前版本有巨大差别,而当年的开发及运行环境已不复存在,所以无法对其原始版本直接编译运行,需要对其源码作必要修改.然而从目前情况来看,各种修改版本基本上只是实现了在当前操作系统环境下的编译链接.所谓运行,也只是在X86保护方式下引导启动,并没有实现根文件系统,因此也就无法使用Shell环境.

对Linux 0.01及Linux 0.11的文件系统源代码[2,3]进行对比分析,不难发现二者在文件系统方面没有本质差别.二者都是使用早期的Minix文件系统,尤其是其运行可执行文件部分的主要代码基本一致.笔者参照Linux 0.11源代码,对Linux0.01与Shell运行相关的代码部分作了精心修改,并增加了动态链接系统调用,使用Linux 0.11的根文件系统,成功实现了Linux 0.01的启动运行,可在Shell环境下执行Linux基本命令,还可用GCC编译运行C语言程序.

1 Linux 0.01改造的关键技术及编程要点

1.1 关键技术

Linux 0.01改造[1]的关键技术在于:

(1)编译环境的选择.考虑到系统稳定性、可靠性及方便性,笔者采用Redhat 9.0平台作为开发环境,GNU工具链作为开发工具.

由于Linux 0.01使用了汇编程序,因此涉及到汇编器的选择.Linux 0.01从一开始就使用GNU工具链作开发工具,而且除boot.s外的所有汇编程序都使用AT&T语法,所以使用GNU工具链的AT&T语法汇编器是最佳选择.

(2)源代码语法的修改.由于汇编器及编译器的改变,涉及到源代码语法的差别.在汇编语言程序中,主要是变量名的命名等;在C语言程序中主要是内嵌汇编(embedded assembly)语法等.

(3)源程序功能的修改.主要是init()函数的修改,使之更适于Shell命令调用;其次是增加与动态链接相关的系统调用.

由于多数Shell命令运行时需要动态链接库支持,所以动态链接系统调用是必要的先决条件.

(4)根文件系统的创建.根文件系统是Linux0.01启动运行的基础,由于编译版本及文件系统结构的原因,不能直接采用当前Linux操作系统的根文件系统.笔者在网上下载了多个Linux 0.11版本的根文件系统,经实验比较,最后选定了一个较合适的版本作为Linux 0.01的根文件系统,其镜像文件容量为60M,CHS结构为121个柱面、4个磁头、每道63扇区.

1.2 编程要点[1,3]

(1)修改Makefile文件.首先,修改工具名称,gas改为as,gld改为ld,gar改为ar.其次,修改汇编选项,将汇编器(as)的-c选项去掉,将编译器(gcc)的-fcombin-regs及-mstring-insns选项去掉.第三,增加-m386选项,使之不含80486及以上CPU指令,使内核可以在80386机器上运行.

所有子目录中的Makefile都要依据上述要求作相应修改.

(2)改写boot/boot.s文件.该文件是Linux 0.01的bootloader,当年Linus是用as86汇编语言编写的.为统一使用GNU工具链汇编器as,需要将该文件按AT&T语法重写.

由于该程序规模较小,直接手工改写并不困难.也可以用INTEL-AT&T转换工具,但一般也还需要手工整理.

值得注意的是,as汇编后的默认格式为ELF,要将boot目标文件作为引导扇区镜像,还需要用objcopy将其转换成纯二进制镜像文件.

与之相关,tools/build.c也要作相应修改,以适应新的boot镜像文件.

(3)汇编程序的修改.首先是C语言程序引用变量的表示方法,早期的汇编程序要在引用变量前加一下划线“_”,而现在的GCC编译器可直接识别,因此需要将所有引用变量前的下划线去掉.其次是内存对齐指令align值的修改,早期版本用乘方数表示,而现在版本直接使用其值(如早期的align 2,现在表示为align 4).

(4)C语言内嵌汇编的修改.Linus当年在Linux 0.01中大量使用内嵌汇编.随着汇编器和编译器版本的增加,内嵌汇编的语法有很大改变,主要是不再需要人工指定变量所使用的CPU寄存器.

因此要去掉所有_asm__(“ax”)代码,以及内嵌汇编代码中所有对寄存器内容的无效声明,例如:

…:“cx”,“di”,“si”)

应改为

…)

1.3 init()函数关键代码简介

笔者对init()函数作了修改,使之更适用于Shell的运行,其关键代码如下:

static char * argv[] ={“-”,NULL};

static char * envp[]={“HOME=/usr/root”,NULL};

static char * vmsg=“ Linux-0.01-rh9===”;

static char * gmsg=“Adapted... ”;

void init(void)

{int i,pid;

setup();

(void) open(“/dev/tty0”,O_RDWR,0);

(void) dup(0);(void) dup(0);

printf(“%d buffers=%d bytes buffer space ”,NR_BUFFERS,NR_BUFFERS*BLOCK_SIZE);

printf(vmsg);printf(gmsg);

while(1){

if((i=fork())<0)

printf(“Fork failed in init ”);

else if (!i){

close(0);close(1);close(2);

setsid();

(void) open(“/dev/tty0”,O_RDWR,0);

(void) dup(0);(void) dup(0);

_exit(execve(“/bin/sh”,argv,envp));

}

pid=wait(&i);

printf(“child %d died with code %04x ”,pid,i);

sync();

}

_exit(0);/* NOTE! _exit, not exit()*/

}

该函数首先调用setup()函数,读入硬盘分区参数,装入(mount)根文件系统;然后,用读写方式打开tty终端设备,并返回标准输入设备句柄,dup(0)用来复制句柄,产生标准输出及标准错误输出句柄;接下来显示系统信息及版本信息;最后进入无限循环,在其中创建子进程,在子进程中首先关闭父进程的标准输入、标准输出及标准错误输出,然后重新以读写方式打开tty设备并复制及柄;最后调用execve()函数,由该函数将Shell命令(/bin/sh)装入内存并运行;wait()函数使父进程暂时阻塞,等待子进程终止.

当子进程中的Shell程序终止时,引起_exit()函数的执行返回,使子进程终止;这时,父进程被唤醒,产生一条系统提示后进入下一轮循环.

2 Linux 0.01编译运行实例

笔者基于Redhat 9.0平台,按照上述方法成功改造了Linux 0.01系统,在bochs虚拟机成功运行,其运行脚本的关键代码如下:

megs:16

floppya:1_44=“linux-fd.img”,status=inserted

ata0-master: type=disk,path=“hdc-0.11.img”,mode=flat,cylinders=121,heads=16,spt=63

boot: a

上述脚本表示:虚拟机内存容量为16M;使用的软盘镜像文件为linux-fd.img;硬盘镜像文件为hdc-0.11.img,其CHS结构为121个柱面、16个磁头、每道63个扇区;操作系统由A盘引导启动.

图1显示了运行的结果.操作系统启动后,首先显示系统信息及版本信息,然后调用Shell命令,等待用户键盘输入.

在本例中,演示了如下命令:

df命令,列出了当前装入系统的文件设备;

pwd命令,列出当前工作目录;

ls命令,显示当前目录内容;

rm命令,删除hello文件;

gcc命令,编译链接C语言程序hello.c,生成可执行文件hello.

./hello,运行用GCC编译链接生成的可执行文件hello,显示运行结果:Hello,world!

图1 Linux 0.01改进版的运行演示

3 结束语

Linux 0.01虽然是Linux的早期版本,但具备了操作系统最重要最基本的组成部分;其规模较小,便于初学者及计算机本科专业学生学习研究.本文提出的方法实现了Linux 0.01版在Redhat 9.0平台下的编译链接,并在bochs虚拟机上运行成功,具有重要的实用价值.

参考文献:

[1]卢军.Linux 0.01内核分析与操作系统设计[M].北京:清华大学出版社,2005.

[2]Pramode C.E,Gopakumar C.E.The Linux Kernel 0.01 Commentary [EB/OL].http://ranger.uta.edu/~dliu/courses/os/kc.pdf,2008.

[3]赵炯.Linux内核完全注释[M].北京:机械工业出版社,2007.

猜你喜欢
镜像文件源代码命令
只听主人的命令
基于TXL的源代码插桩技术研究
新旧电脑资料迁移高效帮手
软件源代码非公知性司法鉴定方法探析
没光驱不要紧 装个免费虚拟的
移防命令下达后
基于语法和语义结合的源代码精确搜索方法
开发闲置内存,为本本轻松提速
用RamOS降低公用机的维护工作量
揭秘龙湖产品“源代码”