什么是Init(initial:原始,初始,开始)系统,init系统的历史和现状
Linux操作系统的启动首先从BIOS
开始,接下来进入boot loader
,由boot loader
载入系统内核,进行内核的初始化。内核初始化的最后一步就是启动pid
为1 的init
进程。这个进程是系统的第一个进程,它负责产生其它所有用户进程。
init
进程以守护进程方式存在,是所有其它进程的祖先,init
进程非常独特,能够完成其它进程无法完成的任务。Init
系统能够定义、管理和控制init
进程的行为,它负载组织和运行许多独立的或相关的初始化工作(因此被称为init系统),从而让计算机系统进入某种用户预定的运行模式。
仅仅将内核运行起来是毫无实际用途的,必须由init系统将系统带入可以操作状态。比如启动壳shell之后,便有了人机交互的可能,这样就可以让计算机执行一些预定程序完成有实际意义的任务,或者启动X图形系统以便提供更佳的人机界面,更加高效的完成任务,这里字符界面的shell 或者 X系统都是一种预设的运行模式。
大多数Linux
发行版的init
系统是和System V
相兼容的,被称为sysvinit
。这是人们最熟悉的init系统。一些发行版如Slackware
采用的是BSD
风格的Init
系统,这种风格使用较少,本文不涉及,其它的发行版如果Gentoo是自己定制的。Ubuntu
和 RHEL
采用upstart
替代了传统的sysvinit
。而Fedora
从版本15开始使用了一个被称为systemd
的新init
系统.
可以看到不同的发行版采用了不同的init实现,本系列文章打算讲述三个主要的Init系统: sysvinit、UpStart、systemd。了解他们各自的涉及特定,并简要介绍它们的使用。
Sysvinit 概况
sysvinit 就是system V风格的init系统,顾名思义,它源于System V系列UNIX。它提供了比BSD风格init系统更搞高的灵活性,是已经风行了几十年的UNIX init系统,一直被各类Linux发行版所采用。
运行级别
Sysvinit 用术语 runlevel 来定义“预定的运行模式”。Sysvinit 检查/etc/inittab
文件中是否含有initdefault
项。这告诉init系统是否有一个默认运行模式。如果没有默认的运行模式,那么用户将进入系统控制台,手动决定进入何种运行模式。sysvinit中运行模式描述了系统各种预定的运行模式,通常会有8中运行模式,即运行模式0到6和S或者 s。
每种Linux发行版对运行模式的定义都不太一样,但是 0,1,6却得到了大家的一直赞同:
- 0 关机
- 1 单用户模式
- 6 重启
通常在/etc/inittab文件中定义了各种运行模式的工作范围。比如RedHat定义了runlevel 3和5。运行模式3 将系统初始化为字符界面的shell模式;运行模式5将系统初始化为GUI模式。无论是命令行界面还是GUI,运行模式3和5相对于其它运行模式而言都是完整的正式的运行状态,计算机可以完成用户需要的任务。而模式1,S等往往用于系统故障之后的排错和恢复。
很显然,这些不同的运行模式下系统需要初始化运行的进程和需要进行的初始化准备都是不同的。比如运行模式3 不需要启动X系统。用户只需要指定需要进入哪中模式,sysvinit将负责执行所有该模式所必须的初始化工作。
sysvinit运行顺序
Sysvinit 巧妙的用脚本,文件命名规则和软连接来实现不同的runlevel。首先,sysvinit需要读取/etc/inittab文件。分析这个文件的内容,它获得一下一些配置信息:
- 系统需要进入的runlevel
- 捕获组合键的定义
- 定义电源 fail/restore脚本
- 启动getty和虚拟控制台
得到配置信息后,sysvinit顺序执行一下这些步骤,从而将系统初始化为预定的runlevel X。
- /etc/rc.d/rc.sysinit
- /etc/rc.d/rc和/etc/rc.d/rcX.d (X 代表运行级别0-6)
- /etc/rc.d/rc.local
- X Display Manger(如果需要的话)
首先,运行rc.sysinit 以便执行一些重要的系统初始任务。在RedHat公司的RHEL5中(RHEL6 已经开始使用upstart了),rc.sysinit主要完成一下这些工作。
- 激活udev和selinux
- 设置定义在/etc/sysctl.conf中的内核参数
- 设置系统时钟
- 加载keymaps
- 使能交换分区
- 设置主机名(hostname)
- 根分区检查和remount
- 激活RAID和LVM设备
- 开启磁盘配额
- 检查并挂载所有文件系统
- 清楚过期的locks和PID文件
完成以上这些工作之后,sysvinit开始运行/etc/rc.d/rc脚本。根据不同的runlevel,rc脚本将打开对应该runlevel的rcX.d目录(X 就是runlevel),找到并运行存放在该目录下的所有启动脚本。每个runlevel X都有一个这样的目录,目录名为/etc/rc.d/rcX.d。
这些目录下存放着很多不同的脚本。文件名以S开头的脚本就是启动时应该运行的脚本,S后面跟的数字定义了这些脚本的执行顺序。在/etc/rc.d/rcX.d 目录下的脚本其实就是一些软链接文件,真实的脚本文件存放在/etc/init.d目录下。如下所示:
[root@li1556-65 rc.d]# ls
init.d rc rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d rc.local rc.sysinit
[root@li1556-65 rc.d]# cd rc3.d/
[root@li1556-65 rc3.d]# ls
K10saslauthd K75ntpdate K89rdisc S08ip6tables S11auditd S25netfs S55sshd S80sendmail S99local
K50netconsole K87restorecond K92iptables S10network S12rsyslog S26udev-post S58ntpd S90crond
[root@li1556-65 rc3.d]# ll
total 0
lrwxrwxrwx 1 root root 19 Mar 9 2014 K10saslauthd -> ../init.d/saslauthd
lrwxrwxrwx 1 root root 20 Mar 9 2014 K50netconsole -> ../init.d/netconsole
lrwxrwxrwx 1 root root 17 Apr 14 2017 K75ntpdate -> ../init.d/ntpdate
lrwxrwxrwx 1 root root 21 Mar 9 2014 K87restorecond -> ../init.d/restorecond
lrwxrwxrwx 1 root root 15 Mar 9 2014 K89rdisc -> ../init.d/rdisc
lrwxrwxrwx 1 root root 18 Dec 9 2016 K92iptables -> ../init.d/iptables
lrwxrwxrwx 1 root root 19 Mar 9 2014 S08ip6tables -> ../init.d/ip6tables
lrwxrwxrwx 1 root root 17 Mar 9 2014 S10network -> ../init.d/network
lrwxrwxrwx 1 root root 16 Mar 9 2014 S11auditd -> ../init.d/auditd
lrwxrwxrwx 1 root root 17 Mar 9 2014 S12rsyslog -> ../init.d/rsyslog
lrwxrwxrwx 1 root root 15 Mar 9 2014 S25netfs -> ../init.d/netfs
lrwxrwxrwx 1 root root 19 Mar 9 2014 S26udev-post -> ../init.d/udev-post
lrwxrwxrwx 1 root root 14 Mar 9 2014 S55sshd -> ../init.d/sshd
lrwxrwxrwx 1 root root 14 Apr 14 2017 S58ntpd -> ../init.d/ntpd
lrwxrwxrwx 1 root root 18 Apr 14 2017 S80sendmail -> ../init.d/sendmail
lrwxrwxrwx 1 root root 15 Mar 9 2014 S90crond -> ../init.d/crond
lrwxrwxrwx 1 root root 11 Mar 9 2014 S99local -> ../rc.local
当所有的初始化脚本执行完毕,Sysvinit运行/etc/rc.d/rc.local脚本。
rc.local是Linux留给用户进行个性化设置的地方。您可以把你自己想设置和启动的东西放到这里来,一台Linux server的用户一般不止一个,所以才有这样的考虑。
Sysvinit 和系统关闭
Sysvinit 不仅需要负载初始化系统,还需要负责关闭系统,在系统关闭时,为了保证数据的一致性,需要小心地按照顺序进行结束和清理工作。
比如应该先停止对文件系统有读写操作的服务,然后再umonut文件系统,否则数据就会丢失。
这种顺序的控制也是依靠/etc/rc.d/rcX.d/目录下所有脚本的命名规则来控制的,在该目录下所有以K开头的脚本都将在关闭系统时调用,字母K之后的数字定义了它们的执行顺序。
这些脚本负责安全地停止服务或者其它的关闭工作。
Sysvinit 的管理和控制功能
此外,在系统启动之后,管理员还需要对已经启动的进程进行管理和控制。原始的sysvinit软件包含了一系列的控制启动,运行和关闭所有其它程序的工具。
- halt:
- 停止系统
- init
- 这个就是sysvinit本身的init进程实体,以PID1身份运行,是所有用户进程的父进程。最主要的作用是在启动过程中使用/etc/inittab文件创建进程。
- killall5
- 就是 SystemV 的killall 命令,向除自己的会话(session)进程之外的其它进程发出信号,所以不能杀死当前使用的shell
- last
- 回溯/var/log/wtmp 文件(或者-f 选项指定的文件),显示自从这个文件建立以来,所有用户的登录情况。
- lastb
- 作用和last差不多,默认情况下使用/var/log/btmp文件,显示所有失败登录企图。
- mesg
- 控制其它用户对用户终端的访问
- pidof
- 找出程序的进程识别号(pid),输出到标准输出设备
- poweroff
- 等于shutdown -h -p ,或者telinit 0.关闭系统并切断电源
- reboot
- 等于shutdown -r 或者 telinit 6。重启系统。
- runlevel
- 读取系统的登录文件(一般是/var/run/utmp)把以前和当前的系统运行级输出到标准输出设备。
- shutdown
- 以一种安全的方式终止系统,所有正在登录的用户都会收到系统将要终止通知,并且不准新的登录
- sulogin
- 当系统进入单用户模式时,被init调用。当接收到启动加载程序传递的-b选项时,init也会调用sulogin。
- telinit
- 实际是init的一个连接,用来向init传递单字符参数和信号
- utmpdump
- 以一种用户友好的格式想标准输出设备显示/var/run/utmp文件的内容。
- wall
- 向所有有信息权限的登录用户发送消息
不同的Linux发行版在这些sysvinit的基本工具基础上又开发了一些辅助工具用来简化init系统的管理工作。比如RedHat的RHEL在sysvinit的基础上开发了initscripts软件包,包含了大量的启动脚本(如:rc.sysinit),还提供了service,checonfig等命令行工具,甚至一套图形化界面来管理init系统。其它的Linux发行版也各有各自的initscripts或其他名字的init软件包来简化sysvinit管理。
只要您理解了sysvinit机制,在一个最简的仅有sysvinit的系统下,您也可以直接调用脚本启动和停止服务,手动创建inittab和创建软连接来完成这些任务,因此理解sysvinit的基本原理和命令是最重要的。您甚至可以开发一套自己的管理工具。
Sysvinit的小结
Sysvinit 的优点是概念简单。Service 开发人员只需要编写启动和停止脚本,概念非常清楚;将 service 添加/删除到某个 runlevel 时,只需要执行一些创建/删除软连接文件的基本操作;这些都不需要学习额外的知识或特殊的定义语法(UpStart 和 Systemd 都需要用户学习新的定义系统初始化行为的语言)。
其次,sysvinit 的另一个重要优点是确定的执行顺序:脚本严格按照启动数字的大小顺序执行,一个执行完毕再执行下一个,这非常有益于错误排查。UpStart 和 systemd 支持并发启动,导致没有人可以确定地了解具体的启动顺序,排错不易。
但是串行地执行脚本导致 sysvinit 运行效率较慢,在新的 IT 环境下,启动快慢成为一个重要问题。此外动态设备加载等 Linux 新特性也暴露出 sysvinit 设计的一些问题。针对这些问题,人们开始想办法改进 sysvinit,以便加快启动时间,并解决 sysvinit 自身的设计问题。
Upstart 是第一个被广泛应用的新一代 init 系统。我们在接下来的第二部分介绍 UpStart。