loong博客

Linux init

目录

  • Linux操作系统启动
  • System V init
  • UpStart
  • Systemd

Linux操作系统启动

Linux操作系统的启动首先从BIOS(基本输入输出系统)开始。BIOS系统会初始化和测试系统硬件(开机自检,Power-On Self Test, 简称POST), 已确定所连接的装置是否正常运行,然后从存储设备中加载引导程序,载入内核并初始化内核。

操作系统启动

内核初始化的最后一步就是启动 init 进程,这个进程是系统的第一个进程(PID为1),又叫超级进程。它负责产生其它所有用户进程。如果这个进程退出了,那么所有进程都将被 kill 。如果一个子进程的父进程退出了,那么这个子进程会被挂到 PID 1 下面。

System V init

System V init是类 Unix 操作系统中第一个初始化系统。System V init 定义不同的运行模式(runlevel),通常分为7种。

  • 0 停机
  • 1 单用户
  • 2 多用户(无网络连接)
  • 3 多用户(启动网络连接)
  • 4 用户自定义
  • 5 多用户带图形界面
  • 6 重启

通常在 /etc/inittab 文件中定义了各种运行模式的工作范围。 System V init 巧妙地用脚本,文件命名规则和软链接来实现不同的 runlevel 。System V init会读取 /etc/inittab 文件获得配置信息,然后顺序地执行以下步骤。

  • /etc/sysinit 执行一些重要的系统初始化任务;

  • /etc/init.d/ 存放各种进程的启停脚本;

  • /etc/rc[X].d/ 存放不同 runlevel 下后台进程服务, 该目录下的脚本其实都是一些软链接文件,真实的脚本文件存在在 /etc/init.d/ 下;

  • /etc/rc.d/rc.local 存放用户个性化设置脚本;

System V init 优点

  • 概念简单 开放人员只需要编写启动和停止脚本,不需要学习额外的知识;

  • 确定的执行顺序 脚本严格按照启动数字的大小顺序执行,有益于错误排查;

System V init 缺点

  • 启动速度慢 串行地执行脚本导致运行速度较慢;

UpStart

在 Linux 进入桌面系统后,桌面系统的一个特点是经常重启,需要频繁地使用硬件热插拨技术。在 Linux 2.6内核支持下,一旦新外设连接到系统时, 内核便可以自动实时地发现它们,并初始化这些设备,进而使用它们。可是这些特性为 System V init 带来一些挑战。当系统初始化时, 发现需要被初始化的设备并没有连接到系统上,需要重启服务。另外在外设没有接入系统的情况下,启动这些服务也是一种浪费。另外还有网络硬盘挂载的问题。在 /etc/fstab 中,可以指定系统自动挂载网络硬盘,比如 NFS 。System V init 可能在初始化挂载网盘时,还没有网络,导致网络硬盘无法访问。针对这些问题,重新设计和开发了一个全新的初始化系统,即 UpStart。UpStart 基于事件驱动的机制,把之前的串行同步启动服务的方式改成由事件驱动的异步方式。UpStart 有两个重要的概念,分别是 Job 和 Event 。

Job

Job 是一个工作单元,用来完成一件工作。Job 分为三个类型:

  • task job

    在一定时间内执行完毕的任务,比如删除文件等。

  • service job

    后台服务任务,比如 apache httpd。这类进程一般不会退出,一旦开始运行就成为一个后台精灵进程,由 init 进程管理。

  • abstract job

    仅由 upstart 内部使用。

Event

Event 是一个事件。一旦某个事件发生,UpStart就向系统发送消息。Event 分为三类:

  • Signals

    Signals 事件是异步的,发送一个信号后控制权立即返回;

  • Methods

    Methods 事件是同步的;

  • Hooks

    Hooks 事件是同步的。它介于 Signals 和 Methods 之间,调用发出 Hooks 事件的进程必须等待事件完成才可以得到控制权,但不检查事件是否成功;

常见的事件包括:

  1. 系统上电启动,init 进程会发送start事件;
  2. 根文件系統可写时,相应 job 会发送文件系統就绪的事件;
  3. 一个网络设备被发现时,可以发出相应的事件;

Systemd

直到2010年,在 RedHat工作的工程师 Lennart 引入了一个新的 init 系统 —— systemd。systemd 不但想取代已有的 init 系统,而且还想干更多的东西。Lennart 认为 upstart 基于事件的设计很好,但是还是不够快。虽然 upstart 用事件可以达到一定的启动并行度,但是,本质上来说,这些事件还是会让启动过程串行在一起, 如 NetworkManager 在等待 D-Bus 的启动事件,而 D-Bus 在等待 syslog 的启动事件。

systemd 清醒的认识到 init 进程的首要目标是要让用户快速的进入可以操作系统的环境,所以这个速度一定要越快越好。于是 systemd 的设计理念就是两条:

  • To start less.

  • And to start more in parallel.

按需启动还好理解,那么”有依赖关系的并行启动”是怎么做到的 ? 这里,systemd 借鉴了 MacOS 的 Launchd

要解决这些依赖性,systemd 需要解决好三种底层依赖 - Socket, D-Bus ,文件系统。

  • Socket依赖

如果服务C依赖于服务S的socket,那么就要先启动S,然后再启动C,因为如果C启动时找不到S的Socket,那么C就会失败。systemd 可以帮你在S还没有启动好的时候,建立一个socket,用来接收所有的C的请求和数据,并缓存下来。一旦S全部启动完成,再把临时socket缓存的数据和Socket描述符替换过去。

  • D-Bus依赖

D-Bus 全称 Desktop Bus,是一个用来在进程间通信的服务。除了用于用户态进程和内核态进程通信,也用于用户态的进程之间。比如:NetworkManager 就是通过 D-Bus 和其它服务进程通讯的,也就是说,如果一个进程需要知道网络的状态,那么就必需要通过 D-Bus 通信。D-Bus 支持 “Bus Activation” 的特性。也就是说,A要通过 D-Bus 服务和B通讯,但是B没有启动,那么 D-Bus 可以把B起来,在B启动的过程中,D-Bus 帮你缓存数据。systemd 可以帮你利用好这个特性来并行启动 A 和 B。

  • 文件系统依赖

系统启动过程中,文件系统相关的活动是最耗时的,比如挂载文件系统,对文件系统进行磁盘检查,磁盘配额检查等都是非常耗时的操作。在等待这些工作完成的同时,系统处于空闲状态。那些想使用文件系统的服务似乎必须等待文件系统初始化完成才可以启动。systemd 参考了 autofs 的设计思路,使得依赖文件系统的服务和文件系统本身初始化两者可以并发工作。autofs 可以监测到某个文件系统挂载点真正被访问到的时候才触发挂载操作,这是通过内核 automounter 模块的支持而实现的。比如一个 open() 系统调用作用在某个文件系统上的时候,而这个文件系统尚未执行挂载,此时 open() 调用被内核挂起等待,等到挂载完成后,控制权返回给 open() 系统调用,并正常打开文件。

除此之外,与System V风格 init 相比,systemd 还能:

  • 用cgroups代替进程ID来追踪进程,因此即使是两次fork之后生成的守护进程也不会脱离systemd的控制;

  • 自动检测启动的服务间有没有环形依赖;

  • 内建 autofs 自动挂载管理功能;

  • 支持快照和系统恢复

  • 基于journald的服务日志管理;

  • ……

参考