把玩 Minix

类别:编程语言 点击:0 评论:0 推荐:

    前些日子在 China-Pub 买了本《操作系统:设计与实现(第二版)》,然后就开始满世界的找 Minix(呵呵,我向来对底层机制比较感兴趣,想起来了就看看吧)。谁知道在网上一搜,发现了 Bochs 这个东西,不错啊!身材确实苗条,还是开源的:)它的官方网站上正好有 Minix 的硬盘 IMG 下载,一起给我拖回来。这个 Minix IMG 包的作者甚至还在包里带了配置文件(bochsrc.txt),省得自己配了:)(还是老外比较能办实事啊,这个不得不承认)

    我就效仿 Bochs 自带的 dlxlinux 建了个叫 Minix 的文件夹,然后把 dlxlinux 里面那个 Run.bat 拷过来修改如下(我的 Bochs 装在 C:\Program Files\Bochs 里面):
cd "C:\Program Files\Bochs\Minix"
..\bochs -q -f bochsrc.txt

    以后要用 Bochs 运行 Minix 双击这个批处理就搞定了——至此,算是解决了 Minix 的安装问题。其实虚拟机还有 vmWare 和 Virtual PC,而且在虚拟机和主机之间交换数据做得也比 Bochs 好,可是冲着 Bochs 的身材我算是喜欢上它了:)

好了,马上可以上路了!启动 Bochs 不久后可以看到:

Minix boot monitor 2.16 Press ESC to enter the monitor Hit a key as follows: = Start Minix

按等号后就可以看见登录画面了:

Multiuser startup in progress. Starting daemons: update cron. Minix Release 2 Version 0.3 noname login:

    主机名是 noname,登录后可以在 /etc/hostname.file 里面改。这里的登录名用 root,没有密码。登录之后就可以看到 shell 提示符“#”,不过只有两个控制台可用。找了半天好像没有办法把提示符设成当前工作目录(就算设了也不能即时刷新,这点用起来觉得不太方便,不过它毕竟是个教学系统,不是用来干活的,凑合一下吧:)

    大概转了转,基本的命令都有,不过也不可以 alias,以前在 Linux 和 FreeBSD 里面都习惯来个“alias dir 'ls -al'”,这么一来还真觉得不方便:)看了一下手册,Minix 的 shell 好像叫 ash(以前没听说过),看来功能确实是简单啊。vi 也有,其实是 elvis(跟 vi 兼容又没版权问题的工具),居然还可以读配置文件,就是 home 目录下的 .exrc 文件(这个 IMG 把 root 的 home 目录设在了 /,当然你也可以改 /etc/passwd)。试用的感觉好像比 Solaris 下面带的那个 vi 还好用一些:)
set ts=4
set sw=4
这两个选项也可以用,就是不支持语法高亮(开个玩笑:))。

    最爽的是这个 IMG 里面还带了 cc 和所有的源代码,这样一来我们有什么不满就可以自己动手丰衣足食了,不错,不错!看来这个 Bochs + Minix 2.0 IMG 还真是个学习操作系统原理和 Minix 的好东西!所有的源码就放在 /usr/src 下。

看着手痒,就先小试一把吧,ls 列目录的时候没有颜色,就拿这个改改吧:)什么?颜色怎么出来?给你看看下面的代码吧:

#include <stdio.h> int main( void ) { printf( "\033[31m2ndBoy\033[0m\n" ); return( 0 ); }

假设程序保存为 color.c,编译:
# cc -o color color.c
运行结果如下:
# ./color
2ndBoy

怎么样?看到彩色了吧!我们只要把格式串里面的 31 换成别的值就可以看到其它颜色了,其它部分只要照搬就可以了。下面来看看我们要修改的对象:
# cd /usr/src
# find . -name ls.c
./commands/simple/ls.c

ls.c 是个 1134 行的程序,大致的看了一下源代码,发现一个叫 printname() 的函数,在没有详细分析过代码的前提下它看起来比较象是我们要找的对象,那就先试试看,我们参照上面的代码来修改一下这个函数:

void printname( char *name ) { int c, q = present( 'q' ); printf( "\033[31m" ); while( ( c = (unsigned char)*name++ ) != 0 ){ if( q && ( c <= ' ' || c == 0177 ) ) c = '?'; putchar( c ); } printf( "\033[0m" ); /* 恢复默认颜色,免得影响后续内容 */ }

红色代码是我加进去的,存盘退出,编译:
# make ls
cc -i -D_MINIX -D_POSIX_SOURCE -o ls ls.c
install -S 20kw ls
# ./ls

果然,所有列出的文件名全变成了红色,看来是找对门儿了!我们继续修改。

又看了看,程序中是用一个 struct file (ls.c:248)来存放每一个文件的文件信息的,而这些信息又是通过调用 stat() 得来的(注意程序中的 status()),而这正是我们需要的!由于 struct stat 结构中的 st_mode 中存放了文件的文件类型,所以我们可以通过解读这个成员来用不同的颜色显示不同类型的文件。但是 printname() 只有一个参数,看来我们要给它再加一个参数了,改成这样:
void printname( char *name, struct file *f )
当然调用的地方也要做相应的修改,一共有两处,改为:
printname( xxxx, f );

这样一来我们就有了文件类型和文件名这两个东西了,代码修改如下:

void printname( char *name, struct file *f ) { int c, q = present( 'q' ); if( S_ISDIR( f->mode ) ) printf( "\033[1;34m" ); /* 目录用亮蓝色 */ else if( f->mode & S_IXUSR || f->mode & S_IXGRP || f->mode & S_IXOTH ) printf( "\033[1;32m" ); /* 可执行文件用亮绿色 */ else if( f->mode & S_IFCHR || f->mode & S_IFBLK ) printf( "\033[1;33m" ); /* 字符和块设备用黄色 */ #ifdef S_IFLNK else if( f->mode & S_IFLNK ) printf( "\033[1;36m" ); /* 符号链接用青色 */ #endif while( ( c = (unsigned char)*name++ ) != 0 ){ if( q && ( c <= ' ' || c == 0177 ) ) c = '?'; putchar( c ); } printf( "\033[0m" ); }

编译完之后我们用修改过的 ls 将原来的 ls 覆盖,方便以后正常:
# cp ls /usr/bin
覆盖之后试试有无效果:
# ls -al /

    果然,目录用亮蓝色显示出来了。不过在不加 -l 参数时显示出的文件名却没有颜色,看来我们在没有详细分析过程序的前提下改出的代码还有有问题的:)想了想:要取得文件类型肯定要调用 stat(),顺着这个思路搜了一下,果然,程序里调用 stat() 的地方必须要求 field 不为 0(也就是设置了某个参数)。所以我们换个思路,让 ls 认为我们所有对它的调用都加了参数,field 所有的取值在程序中都定义了宏,从 0x0001 到 0x2000,那我们就用它还没有霸占的 0x4000:)在 main() 中做如下修改(红色代码是我加上的):
if( field & L_LONG ) field &= ~L_EXTRA;
field |= 0x4000;

这样一来就可以了,我们的 ls 算是修改成功!!!

    上面说到了,Minix 的 shell 不能把提示符设置成即时显示当前目录,那我们再来改改:)搜了一下发现在 /usr/src/commands/ 下面有 sh 和 ash 这两个目录,编译后试了一下,sh 不支持 Tab 键命令补全,那么我们用的这个 shell 一定是 ash 了。在 /usr/src/commands/ash/ 下一番 grep 后确定了目标:parser.c。看起来在显示提示符之前都要用 ps1val() 来获得 PS1 的值,那我们改写这个 ps1val() 提前做个处理不就行了?!打开程序一看,原来这个 ps1val() 是个宏,它在 var.h 里面定义成了 #define ps1val() (vps1.text + 4),而 vps1 里面存放的是 PS1=xxx 的形式,所以 +4 之后直接得到了它的显示内容。要做手脚就不太可能继续用宏,我们把这个宏注视掉,把它改成函数声明:
char *ps1val( void );

现在打开 parser.c,按 G 跳到文件尾加入如下代码:

#include <pwd.h> char * ps1val( void ) { static char szPS1[128]; char *p = vps1.text + 4; int nPos = 0; memset( szPS1, 0, sizeof( szPS1 ) ); while( *p != '\0' ) { if( *p != '\\' ) /* 不是转义字符就原样输出 */ { szPS1[nPos] = *p; nPos++; } else { p++; if( *p == 'w' ) /* 取当前工作路径 */ { getcwd( szPS1 + nPos, sizeof( szPS1 ) - nPos ); } else if( *p == 'u' ) /* 取用户登录名 */ { uid_t uid = getuid(); struct passwd *pPasswd = getpwuid( uid ); strcat( szPS1 + nPos, pPasswd->pw_name ); } nPos = strlen( szPS1 ); } p++; } szPS1[nPos] = '\0'; return( szPS1 ); }

这里我模仿了 Bash 的做法,你可以用转义符来代替一些东西,比如这里用 \u 代表当前用户名,\w 代表当前工作目录。存盘后编译:
# make

这里 make 时间要长一些,好了之后让我们来试一下:
# ./sh
# PS1="[\u \w]# "
[root /usr/src/commands/ash]#

哈哈,大功告成,双儿,来……

把编译好的 sh 复制到 /bin 下,以后我们进入系统就用它了,记得改一下 .profile 设置好 PS1,这下 Minix 的环境好用多了!!!

    Minix 是专门设计用来学习操作系统原理的,我在这里改改 shell 跟 ls 根本没有接触到 OS 理论的东西,呵呵,不过用这两个东西开个头倒也不错。

本文地址:http://com.8s8s.com/it/it29038.htm