• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

Linux系统启动过程详解之SysV init(二)

系统管理 彭东稳 9年前 (2015-09-07) 24921次浏览 已收录 0个评论

初始化kernel

BIOS把路已经铺稳了,GRUB借着MBR的地儿也开始运行了,内核启动参数全都弄好了。接下来就该加载内核并初始化了。接下来就是看看内核初始化的过程了。

startup_32():内核自解压(内核是压缩的历史遗留问题)完毕之后将跳转到startup_32这个函数继续执行。这个函数式完全用汇编语言写的。内核源码中叫startup_32的函数有两个。第一个是在内核解压之前,调用内核自解压缩代码用的。而第二个是内核解压缩后用的,这两个函数都是汇编写的。而且都在名为head.s的文件中定义。当然也是两不同的文件。这个startup_32对于整个Linux系统来说意义是非同小可的,它代表Linux的第一个进程——process 0的开始。这里,决定了Linux的内存布局;这里,Linux了解了自己将要与什么样的CPU在一起,这里,会把由BIOS提供和bootloader们整理的资料放入内存的第一个分页里;这里,为以后要发生的中断请求做好准备;这里,如果是64位系统,将把长模式开启;这里,将跳往Linux内核的主函数——start_kernel,开启Linux内核真正的生命之旅。

start_kernel():每一个C语言写的程序,都有入口函数叫mainLinux内核也有这样的函数,叫start_kernel。它就静静地躺在内核源代码的init/main.c文件中。其实这个函数跟大家想象的不太一样,它跟具体的硬件关系不大。大部分时间都是再为Linux内核准备必要的数据结构。比如,用于内存管理的那些数据结构,用于任务调度的那些数据结构,用于处理多任务同步的数据结构,用于处理中断的数据结构,用于文件系统管理的数据结构等。反正经过对这些数据结构的初始化,Linux此时已经具备了一个操作系统应该具备的功能,各种子模块都可以良好运行。在这之后start_kernel会创建一个名为kernel_init的内核进程,然后进入自己的最终归宿———cpu_idle函数,进入无休止的循环当中。这个CPU_idle函数就是我们常见的CPU占用率已经是99%的进程。进程的pid0.只要你看到它对CPU的占用率还在80%以上,就说明你的系统还很闲。虽然这只是一个死循环,而且优先级很低,但是功能却很大。它会一直调用CPUidle指令,让CPU降温且省电。

kernel_init():如果是内核比较老就叫init内核线程。新的内核叫kernel_init,这是Linux系统的第二个进程,这也是init进程的pid一定是1的由来。kernel_init内核进程比较重要的工作就是让Linux内核开始与外面进行沟通。比如:给硬件加载驱动程序、初始化网络堆栈、在多CPU的系统中让其他CPU开始工作、寻找用户指定的根文件系统等。当一切都稳妥之后,kernel_init会调用用户空间的第一个进程—–init程序。这个时候,内核的初始化工作就完成了,Linux系统向用户控件开启了大门。

寻找init的问题

但是当要去运行/sbin/init程序完成系统初始化时。问题来了,init进程放在根文件系统中。如果Linux找不到根文件就没戏了。通常,可用的文件系统都会罗列在/etc/fstab文件中,所以mount命令可以找到它们。但是/etc/fstab既然是文件,就必须呆在文件系统中。这样,Linux找根文件系统的过程就成了鸡生蛋蛋生鸡的问题。要访问根文件系统必须要加载根文件系统所在的设备,而这时根文件系统又没有挂载,要挂载根文件系统又需要根文件系统所在设备的驱动程序。为解决这个问题,可以把驱动程序直接做进内核。但是操作系统是面向每一个人的。不过,这要是以前也可能还行,那时候也就是软盘或硬盘的差别。但现在技术百花齐放,有SCSISATARAIDSAS等,而且位置都开始不固定了,比如U盘,一个电脑上有好多USB孔,插哪儿只有谁用谁知道。有的还带加密的。这让Linux内核情何以堪啊?其实我们只用到其中的一种驱动就行了,但是需要在内核中预先安装上百种驱动。后来就把这些驱动都做成模块放在系统的/lib/modules下,也就产生了上面所说的需要到根文件系统中去找驱动程序。另外还有一个问题就是这年头不但技术多,山寨的也多,某些山寨为了表示自己不山寨,还会采用特别的技术手段,比如改改某些参数什么的,并美其名为自主创新。这样一来,Linux内核就更伤不起了啊!后来就出现了及时雨initrd,还内核一个清静,在initrd/initramfs中想怎么山寨怎么山寨。

initrd

initramfs也算是个新人在centos6.x系列才开始使用,而它还有一个同行老大哥initrdcentos5.x系列及之前系列中一直使用。

initramfsinitrd干的工作时一样的,只是机制不同罢了。initrd实际上就是在内存中开辟了一块模拟的磁盘空间。在Linux内核启动之后,先将这个模拟的磁盘当做根文件系统,并执行initrd当中的/linuxrc文件。linuxrc文件就是一个脚本程序,负责加载那些内存要访问的真正的根文件系统所必须的驱动和挂接真正的根文件、initrd对于Linux发行版和LiveCD是非常有好处的。比如作为发行版就要尽可能多的适应硬件环境。但是做一个超大的内核显然是会被人喷的。这个时候就可以请initrd来帮忙。在系统安装阶段,检测用户的硬件特性并找到用于作为根文件系统的设备驱动。并将这些放入initrd中。如果在安装过程中发现没有附带合适的驱动时,还能请求用户拿出设备驱动程序盘来安装。这样就基本不怕找不到init进程了。如果用户拿不出合适的驱动,还可以人性化地告诉用户;您的电脑不适合按Linux。“合计以人为本”是不能违反的真理!

initrd展开文件

initrd跟内核存放在同一个基本磁盘并一块加载到内存中去;其实initrd文件其实是一个虚拟的根文件系统,里面有binliblib64sysvaretcsysrootdevproctmp等根目录,它的功能就是把内核与真正的根建立联系,内核通过它加载根文件系统的驱动程序,然后以读写方式挂载根文件系统,以便于切换到真正的rootfs。然后执行/sbin/init程序。

initramfs

为什么要选择initramfs呢?它跟initrd干的活是一样的,原因就在于机制的不同。initrd是一个ramdiskramdisk必须是某种文件系统,比如ext2,。如果Linux要访问initrd,就必须带有相应的文件系统驱动。initramfs是一个使用gzip压缩的cpio打包文件。Linux内核会将它的内容装入一个tmpfs,这就不需要附带任何文件系统驱动就能工作。而且ramdisk大小必须固定,tmpfs却可以动态扩展。当然,这些都是一些小的技术差别。比较大的差别就是如何对待init进程这个问题。现在的Linux开发者们有了一个十分大胆的设想,就是尽早进入用户空间,尽最大努力把所有能在用户空间完成的工作都不用内核去做。使用initrd就必须在内核空间完成磁盘的挂接工作,这显然是可以在用户空间完成的。于是在继LVMUDev等之后,开始动起了initrd的注意。于是就有了initramfs这玩意了。

initramfs不再需要linuxrc这个文件了,取而代之的是直接启动init进程。initramfsbootloader准备好,告诉内核它在什么位置。内核知道之后kernel_init内核线程中也不挂接什么根文件系统,直接启动initramfs中的init进程。接下来去找根文件系统的活就都交给init进程。这下永远都不怕找不到init进程了。所以initramfs是个真正的及时雨,毕竟它比initrd要先走好几步呢!

initramfs展开文件

init程序

如果非要给Linux系统的初始化设定一个期限的话,我想是从开机到屏幕出现登陆界面为止。既然Linux认准了init,而且从名字上一眼就能看出它就是用于初始化的,而且它也的确这么做了。init进程会根据用户设定的一个叫运行级别的指标来对Linux系统进行一些力的初始化过程。比如:启动对应运行级别的服务进程、配置网络、继续为某些硬件加载驱动、运行相关软件等。

注意init是这个特定程序的名称,而能够实现init的程序或项目有很多。比如最古老的Sysv init(Centos5上使用)。以及后来ubuntu开发的Upstart(Centos6上使用)。如果使用过centos5和centos6就会发现他们之间的init的文件组织的差异,并且Upstart并不是很兼容Centos系统。所以后来又有一个程序员开发了systemd(Centos7上使用)。自由的世界是一个混乱的世界,但这混乱中却充满了竞争。init这个名字掩盖了那背后各种实现策略的博弈,也让我们有所从有所不从。至于谁能在这混乱的竞争中胜出,或永久消亡,只能让历史去做个见证。而Linux要执行init过程的这个机制就像真理一样,从来没有改变过。

RHEL5.0 init(SysV init)

1)SysV init介绍

SysV init是systemV风格的init系统,顾名思义,它源于SystemV系列UNIX。它提供了比BSD风格init系统更高的灵活性。是已经风行了几十年的UNIX init系统,一直被各类Linux发行版所采用。

SystemV,曾经也被称为AT&T SystemV,是Unix操作系统众多版本中的一支。它最初由AT&T开发,在1983年第一次发布。一共发行了4个SystemV的主要版本:版本1、2、3和4。SystemV Release4,或者称为SVR4,是最成功的版本,成为一些UNIX共同特性的源头,例如”SysV初始化脚本“(/etc/init.d),用来控制系统启动和关闭,SystemV Interface Definition(SVID)是一个SystemV如何工作的标准定义。

2)SysV init的运行级别

SysV的程序/sbin/init,其配置文件/etc/inittab说明,这个文件中的格式是很严谨的,任何一行都是按照id:runlevels:action:process 这样一个固定模式编写的,

SysV init用术语runlevel来定义”预订的运行模式”。SysV init检查’/etc/inittab’文件中是否含有’initdefault’项。来告诉init系统是否有一个默认运行模式。如果没有默认的运行模式,那么用户将进入系统控制台,手动决定进入何种运行模式。SysV init中运行模式描述了系统各种预订的运行模式。通常会有8种运行模式,即运行模式0到6和S或者s。每种Linux发行版对运行模式的定义都不太一样。但0-6却得到了大家的一致赞同:

下面解释一下《id:runlevels:action:process》格式:

/etc/inittab内容解释,这是centos5上使用的SysV版的init程序。

但是目前centos6上使用的upstart基本上很少碰inittab文件了。它使用了一套全新的配置脚本来实现与inittab文件差不多的功能。这些配置脚本在/etc/init/目录下可以找到。当你翻阅这些配置脚本的时候,会发现与单独的inittab没什么特别本质的区别。只能说采用配置脚本将inittab模块化了。

3)SysV init进入运行级别之前

SysV init巧妙地用脚本,文件命名规则和软链接来实现不同的runlevel。首先,SysV init需要读取/etc/inittab文件。分析这个文件的内容,它获得以下一些配置信息:

1、系统需要进入的runlevel;

2、捕获组合键的定义;

3、定义电源fail/restore脚本;

4、启动getty和虚拟控制台;

5、得到配置信息后,SysV init顺序地执行以下这些步骤,从而将系统初始化为预订的runlevelX:

/etc/rc.d/rc.sysinit

/etc/rc.d/rc和/etc/rc.d/rcX.d/(X代表运行级别0-6)

/etc/rc.d/rc.local

XDisplayManager(如果需要的话)

其实不管设定了何种运行级别,在init进程刚刚被启动的时候,Linux系统还是不健全的,光秃秃的只有内核。为了让Linux系统能够被人使用,也为了Linux能够恰当地运行于某一个运行级别智商,必须要进行一系列的初始化操作才行。这个初始化的过程通常会由一个脚本程序来完成。就是/etc/rc.d/rc.sysinit系统初始化脚本。其主要作用大概如下:

1.显示Welcome to redhat字体

2.激活udevselinux

3.根据/etc/sysctl.conf文件来设定内核参数

4.设定时钟

5. 装载键盘映射

6. 初始化外围硬件驱动

7. 启用交换分区

8. 设置主机名

9. 根文件系统检测,并以读写方式重新挂载

10.激活RAIDLVM设备

11.启用磁盘配额

12.根据/etc/fstab检查并挂载其它文件系统

13.清理过期的锁和PID文件

14.启动交换分区

15.清理/tmp//var目录

16.将开机信息写入/var/log/dmesg文件中

实际上rc.sysinit这样的脚本所执行的操作远不止这些,有很多发行版自己设计的特性操作。如果想看看自己的系统在这个过程中都执行了哪些操作,看一下/var/log/dmesg文件就好了。

4)SysV init进入运行级别

如果将进入运行级别之前所作的初始化认为是系统的通用初始化阶段,则进入运行级别时还要进行针对运行级别的个性初始化。

个性初始化的目的就是为了启动不同的软件集,从而让不同运行的运行级别能够有不同的处在表现。从inittab文件的例子中可以发现/etc/rc.d/rc这个脚本是做这个事情的。不同的运行级别会有不同的数字参数传递给它,这样可以保证它在不同的运行级别上可以有不同的行为表现。

既然是个性化,就应该允许用户可配置。但是如果用户想自己针对某个运行级别进行个性化配置的时候,就必须修改rc这个脚本才行。显然门槛有点高,通用性也很差。那么这个时候就需要有一种方法,既可以配置,又没有高门槛,还能很通用就是一个好方法。System V就提供了这样一个好方法。Linux的设计者们也就懒得去想了,直接将它的方法继承下来了。

这个方法是将个性初始化脚本在功能上进行了细粒度的划分,基本上一个程序对应一个初始化脚本,甚至一个程序会对应几个初始化脚本。将这些初始化脚本统一放到/etc/init.d/etc/rc.d/init.d目录下。然后准备一些形如/etc/rc[N].d/etc/rc.d/rc[N].d的目录,其中[N]与运行级别对应。在这些目录下建立到init.d目录下某些启动脚本的符号连接。建立了对哪些脚本的连接,就表明在进行个性初始化的时候要执行哪些连接。这样运行级别所需要启动的软件集也就被启动了。这些脚本谁去调用呢?交给rc这个脚本好了。让它根据给定的参数执行不同目录的所有脚本就行了。而且参数是数字的,正好可以和[N]对应,rc脚本很简单可以阅读一下。/etc/rc.d/rc脚本启动0-6级别时要启动的脚本根据级别的不同启动不同的目录/etc/rc.d/rc[N].d

如:/etc/rc.d/rc3.d目录下都是类似这种以KS开头的符号链接文件

这些符号链接文件都指向了/etc/rc.d/init.d/目录下的某个可执行文件,那么KS又是干嘛的呢?首先要明白一个简单道理就是:开机启动的程序,关机的时候得关闭。当然,这也不是绝对的。那么K就代表kill(杀死或停止)。S就代表start(启动)。所以通过名称就能看出哪些是开机的时候要执行的,哪些是关机的时候回执行的。细心的人会发现KS开头的符号链接实际上连接的是同一个脚本程序,那么后面的数字是干嘛的呢?其实就是程序启动或关闭时的先后顺序。因为程序之间有时候会有一些依赖关系。一般遵循先启动后关闭的理念来进行SK的值设定。

另外很多发行版中,在rc2.drc3.drc4.drc5.d这四个目录中都会发现S99local这样一个符号链接。都会链接到/etc/rc.d/rc.local文件。这个是专门为用户准备的一个设定开机自动启动程序的地方。

安装惯例,凡是能放入init.d目录的都应该是守护进程。而/etc/rc.d/init.d/*.sh脚本都会有相同的三行控制指令如下:

chkconfig345(默认启动级别) 11(S开头的启动优先次序) 88(K开头的关闭优先次序);一般都遵循先开启的后关闭因为先开启的可能会被其他程序依赖;而表示默认所有级别都是以K开头的。

description一般用于说明此脚本的简单功能。

系统上的这一类脚本就是靠这两行能够实现成为系统服务的;而chkconfig就是用来定义它能够接受另外一个命令的控制并自动在/etc/rc.d/rc0.drc1.drc2.d….下面创建链接而且是K开头或是S开头的文件;这些可以通过一个命令来实现就叫chkconfig,一般此类系统服务开启后就会在/var/lock/subsys/下创建一个同服务名称相同的名字而关闭此服务后就会删除此文件以此来判断此服务状态Status。

5)一个标准SysV风格的脚本格式

6)SysV init和系统关闭

SysV init不仅需要负责初始化系统,还需要负责关闭系统。在系统关闭时,为了保证数据的一致性,需要小心地按顺序进行结束和清理工作。比如应该先停止对文件系统有读写操作的服务,然后再umount文件系统。否则数据就会丢失。

这种顺序的控制这也是依靠/etc/rc.d/rcX.d/目录下所有脚本的命名规则来控制的,在该目录下所有以K开头的脚本都将在关闭系统时调用,字母K之后的数字定义了它们的执行顺序。这些脚本负责安全地停止服务或者其他的关闭工作。

7)SysV init的管理和控制功能

此外,在系统启动之后,管理员还需要对已经启动的进程进行管理和控制。SysV init软件包包含了一系列的控制启动,运行和关闭所有其他程序的工具。

不同的Linux发行版在这些SysV init的基本工具基础上又开发了一些辅助工具用来简化init系统的管理工作。比如RedHat的RHEL在SysV init的基础上开发了initscripts软件包,包含了大量的启动脚本(如rc.sysinit),还提供了service,chkconfig等命令行工具,甚至一套图形化界面来管理init系统。其他的Linux发行版也有各自的initscript或其他名字的init软件包来简化SysV init的管理。

只要理解了SysV init的机制,在一个最简的仅有SysV init的系统下,可以直接调用脚本启动和停止服务,手动创建inittab和创建软连接来完成这些任务。因此理解SysV init的基本原理和命令是最重要的。甚至也可以开发自己的一套管理工具。


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (1)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!