phyma 发表于 2005-1-11 17:01:12

Grub 源代码分析

这个主题源自westroom兄的意见。

我是第一次写这种“标题庄重”的技术文章,感觉好怕怕的,大家一起来写这个吧!
版本就用0.93的吧,我手头正好有这个版本。

其实总体上我们可以把grub看成一个mini os,他有shell,支持script,有文件系统……
我们可以把stage1 stage1-5看成一个boot loader,而stage2则是一个os,只不过这个os是专门load其他os的os,为此,stage2支持像kernel,initrd,chainloader等等为此目的而设置的内部“命令”……

(自己看书的时候常常跳过前言,觉得很多书的前言绪论实在让人厌烦,等看到正文的时候已经头晕目眩了,所以写这么多的介绍性文字已经是我的极限了,权当作抛砖引玉的作用,后面会陆续写些详细的分析文章,大家有兴趣可以一起写写)

phyma 发表于 2005-1-11 17:39:03

1.简略流程
(如果你不熟悉grub,建议您先看完这这一节再向后)
a,Bios执行int 0x19,加载MBR至0x7c00并跳转执行。
   如果你安装grub到mbr,grub的安装程序会把stage1(512B)拷贝到mbr。
   视stage2的大小,安装程序会在stage1中嵌入stage1-5或者stage2的磁盘位置信息。
b,stage1开始执行,它在进行直接加载stage1-5或者stage2并跳转执行(还有些额外的工作,这里忽略先)
   stage1-5很无辜,除了加载stage2以外它没有其他任何作用。
   所以,不论是哪种情况,这一步的结果都是stage2开始运行了。
c,stage2这个mini os终于开始正式运行了!它会把系统切入保护模式,设置好c运行环境(主要是bss)
   他会找config文件先(就是我们的menulist),如果没有的话就执行一个shell,等待我们输入命令。
   然后grub的工作就是输入命令-解析命令-执行命令的循环,当然stage2本身也很无辜,他是为加载其他os
    而存在的,所以如果情况允许,在他执行boot命令以后就会把控制权转交出去!


(已经尽量控制篇幅,还是超过预算,作为骨架性的一节,这应该是要常驻读者的“内存”的,所以还是显得太多了)

officerlinux 发表于 2005-1-11 22:43:05

版主可以将此版本的Grub 源代码给放上来吗?或者给出下载的连接好吗
关注。

phyma 发表于 2005-1-12 10:43:19

grub0.93

phyma 发表于 2005-1-12 10:57:13

grub0.93

westroom 发表于 2005-1-12 11:21:12

顶一个先哈!

phyma 发表于 2005-1-12 11:48:53

2 Stage2-文件系统

a,调用接口

类似系统调用,在stage2/disk_io.c中定义了grub_open,grub_close,grub_read,grub_dir
全局函数用于stage2的文件操作:打开,关闭,读,切换目录。

为了简化文件系统驱动的编写,grub不支持磁盘写(对于一个loader来说也没有必要去写磁盘)


b,文件系统驱动接口

任何一个文件系统驱动必须在fsys_table(stage2/disk_io.c)数组中去放置一个
struct fsys_entry的结构体,定义如下(stage2/filesys.h):
struct fsys_entry
{
char *name;//文件系统名称
int (*mount_func) (void);//初始化函数
int (*read_func) (char *buf, int len);//文件读函数
int (*dir_func) (char *dirname);//目录/文件打开函数
void (*close_func) (void);//文件关闭函数
int (*embed_func) (int *start_sector, int needed_sectors);//?大部分驱动设置此项为NULL
};


c,情景:读入文件

假设在grub的Menulist中有:kernel (hd0,0)/boot/vmlinuz(或者我们在grub shell中执行此命令)

stage2会先调用grub_open("(hd0,0)/boot/vmlinuz")来打开文件。
在执行这个函数中,grub会先在fsys_table中循环调用fsys_entry::mount_func去发现一个返回值为真的文件系统,即为当前的文件系统。然后利用当前文件系统驱动的fsys_entry::dir_func去打开/boot/vmlinuz
然后stage2会调用grub_read(buf,0)读入全部的/boot/vmlinuz文件至内存中的buf地址
grub_read是fsys_entry::read_func的封装。
最后stage2回调用grub_close关闭文件,跟前面一样,这个调用仅仅是当前文件系统fsys_entry::close_func的简单封装。

d,文件系统驱动

你可以在stage2/fsys_*.c找到各个文件系统驱动,当然你如果 对某个文件的结构熟悉,而grub中又没有相应的驱动,你可以安装b节中方法向grub中添加此文件系统的支持,比如NTFS,你只要实现mount(判断是否是你所支持的文件系统),dir(打开文件或者目录),read(读),close(关闭文件)功能就好。

redjing 发表于 2005-1-12 19:05:40

顶一个先,希望能够继续下去

phyma 发表于 2005-1-13 11:52:31

3 Stage2-Shell

大部分情况我们是通过一个menulist文件来控制grub,这样grub会分析此文件的内容,然后显示一个菜单让我们选择。但是我不打算介绍grub stage2时怎样显示这个菜单的,这不是grub特殊的地方,这一节将对菜单选择执行的底层机制做小小的分析。(在grub没有找到menulist的时候,他就会执行一个shell,你完全可以通过这里内置的命令来控制grub,实际上你可以把menulist当作一个脚本文件,它也完全是通过内置命令执行的)

a,stage2流程

stage1或者stage1-5加载完stage2后,会跳转至stage2执行,stage2的入口是stage2/asm.S
asm.S在设置好c运行环境之后,会调用第一个c函数init_bios_info(stage2/common.c),这个函数在执行一些底层的初始化之后,会调用stage2的main函数cmain(stage2/stage2.c),这样stage2这个 mini os正式开始运行了!

针对menu.lst和shell这两种情况,cmain将:
menu.lst: run_menu()(stage2.c)->run_script()(cmdline.c)->find_command->执行命令函数
shell: enter_cmdline()(cmdline.c)->find_command->执行命令函数

殊途同归,最后都归结为命令行的解释执行
find_command(stage2/cmdline.c)按照menu.lst中或者shell用户输入的命令字符串,在一个全局性struct builtin *builtin_table[](stage2/builtin.c)变量中去找到内置命令的函数,然后执行。

值得一提的是grub的shell类似bash的命令补全和命令历史纪录。



b,内部数据结构
struct builtin
{
/* 命令名称,重要,是搜索命令时的依据*/
char *name;
/* 命令函数,重要,是搜索匹配后调用的函数*/
int (*func) (char *, int);
/* 功能标示,一般未用到.*/
int flags;
/* 简短帮助信息*/
char *short_doc;
/* 完整帮助信息 */
char *long_doc;
};
struct builtin *builtin_table[];(stage2/builtin.c)



c,Hack 提示

编写一个自己的grub命令。只需按照b节所述,填充一个struct builtin结构,按后将其指针放入builtin_table指针数组,你就可以在menu.lst或者grub shell中使用这个命令了。

redspider 发表于 2005-1-13 12:02:35

顶,:thank
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: Grub 源代码分析