九牛一毛的意思和造句:6.3 init进程

来源:百度文库 编辑:中财网 时间:2024/05/11 00:39:28

    6.3 init进程

    除非你要做一些非常特殊的配置,否则永远都不需要一个由用户定制的初始化程序,因为标准init进程的功能和使用非常灵活。init进程以及即将探讨的一系列启动脚本一起实现了通常所谓的 System V Init,这一名称源自最初使用这种模式的UNIX System V。现在我们就来研究这个功能强大的系统配置和控制工具。

    在前几节提到,init进程是在内核引导过程结束后由内核产生的第一个用户空间程序。就像你所了解的,在运行中的Linux系统里,每个进程与其他进程存在"子父"关系,而init进程是Linux系统里所有用户空间进程最终的父进程。此外,init进程会提供一套环境变量(包括PATH和CONSOLE等)的默认配置,供其他所有进程继承。

    init进程的主要作用是通过一个特殊的配置文件来产生其他程序,这个特殊的配置文件通常就是/etc/inittab。Init具有运行级(runlevel)的概念,运行级可以视作系统的一种运行状态。每个运行级都由进入该运行级时启用的服务和产生的程序所确定。

    init进程在任何给定时刻都只能处于一种运行级。init进程使用的运行级包含从0到6的运行级和一个称为S的特殊运行级。运行级0指示init进程终止系统,而运行级6则会使系统重新启动。对于每一种运行级,通常都会提供一套启动和关闭脚本,以便指定每种运行级的系统应当执行的动作。每个指定运行级所执行的动作都由稍候将介绍的配置文件/etc/inittab决定。

    许多Linux版本都保留了一些运行级用于特定的目的,表6-2详细列出了在许多Linux版本中的init运行级及其使用目的。

    表6-2 运行级

    运 行 级

    使用目的

    0

    关闭系统(停机)

    1

    维护用的单用户系统配置

    2

    用户自定义

    3

    通用多用户配置

    4

    用户自定义

    5

    多用户模式,启动进入图形用户界面

    6

    重新启动系统(reboot)


    运行级脚本通常位于/etc/rc.d/init.d目录下,从中可以找到绝大部分的脚本,分别用于启用或禁用对应的服务。调用某个脚本并传入一个合适的参数,如start(启动)、stop(停止)或restart(重新启动),就能对相应的服务进行手动配置。代码清单6-3例举了如何重新启动NFS服务。

    代码清单6-3 重新启动NFS服务

    1. $ /etc/rc.d/init.d/nfs restart  
    2. Shutting down NFS mountd:                           [  OK  ]  
    3. Shutting down NFS daemon:                           [  OK  ]  
    4. Shutting down NFS quotas:                           [  OK  ]  
    5. Shutting down NFS services:                         [  OK  ]  
    6. Starting NFS services:                              [  OK  ]  
    7. Starting NFS quotas:                                [  OK  ]  
    8. Starting NFS daemon:                                [  OK  ]  
    9. Starting NFS mountd:                                [  OK  ] 

    如果你用过桌面Linux发行版(如Red Hat或Fedora),一定会在系统启动过程中看到过类似代码清单6-3所示的信息。

    运行级是由该运行级下启用的服务所定义的。大多数Linux发行版都会在/etc下提供一个目录结构,其中包含指向目录/etc/rc.d/init.d中服务脚本的链接。这些运行级相关的目录一般位于/etc/rc.d中。在这个目录里,你会看到一系列运行级的目录,分别包含各个运行级里启动和停止服务的脚本,init只是在进入和退出运行级时执行这些脚本。这些脚本定义了系统的状态,同时,inittab文件会告知init关于脚本和指定运行级的对应关系。代码清单6-4显示了一个/etc/rc.d下的目录结构,这些目录控制着运行级所分别对应服务的启动和停止。

    代码清单6-4 包含运行级的目录结构

    1. $ ls -l /etc/rc.d  
    2. total 96  
    3. drwxr-xr-x 2 root root  4096 Oct 20 10:19 init.d  
    4. -rwxr-xr-x 1 root root  2352 Mar 16  2004 rc  
    5. drwxr-xr-x 2 root root  4096 Mar 22  2005 rc0.d  
    6. drwxr-xr-x 2 root root  4096 Mar 22  2005 rc1.d  
    7. drwxr-xr-x 2 root root  4096 Mar 22  2005 rc2.d  
    8. drwxr-xr-x 2 root root  4096 Mar 22  2005 rc3.d  
    9. drwxr-xr-x 2 root root  4096 Mar 22  2005 rc4.d  
    10. drwxr-xr-x 2 root root  4096 Mar 22  2005 rc5.d  
    11. drwxr-xr-x 2 root root  4096 Mar 22  2005 rc6.d  
    12. -rwxr-xr-x 1 root root   943 Dec 31 16:36 rc.local  
    13. -rwxr-xr-x 1 root root 25509 Jan 11  2005 rc.sysinit 

    每一个运行级都由rcN.d里包含的脚本进行定义,其中N即为运行级。在每个rcN.d目录下,你可以看到大量以特定顺序排列的符号链接,这些符号链接的名称都以K或S开头。以S开头的服务脚本在执行启动操作时调用,而以K开头的服务脚本则在执行停止操作时调用。代码清单6-5例举了一个只包含几个服务脚本的目录。

    代码清单6-5 运行级目录示例

    1. lrwxrwxrwx 1 root root 17 Nov 25  2004 S10network -> ../init.d/network  
    2. lrwxrwxrwx 1 root root 16 Nov 25  2004 S12syslog  -> ../init.d/syslog  
    3. lrwxrwxrwx 1 root root 16 Nov 25  2004 S56xinetd  -> ../init.d/xinetd  
    4. lrwxrwxrwx 1 root root 16 Nov 25  2004 K50xinetd  -> ../init.d/xinetd  
    5. lrwxrwxrwx 1 root root 16 Nov 25  2004 K88syslog  -> ../init.d/syslog  
    6. lrwxrwxrwx 1 root root 17 Nov 25  2004 K90network -> ../init.d/network 

    在这个例子中,我们指示启动脚本在进入这个假想的运行级时开启3个服务:network、syslog和xinetd。由于以S*开头的脚本会在S*后面紧跟着数字,所以它们会按照这些数字的顺序来执行。类似地,在退出该运行级的时候,xinetd、syslog和network这3个服务依次被终止,而且也会类似地根据符号链接名中字母K后面的两位数字的大小顺序依次将这些服务停止。在真正的系统中,毫无疑问,目录下的脚本(链接)数量要多得多。你还可以为自己特定的应用程序添加相应的服务脚本。

    执行这些服务启动和关闭脚本的顶层脚本则在init配置文件中定义,接下来就将探讨这个配置文件。   6.3.1 inittab

    启动init进程时会试着读取系统配置文件/etc/inittab,包含针对每个运行级及适用于所有运行级的指令。这个文件和init的行为在绝大多数Linux工作站的帮助手册里都有详细的记录,在一些关于Linux系统管理的书籍中也有详细描述。我们不打算在这里重复这些工作,而是重点着眼于开发人员如何为嵌入式系统配置inittab。至于inittab和init如何一起工作的详细说明,在大部分Linux工作站下可以通过键入man init和man inittab查看帮助手册得知。

    我们来看一个简单嵌入式系统的典型inittab文件。代码清单6-6是针对一个系统的inittab简单示例,该文件只支持一个运行级,实现系统的关机和重启。

    代码清单6-6 简单的inittab文件示例

    1. # /etc/inittab  
    2.  
    3. # The default runlevel (2 in this example)  
    4. id:2:initdefault:  
    5.  
    6. # This is the first process (actually a script) to be run.  
    7. si::sysinit:/etc/rc.sysinit  
    8.  
    9. # Execute our shutdown script on entry to runlevel 0  
    10. l0:0:wait:/etc/init.d/sys.shutdown  
    11.  
    12. # Execute our normal startup script on entering runlevel 2  
    13. l2:2:wait:/etc/init.d/runlvl2.startup  
    14.  
    15. # This line executes a reboot script (runlevel 6)  
    16. l6:6:wait:/etc/init.d/sys.reboot  
    17.  
    18. # This entry spawns a login shell on the console  
    19. # Respawn means it will be restarted each time it is killed  
    20. con:2:respawn:/bin/sh 

    这个非常简单的inittab 脚本描述了3个不同的运行级,每个运行级都与一个脚本相关联,这些脚本必须是开发人员根据每个运行级所期望的动作而创建的。当init进程读取这个文件时,执行的第一个脚本是/etc/rc.sysinit,由标签sysinit表示。然后init进程进入运行级2,执行为运行级2定义的脚本,这个例子里即为脚本/etc/init.d/runlvl2.startup。正如代码清单6-6中的:wait:标签所示,init进程在该脚本执行完毕之前一直处于等待状态。在运行级2的脚本执行完毕后,init进程会在控制台中生成一个shell(通过符号链接/bin/sh),如代码清单6-6最后一行所示。关键词respawn指示init进程一旦检测到shell退出便重新启动shell。代码清单6-7显示了启动期间的输出信息。

    代码清单6-7 启动信息示例

    1. ...  
    2. VFS: Mounted root (nfs filesystem).  
    3. Freeing init memory: 304K  
    4. INIT: version 2.78 booting  
    5. This is rc.sysinit  
    6. INIT: Entering runlevel: 2  
    7. This is runlvl2.startup  
    8.  
    这个例子里的启动脚本除了为便于说明而打印自身被执行的信息外,不做其他任何事情。当然,在一个实际的系统中,这些脚本会启用若干功能和服务,完成一些有用的任务!就该例的这个简单配置文件而言,你可以在脚本/etc/init.d/runlvl2.startup里为特定的组件启用一些服务和应用程序,同时在关机或者重启脚本里执行逆操作,即终止这些应用程序、服务和设备。我们会在下一节分析一些典型的系统配置,以及在启动脚本里启用这些配置所必需的条目。  6.3.2 Web服务器启动脚本示例

    这个启动脚本示例很简单,毕竟只是为了说明其工作机制,并指导你设计自己的系统启动和关机动作。这个例子基于busybox工具,其初始化行为与前面提到的init有稍许差别,具体的差别将在第11章中详细介绍。

    在一个包含Web服务器的典型嵌入式设备中,为便于维护和远程接入,我们可能会需要设备提供多个服务器。在这个例子里,我们通过inetd启用了两个服务器,支持HTTP和Telnet接入。代码清单6-8为我们假想的Web服务器设备提供了一个简单的rc.sysinit脚本。

    代码清单6-8 Web服务器的rc.sysinit

    1. #!/bin/sh  
    2.  
    3. echo "This is rc.sysinit"  
    4.  
    5. busybox mount -t proc none /proc  
    6.  
    7. # Load the system loggers  
    8. syslogd  
    9. klogd  
    10.  
    11. # Enable legacy PTY support for telnetd  
    12. busybox mkdir /dev/pts  
    13. busybox mknod /dev/ptmx c 5 2  
    14. busybox mount -t devpts devpts /dev/pts 

    在这个简单的初始化脚本中,我们首先启用了proc文件系统,第9章会详细介绍这个非常有用的子系统。接着启动了系统日志记录进程,以便捕获系统运行过程中的信息,当系统运行出现某些错误时,它能派上大用场。最后几行启用了UNIX PTY子系统支持,该子系统是本例实现Telnet服务器时所必需的。

    代码清单6-9中显示了运行级2的启动脚本里的命令,这些命令启用了操作该设备所需的服务。

    代码清单6-9 运行级2的启动脚本示例

    1. #!/bin/sh  
    2.  
    3. echo "This is runlvl2.startup"  
    4.  
    5. echo "Starting Internet Superserver"  
    6. inetd  
    7.  
    8. echo "Starting web server"  
    9. webs & 

    毋庸置疑,这个运行级2的启动脚本非常简单。首先我们启用了所谓的Internet超级服务器inetd,它会拦截常见的TCP/IP请求并为之创建相应的服务。在这个例子中,我们通过名为/etc/inetd.conf的配置文件启用Telnet服务,然后执行Web服务器,本例为webs。这就是该脚本的全部内容,尽管简单,不过这个配置已能支持Telnet和Web服务了。

    要完成上述配置工作,还需要提供一个关闭脚本(参考代码清单6-6)。就本例而言,这个脚本会在系统关机之前终止上面的Web服务器和Internet超级服务器。对这个例子来说,要实现正确的系统关机,这样的操作足够了。