Linux的init程序与系统服务管理

一、Init程序

       Init是位于/sbin/init的程序。是在linux系统启动过程中,初始化所有设备驱动程序和数据结构之后,由内核启动的一个用户级程序,并由此 Init 程序完成系统启动过程。

Init 程序主要有 Sysvinit 、Upstart 、Systemd 。新的 Init 程序的目标:1.希望更多的开机程序是并行开启的,而不是串行(即依赖shell脚本,并大量串行的

执行启动脚本,比如 Sysvinit);    2. 希望开机执行的程序越少越好、进入工作状态越快越好。


(一)、sysvinit

1. 运行级别

        sysvinit 是较早使用的 Init 程序,基于运行级别。系统以何种运行级别启动由Init读取 /etc/inittab 文件中的缺省级别设置来确定。运行级别定义具体如下:

 0 => 关机(halt)

 1 => 单用户模式(single)

 2 => 多用户

 3 => 完整的多用户模式(标准的运行级)

 4 => 系统保留

 5 => 完整的多用户,使用 X11 (x window)

 6 => 重启(reboot)


Ubuntu 默认是没有 /etc/inittab 文件的,且运行级别的定义也有不同,具体如下:

 0 => 关闭系统(halt)

 1 => 单用户模式(single)

 6  => 重启(reboot)

 2,3,4,5 => 多用户模式(multi-user)


2. 服务启动

        启动与关闭服务的脚本存放在 /etc/rc[?].d 目录下。其中 [x] 表示0~6,分别对应级别0~6。/etc/rcS.d/ 路径下的脚本会先于 /etc/rc[?].d/ 执行。/etc/rc.local 会在 /etc/rc[?].d/

路径下的脚本执行完成后,最后执行。例如:运行级别为2,将执行 /etc/rc2.d/ 下的脚本。其中的脚本命名方式:K开头(k=>kill)、S开头(S=>start)。K、S 后面跟着的

数字代表执行优先级,数字越小,执行顺序越高(优先执行,比如K15 优先于K20被执行)。当执行K 开头的脚本,比如,K20acpid 会执行 /etc/init.d/acpid脚本,并给脚本

传递一个stop参数,指示其关闭 acpi daemon 服务。S 开头的会传递 start参数,指示其开启服务。而且 /etc/rc[?].d/ 下面的脚本都是软链接,指向 /etc/init.d/ 中真正的执行

脚本。若想要添加用户自定义的服务,需要将自定义服务的启动脚本放到 /etc/init.d/ 路径下,并且在对应的运行级别目录下为其添加软链接即可。

        命令:service 服务名  start | stop | restart      或者 /etc/init.d/   服务名   start | stop | restart 

        服务管理工具:chkconfig       sysv-rc-conf

注:在Ubuntu 中系统定义的服务存放在 /etc/services 文件中。部分非常驻服务由 xinetd 服务控制。xinetd服务配置文件保存在 /etc/xinetd.conf 。例如 Telnet 服务就是

由xinetd 代理服务的。


3. Init 执行流程

sysvinit 时代 Linux 启动流程:

1. BIOS

2. MBR

3. Grub

4. Linux-Kernel

5. Init

    a. 运行 /sbin/init

    b. 读取 /etc/inittab(该文件定义了运行级别,在Ubuntu中演变为 /etc/init/rc-sysinit.conf)

    c. 执行 /etc/rc.d/rc-sysinit (Ubuntu中是 /etc/init/rc-sysinit.conf)

    d. 执行 /etc/rcS.d/ 目录下的脚本

    e. 切换到默认的运行级别执行(Ubuntu默认执行  " env DEFAULT_RUNLEVEL=2 "     telinit  "${DEFAULT_RUNLEVEL}" ) 

        执行 /etc/rc2.d/ 目录下的脚本

    f.  执行 /etc/rc.local (用户自定义的开机服务可放到该脚本中)

    g.  执行 /bin/login (此时,已完成 Init 全过程)

6. 完成系统初始化,用户登录。



(二)、Upstart(Upstart init daemon)

1.  事件和作业

     事件:

        Upstart init daemon 是 sysv init daemon 的替代者,基于事件而非运行级别(并行启动部分服务)。可在系统启动、关闭、任务状态改变时启动或关闭服务。

事件就是 Init 得到的关于服务状态的变更信息。用户可通过 initctl emit 命令来手动触发一个事件,但通常情况下,事件是自动触发的。

Upstart的三种事件类型:Signal Event:异步且非阻塞的、Method Event:同步且阻塞的、Hook Event:同步且阻塞的。


Upstart的三个主要进程:

1)init: upstart 主进程(PID=1)。职责是读取配置文件,处理各种服务和应用程序的依赖关系。根据事件来启动这些功能和服务,并动态的进行管理。

2)initctl: Upstart 事件管理器。可被应用程序用来通知 init 哪些事件发生。initctl list 可类比 Sysvinit 的 chkconfig工具。

3)telinit: 管理运行级别。要研究 Upstart 是如何兼容 sysvinit 的,就要从telinit入手。


附:/etc/init/rc-sysinit.conf 脚本中 telinit 的使用(省略部分,只留下关键的):

[plain]  view plain copy
  1. # rc-sysinit - System V initialisation compatibility  
  2. #  
  3. # This task runs the old System V-style system initialisation scripts,  
  4. # and enters the default runlevel when finished.  
  5.   
  6. start on (filesystem and static-network-up) or failsafe-boot  
  7. stop on  runlevel  
  8.   
  9. env DEFAULT_RUNLEVEL=2  
  10.   
  11. emits runlevel  
  12.   
  13. task  
  14.   
  15. script  
  16.     # Check for default runlevel in /etc/inittab  
  17.     if [ -r /etc/inittab]  
  18.     then  
  19.     eval "$(sed -nre 's/^[^#][^:]*:([0-6sS]):initdefault:.*/DEFAULT_RUNLEVEL="\1";/p' /etc/inittab || true)"  
  20.     fi  
  21.       
  22.     # Run the system initialisation scripts  
  23.     [ -n "${FROM_SINGLE_USER_MODE}" ] || /etc/init.d/rcS  
  24.   
  25.     # Switch into the default runlevel  
  26.     telinit "${DEFAULT_RUNLEVEL}"  
  27. end script  




作业(Job):

         job是 Init 可以理解的一系列指令。用户可以用 initctl start 和 initctl stop 命令手动启动或终止一项工作。作业有10种状态,此处不赘述。

分类:1) 任务(Task):运行、并在执行结束后返回到等待状态的工作;

            2) 服务(Services):通常不会自己结束到工作;

            3) 抽象作业(Abstact job):只存在于 Upstart 内部。没有PID,定义 “永久运行” 的作业。


2. 服务启动

        Upstart 的所有服务放在 /etc/init/ 目录下,以服务名.conf 文本文件存放。*.conf 配置文件中:

        start on、stop on: 服务的依赖关系;

        exec: 服务启动命令;

        respawn :设置服务异常停止后服务自动重启;

        script  ... end script :服务启动前后执行脚本(参看作业的10种状态);

        说明:要在开机启动自定义服务,只要向 /etc/init/ 中添加 自定义服务.conf 配置文件即可。

        命令/工具:initctl list=>查看所有服务状态     initctl start | stop | restart   服务名


3.  Init 执行流程

upstart 下 Init 执行流程:

1. BIOS

2. MBR

3. Grub

4. Linux-Kernel

5. Init

    a. 运行 /sbin/init

    b. Upstart 内部初始化,触发 startup 事件

    c. 触发依赖 startup 事件的服务,主要包括挂载文件系统、本地回环网络(即定义了 start on startup 的服务脚本)

    d. 触发 rc-sysinit 事件(/etc/init/rc-sysinit.conf ),首先执行 /ec/rcS.d/目录下的脚本(事实上,此后的过程与sysvinit 执行过程类似)

    e. 切换到默认的运行级别执行脚本。例如:/etc/rc2.d/ 目录下的脚本(Ubuntu 默认是 " env  DEFAULT_RUNLEVEL=2 "     telinit "${DEFAULT_RUNLEVEL}" )        

    f.  执行 /etc/rc.local

    g. 执行 /bin/login(完成init过程)

6. 完成系统初始化,用户登录。

说明:很多细节的东西还是有待研究,放到实际应用的场合学习、理解可能效果更好。



(三)、Systemd

1. 基本概念及原理

        systemd  同样是 sysv init daemon 的替代者。特点是与 sysvinit 完全兼容、更清晰的服务依赖关系、开机系统初始化服务并行启动、更少的shell开销。每个服务就是

一个 unit,对应于运行级别,systemd有一个 target (multi-user.target)。这当然要比 Upstart  做的更彻底,还记得本文开头说的,Init设计的两个目标吗?

        FedoraProject 官网对 Systemd 的介绍(Fedora已经使用systemd作为默认的Init):   

        systemd 是 Linux 下一个与 SysV 和 LSB 初始化脚本兼容的系统和服务管理器。systemd 使用 socket 和 D-Bus 来开启服务,提供基于守护进程的按需启动策略,

保留了 Linux cgroups 的进程追踪功能,支持快照和系统状态恢复,维护挂载和自挂载点,实现了各服务间基于从属关系的一个更为精细的逻辑控制,拥有前卫的并行

性能。systemd 无需经过任何修改便可以替代 sysvinit 。


2. 服务启动

    服务配置文件放在 /lib/systemd/system/ 目录下,以 *.service命名。没有运行级别的概念,但是完全兼容sysvinit。

    rsyslog.service文件格式:

     [Unit]

     Description=System Logging Service
     
     [Service]

     ExecStartPre=/bin/systemctl stop systemd-kmsg-syslogd.service

     ExecStart=/usr/sbin/rsyslogd -n -c5

     Sockets=syslog.socket

     StandardOutput=null
     
     [Install]

     WantedBy=multi-user.target


    命令:systemctl enable | disable  *.service => 设置服务 自动启动 | 非自动启动

                systemctl start | stop | restart *.service => 服务启动 | 停止 | 重启

                systemctl status *.service => 查看服务状态

    工具:systemadm => 一个图形化的管理工具


3. Init 执行流程

    Systemd init 执行流程:





附:linux2.6.32.61内核启动时的一段代码 ( /init/main.c ),说明 kernel 与/sbin/init 的关系。   

[cpp]  view plain copy
  1. static noinline int init_post(void)  
  2.     __releases(kernel_lock)  
  3. {  
  4.     /* need to finish all async __init code before freeing the memory */  
  5.     async_synchronize_full();  
  6.     free_initmem();  
  7.     unlock_kernel();  
  8.     mark_rodata_ro();  
  9.     system_state = SYSTEM_RUNNING;  
  10.     numa_default_policy();  
  11.   
  12.     if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)  
  13.         printk(KERN_WARNING "Warning: unable to open an initial console.\n");  
  14.   
  15.     (void) sys_dup(0);  
  16.     (void) sys_dup(0);  
  17.   
  18.     current->signal->flags |= SIGNAL_UNKILLABLE;  
  19.   
  20.     if (ramdisk_execute_command) {  
  21.         run_init_process(ramdisk_execute_command);  
  22.         printk(KERN_WARNING "Failed to execute %s\n",  
  23.                 ramdisk_execute_command);  
  24.     }  
  25.   
  26.     /* 
  27.      * We try each of these until one succeeds. 
  28.      * 
  29.      * The Bourne shell can be used instead of init if we are 
  30.      * trying to recover a really broken machine. 
  31.      */  
  32.     if (execute_command) {  
  33.         run_init_process(execute_command);  
  34.         printk(KERN_WARNING "Failed to execute %s.  Attempting "  
  35.                     "defaults...\n", execute_command);  
  36.     }  
  37.     run_init_process("/sbin/init");       
  38.     run_init_process("/etc/init");  
  39.     run_init_process("/bin/init");  
  40.     run_init_process("/bin/sh");  
  41.   
  42.     panic("No init found.  Try passing init= option to kernel.");  
  43. }  



init是Linux系统操作中不可缺少的程序之一。

所谓的init进程,它是一个由内核启动的用户级进程。

内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。所以,init始终是第一个进程(其进程编号始终为1)。

内核会在过去曾使用过init的几个地方查找它,它的正确位置(对Linux系统来说)是/sbin/init。如果内核找不到init,它就会试着运行/bin/sh,如果运行失败,系统的启动也会失败。

Linux执行完一些初始化以后,第一个启动init进程。init进程是所有进程的父进程,负责启动其它进程,这些进程大多数是服务进程(daemon)。随着时间的推移这个启动过程也在变化。但目前主要有两种:System V style的runlevel式启动和upstart代表的event-based启动。

1,System V style的runlevel启动

init进程会读取/etc/inittab来决定进入哪一个runlevel。

/sbin/init => /etc/inittab => runlevel  rc script [/etc/rcN.d]

runlevel有些类似Windows中你按下F8进入的安全模式,但比Windows划分的更细,除此之外你还可以通过配置决定每个runlevel加载一些什么服务。不同的Linux发行版对runlevel的解释会有所不同,但runlevel思想是一样的:init通过/etc/inittab决定它的runlevel,然后去/etc/rcN.d/(N代表runlevel的数字表示)去找相应的启动脚本。

2,upstart式的启动

通过/etc/inittab的启动已经不能满足当前的需要了,比如支持一些热插拔设备。一种event-based的init启动产生了。ubuntu中就是使用upstart来启动系统的。upstart使用/etc/init/目录来决定系统在启动时运行那些服务。你可以通过intctrl来控制upstart启动的服务。

3,Ubuntu对System V style runlevel的模拟

你若查看/etc/init/目录,你可以看见一个为rc.conf的脚本,这个脚本起到的作用就是对system v style runlevel的模拟。我们看下这个脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# rc - System V runlevel compatibility
#
# This task runs the old System V-style rc script when changing between
# runlevels.
  
description  "System V runlevel compatibility"
author       "Scott James Remnant <[email protected]>"
  
emits deconfiguring-networking
emits unmounted-remote-filesystems
  
start on runlevel [0123456]
stop on runlevel [!$RUNLEVEL]
  
export  RUNLEVEL
export  PREVLEVEL
  
console output
env  INIT_VERBOSE
  
task
  
exec  /etc/init .d /rc  $RUNLEVEL

这个脚本通过执行/etc/init.d/rc $RUNLEVEL来运行具体的rc script。这些rc script在/etc/rcN.d/目录下。由此完成了对system v style runlevel的模拟。

不仅如此,ubuntu还提供了update-rc.d命令,你可以通过此命令来完成system v style runlevel启动脚本的配置。比如你想在启动时不启动apache:

update-rc.d apache2 disable

ubuntu将所有启动脚本放在/etc/init.d/这个目录下,当你运行update-rc.d命令时update-rc.d会根据你的参数新建一些symbol link到相应的/etc/rcN.d/目录。



你可能感兴趣的:(linux)