您的位置:首页 > 运维架构 > Linux

Linux原来也就这么回事

2008-06-14 18:14 267 查看
#vp_maindiv{
font-family: Arial, Helvetica, sans-serif;
font-size: 10pt;
line-height: 1.5em;
/*background-color:#C8E0D8;*/
background-color:#C2D8FC;
border-style:dashed;
border-width:1px;
border-color:#B7B7B7;
}
#vp_maindiv dl,ul{
font-size: 10pt;
}

在博客里早就转载了 手把手教你如何建立自己的Linux系统(LFS速成手册)但实际上并没有耐心和时间把LFS完成。最近终于有机会和时间再次进行LFS之旅。用VMWare虚拟机按步骤进行了差不多一步不差的实践(呵呵,还是有很多的测试没有进行),除了参考手把手教你如何建立自己的Linux系统(LFS速成手册)外,还参考了最新英文原版的LFS,主要通过它能明白各个命令的具体含义,速成中并没有给出太详细的解释。
时断时续大约花了两天的时间终于完成了整个LFS的编译,但还是留下了些许遗憾:GRUB怎么安装都有问题,不能自动加载编译好的LFS,只能手工输入命令加载。但无论如何还是非常高兴的,:-)。
回头来看LFS的制作过程,明显的进步就是对Linux的命令明显的熟练,参数也理解的更加深入了。对Linux的内部原理也有了比以前更清晰的理解,有一种茅塞顿开的感觉。下面说说自己现在对Linux的理解(比以前进步,但可能依旧有不正确,一直在进步^_^)。
1、Linux整个操作系统的构成
Linux作为操作系统,内容相当的多,但从外部表现来讲我觉得也就两层:核心层、应用层。核心层主要包括进程管理、存储管理和IO管理,IO管理其实就是各种驱动,我把文件管理也归到IO管理。
核心层具体到文件主要是两类:内核(镜像)文件和驱动程序文件。内核文件包含进程管理等操作系统基本模块,也可以把部分驱动程序编译进来,如文件驱动。内核文件在操作系统运行时全部加载到内存。驱动程序文件是针对于特定IO设备的驱动模块,可以编译成单独的内核模块也可以编译进内核文件,作为单独的模块可以由内核在检测到设备时自动加载或手动加载(modprobe)。极端情况下内核层只有一个较大的内核文件而没有任何其他驱动文件,这在嵌入式系统中应用广泛,因为嵌入式系统的硬件系统固定,硬件模块需要的驱动也固定,一般是系统运行时就全部加载到内存,提高系统的运行速度。
核心层运行在核心态(ring0)。应用层程序运行在ring3,不能直接运行核心态的代码,只能通过CPU的调用门等机制同核心态通信。因此核心层一般以库文件的函数调用API同应用层进行通信,应用层程序只需调用API即可而不用担心具体的调用细节,细节都被封装在API函数实现中。
应用层主要是各种应用程序,其中包括特殊的Init和Shell。Init是系统的第一个应用程序,Shell只是一个特殊的用于解释和调用其他程序的应用程序。他们都是调用库函数来完成自己的任务,和其他我们自己写的程序没有什么本质差别。
LFS在编译的过程中就是从现有的宿主操作系统,逐渐的生成目标系统的工具链(编译器等),去掉对现有系统的依赖性并逐渐让系统适应新的系统,这和交叉编译相似。LFS大部分的时间都在调整工具链,然后编译各种使用工具(包括库),这些工具都是应用层的;而在LFS的最后才进行了内核的编译。所以回过头来感觉好像没必要花那么多时间去编译那些应用程序,但没有那些应用程序,内核还是没有办法纯净的编译出来。
2、Linux的引导
在准备好内核文件、应用程序后,重要的是把这些文件“激活”,让硬件按照新的内核文件来运行,这就是系统的引导部分。这可以通过Lilo、GRUB等引导器完成,具体的过程就略了,可以参考手把手教你如何建立自己的Linux系统(LFS速成手册)后面GRUB安装部分。
3、小结
通过上面的分析就可以看出来,Linux就是由内核文件、应用程序组成,然后由引导程序将内核文件“激活”的过程。以前总搞不清楚Linux安装下来那么多的空间究竟都用来干什么了,分不清哪些文件是必须的哪些可以省略。通过LFS总算明白了,最简单的Linux系统其实只需要一个内核文件就可以运行了(宏内核的好处:-)),但可能什么任务也完成不了,而其他那么多文件是为了具体应用的,可以根据应用来定制了。
但是说着简单,真要发挥Linux强大的功能,应用的复杂性还是必要的。
4、Linux裁减
既然一个内核文件加上必要的库文件、必要的程序就可以满足我们的要求,我们可以尝试把现有的巨大的Linux操作系统裁减成非常小的系统。
4.1创建root文件系统
组建root文件系统更简单:第一步,格式化容纳root文件系统的宿主设备(按照所需的文件系统格式来格式化设备);第二步,拷贝所需文件到root文件系统中。
Linux文件系统的详细结构可以在网上查到,这里就不多讲了。我们这里着手搭建的root文件系统,不必包含太多内容,仅仅提供最基本的目录结构、系统命令就可以了;并且配置文件也应该尽量修改得简洁明了。
root文件系统必须包含的内容如下:

包含/dev 、/proc、/bin 、/sbin 、/etc 、/lib 、/usr 、/root 等目录。

包含一组基本命令,如ls这样的文件管理命令,insmod这样的系统管理命令。

支持上述命令的运行库函数和系统登陆认证的相关库函数。

包括编译内核生成的模块。

必须的设备文件。

一些必要的配置文件和服务管理脚本。

4.2创建root文件系统的内容
第一步:挂载目的主机硬盘到宿主机系统,我们假设我没将在第二块硬盘上安装。mount /dev/hdb1 /rootfs 第二步:在rootfs目录下建立文件系统根目录下必需的子目录: #cd rootfs #mkdir dev, proc, bin, etc, lib, usr,sbin, root, usr/bin, usr/sbin, usr/lib
第三步:向rootfs中拷贝内核文件和你需要的命令,比如ls 命令。

内核文件一般是/boot/kernel-2.6.x,我们只需将/boot下的所有文件(map文件、镜像文件、initd文件等)拷贝到目标目录的/boot下即可。

对于应用程序或命令,我们要先确定它在系统中的位置(使用命令whereis ls,发现它在/bin/ls目录下),然后将该命令拷贝到rootfs下相同的目录结构下(cp /bin/ls /rootfs/bin/ls)。仅仅拷贝命令文件并不够,还必须拷贝该命令所用到的动态链接库。如何查找命令用到了那些动态链接库呢?很简单,利用命令ldd /bin/ls可以完成这种工作,如下所输出:

libtermcap.so.2 => /lib/libtermcap.so.2 (0x4001f000) libacl.so.1 => /lib/libacl.so.1 (0x40023000) libc.so.6 => /lib/libc.so.6 (0x40029000) libattr.so.1 => /lib/libattr.so.1 (0x40149000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
名字中有.so的那些文件就是用到的动态链接库。找到这些动态链接库后,就将它们拷贝到rootfs下与原来位置对应的地方,一般应该在rootfs目录下的lib或lib/i386中。(用户所用到的命令多集中在/bin和/sbin下;此外,一些脚本会用到一些出现在/usr/bin和/usr/sbin下的命令和工具。如果要使用这些脚本,这些命令工具和它们要用的库一个都不能少)。

但是先别忙,这些文件名可并非我们实际想要的,它们只是实际库文件的一个符号链接而已,系统之所以要使用符号链接,是为了便于库文件升级换代过程中不直接影响到使用它的应用程序(应用程序中用到的库路径指向的是库的连接文件,连接文件名称轻易不会改变)。因此我们如果单拷贝符号链接是没有意义,必须将符号链接指向的实际库文件一同拷贝到workdir/lib录下面去。

/lib目录中还有一个重要组成部分,modules目录。它里面包含了内核编译产生的模块,不同版本内核对应的模块存放在以版本号命名的文件中。可别忘了拷贝我们新内核模块到我们的rootfs/lib/modules下。

在很久很久以前,拥有/lib下的这些库就足够用了,但现在的Linux系统相比过去多了许多新要求,尤其是系统从网络安全性考虑,增加了许多验证手段,因此你还必须拥有这些与安全验证相关的附加库。这些库虽然不会在命令中直接使用,但却间接地要被系统的安全机制调用。多数情况下,安全机制使用的具体库会在配置文件中定义,安全框架通过查看配置文件,选择调用具体是何种验证库。安全框架方面的话题,就不多介绍了,有兴趣的话可以查查 pam 和 nss等的用法, 它们都是系统登陆时必须使用的与安全相关的库。在这里,我们就不管它三七二十一,将所有在/lib/security/下和pam相关的库,以及在/lib下与nss相关的所有libnss*库,都拷贝到rootfs/lib下的/security和/lib中(如果没有这些验证库,你就无法登陆,系统不断重复地给你登陆提示,但却不给你输入密码的机会)。方法很笨,但却比较省事。

在Linux系统中,那些会被应用程序频繁使用的库函数,多数都不会以静态方式编译连接到应用程序中,而通常会采取动态链接的方式,进行集中存储管理。这样如果多个程序都用到某个共享库,那么该库文件只会被调入内存一次,驻留在内存供所有应用程序调用。因此利用共享库能显著地节约内存空间,缩减执行文件的体积。当然,天下没有免费的午餐,虽然共享库相比静态库来说灵活得多,但却需要进行路径搜索,而且调入时也更耗时。

链接文件是Unix风格操作系统提供的一个特色之一,其中具体又可分为软链接和硬链接。软链接又称为符号链接,这种文件唯一的内容就是包含实际文件系统的路径,因此,即使实际文件已经被删除或转移,符号链接仍然存在,当然,它不再有效了。相反,硬链接则是和被链接文件共享索引节点的,删除硬链对应的文件则会使索引节点计数减少,而不会破坏硬链接文件。

第四步:该建立设备文件了,这点很重要,但相当容易。Linux继承了Unix的一个优良特性,将设备抽象成特殊文件来使用和管理,想使用系统的外设,比如软硬盘,时钟,系统终端甚至内存,都可以通过设备文件来进行访问。因此我们要建立系统可能会用到的所有设备对应的设备文件。至于你具体需要哪些设备文件不能一概而论,你可以打开/dev/目录看看,当然,里面的文件肯定会多得让你眼花缭乱。

不过也别怕,因为多数都没什么用处。对于我们要建立的实验系统来说,用到的设备文件就屈指可数:

console ——控制台设备; tty* ——由控制台管理的虚拟终端(我们用ctrl-[1-7]切换的就是这个设备); sda1 ——SCSI接口设备; ram ——内存虚拟盘设备; null ——空设备(是一个非常有用的字符设备文件,送入这个设备的所有东西都被忽略。如果将任何程序的输出结果重定向到/dev/null,则看不到任何输出信息,因此脚本中常用它来消除本该显示在屏幕上的信息); zero——零设备 (读取这个设备,只会得到空的内容,所以有时为了获得高压缩率,需要对某空间用全零添充时往往就会用到它); initrd ——这是一个特殊的字符设备,它被用来从用户空间向系统内核发送切换运行级别的信息,属于一个虚拟字符设备(比如改变运行级别的init 1-6命令,都是通过该设备传递给内核的)。 必须的设备文件,大概就是这么多了,明确了你需要所有设备文件,就可以利用mknod命令挨个建立这些需要的设备文件。如果采取这种方式,那么注意mknod是需要参数的。你可以通过命令ls ??Cla /dev/设备名来查看设备是属于块设备还是字符设备,查看设备的主从设备号。如果你觉得这样太烦,就用拷贝命令直接从标准系统的/dev/目录下拷贝这些文件吧,不过要配合参数-R使用,否则,你拷过来的就是设备的所有文件,即设备中的整个内容,而不仅仅是一个设备文件了。拷贝设备内容就如同把自己往自己衣服口袋里塞,你是永远塞不进去的。

Linux系统将设备分为块设备和字符设备,块设备可以随机访问(用符号B描述),字符设备只能按顺序访问(用符号C描述)。另外,一个驱动程序可能会控制多个设备,所以有主设备号和从设备号之分。主设备号对应驱动程序,从设备号用来区分具体设备。

第五步:该建立系统运行需要的配置文件和脚本了。我们还是从简出发,拷贝标准系统现有的文件,然后针对需要进行修改。下面是具体需要的配置文件和脚本文件。

我们的试验系统必须满足多任务、多用户的需求,因此要有用户登陆和用户分组的能力,所以我们需要首先要拷贝/etc下的passwd和group这两个文件,如果系统通过shadow保护用户密码,那么shadow文件也要随同拷过来。

系统启动后最先执行的就是init程序,它需要的配置文件可不算少。首当其冲的便是inittab文件,它规定了系统运行的许多基本属性。接下来init程序会去执行inittab中引用的rc.sysinit脚本,进行系统引导后期的初始化工作,其中又会使用到fstab配置文件,它包含了系统启动后需要安装的文件系统以及安装到的目录位置;对于我们的系统来说只有两项:一个是将/dev/ram作为root文件系统安装到/下,另一个是将proc文件系统安装到/proc目录下,这可以根据你的需要自己进行调整。

init执行完rc.sysinit后,依照inittab中定义的运行级别进入对应的/etc/rc?.d/(1就是/etc/rc1.d/,当然,最常见的是3级,/etc/rc3.d/)目录下执行其中S开头的系统服务脚本。这里面的细节就不多说了,可以使用man init获得相关资料。现在要做的就是把/etc/下initab,rc.sysinit和rc.d目录的所有东西拷贝到新root文件系统的对应位置上。

我们假设系统的运行级别为3,只需要启动网络服务,因此可以把除了S?Network和S?Local外的S脚本都统统删除。(当然你也可以改变系统默认的启动流程,让它执行你自己定义的初始化脚本,要做的只是去inittab中修改“sysinit:XXX”中的脚本名称)。

执行了上述初始化,启动了相关服务后,系统会执行rc.local文件,这里可以放一些你自己希望在开机时执行的命令,我们这里放一句“ ok you are welcome !!!”作为你进入系统前的问候(由于我们系统初始化工作没有什么太多操作,所以启动过程很快,系统中的虚拟终端设备(tty?)往往还来不及初始化完全,所以可能出现系统起来后提示(”Id xxx respawning too fast:disabled for 5 minutes”)一类的话 ,为了避免这个错误,我们在local中还让系统睡眠了20秒)。

另外,登陆程序login往往要使用pam验证模块认证用户合法性,所以pam的配置文件也要拷贝到新系统。很多系统还会用到NSS(名称服务开关,在前面已经提到它要用到的库文件。这个服务来帮助客户机器或应用程序获得网络信息,可从本地或从网络某处取得——从DNS或NIS等。诸如getXbyY()等函数都往往会用到这种服务,用户登陆时login很可能就要使用,这取决于你的libc的版本),所以nss的配置文件/etc/nsswitch.conf也需要拷贝,至于该脚本的内容等细节问题,可通过man nsswitch.conf来了解。

你还要拷贝terminfo/termcap(新旧版本有别)文件,设置TERM终端环境变量要用到该文件。

最后别忘记拷贝模块配置文件modules.conf,它包含了模块相关的信息。

把/root/目录下的那些.开头的用于bash配置的隐藏文件也拷到新系统的root目录下,这些都是bash的环境参数(如果没有,关系也不大,就是不大方便而已)。

最后:ldconfig ??Cr rootfs(目标文件系统目录) 建立库文件路径缓存 ,从此我们新root文件系统上的命令再使用动态链接库时就不必指定库的目录了,因为它们的路径都被缓存了。(ldconfig 要用到动态库配置文件ld.so.conf,它其中指定了需要用的库路径。比如你的程序用到kerberos库,那么就需要在ld.so.conf里面包含/usr/lkerberos/lib路径)。

4.3系统引导

假设用GRUB进行系统引导,可以依据手把手教你如何建立自己的Linux系统(LFS速成手册)最后GRUB引导部分进行。

grub

输入root (hd1,1) 输入setup (hd1) quit
设置grub启动菜单

代码: cat > /boot/grub/menu.lst << "EOF" # Begin /boot/grub/menu.lst # By default boot the first menu entry. default 0 # Allow 30 seconds before booting the default. timeout 30 # Use prettier colors. color green/black light-green/black # The first entry is for LFS. title LFS 6.3 root (hd0,1) kernel /boot/lfskernel-2.6.22.5 root=/dev/hda2 EOF 注意:这里root后面的磁盘分区需要根据实际情况调整。 将menu.lst连接到/etc目录下 代码: mkdir -v /etc/grub ln -sv /boot/grub/menu.lst /etc/grub 到此重启系统,应该可以从新配置的内核引导,并且是我们刚刚够用的系统,:-)
[b]5、总结
用自己摸索的观点解释了Linux的系统本质构成,并给出了从现有系统裁减出最小系统的途径和方法。为什么用摸索呢?因为很多书上都用大量的篇幅在讲Linux内核的特点、结构、源代码等等,但没有用宏观的角度展示Linux“系统”本身宏内核的特点,没有强调让人晕倒的so多的C源代码最后只生成了一个内核文件,我们学了半天内核的源代码都没有搞清楚生成的二进制代码到底在什么地方,到底是如何被系统使用的,这些只有通过实践才能真正体会到。当我们从直观上清楚了内核的地位和表现,再去学习、修改源代码才能更加有的放矢!
6、参考文献
手把手教你如何建立自己的Linux系统(LFS速成手册) 安得倚天抽宝剑——搭建实验系统 手把手教你如何建立自己的Linux系统 第二版

感谢为我们美好网络生活无私奉献的人们! [/b]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: