Linux学习51 CentOS系统启动流程介绍
一、CentOS系统启动流程
1、Linux系统的组成部分:内核+根文件系统
a、内核要实现的功能:进程管理、内存管理、网络协议栈、文件系统、驱动程序、安全功能
IPC:inter Process Communication 进程间通信
本地通信:消息队列、semerphor、shm(共享内存)
主机间通信:socket
2、运行中的系统环境可分为两层:内核空间,用户空间
用户空间:应用程序(进程或线程)
内核空间:内核代码(系统调用)
3、根分区
a、启动分区:/boot
b、rootfs:FHS特定目录结构,/bin,/sbin,/lib,/etc,/proc,/sys,/dev,init
c、系统还没有加载时,/boot在根上,如果内核还没有装载肯定不可能有根,没有根我们怎么去找/boot呢?这个时候我们就需要系统启动时有一个工作于内核启动之前的程序来负责完成这一切,因此站在动态视角,内核的确是系统启动起来以后第一个脱离根文件系统并且要负责挂载和创建管理根文件系统的一个最底层的虚拟机,他把计算机虚拟成一种虚拟接口,因此我们站在启动起来以后的视角来讲,我们操作系统其实由两部分组成,即由内核和根文件系统组成,这是动态视角。但是站在静态视角来讲,我们除了根文件系统之外,或者除了磁盘分区和相关文件之外他没有其他的东西。也就是所在启动启动之前他们都静静的躺在磁盘之上表现为文件。
4、操作系统组成部分
底层硬件+上层内核+内核提供的系统调用和库调用,任何程序员都可以基于系统调用或库调用来写程序。而我们的shell相当于就是用户接口程序,如果用户期望能和当前主机交互就需要通过一个shell接口进而来管理整个操作系统之上的其它的应用程序。我们shell是基于库调用的,也就是说是在库调用之上。下面这个图稍微有点问题,严格意义上来讲我们的库调用也是基于系统调用的封装,是在系统调用之上的,相当于是封装了同名的系统调用。我们说过我们的linux的重要哲学思想是一切皆文件,所以我们连到shell这个交互式接口上对我们操作系统管理大多数情况下都是对一个个文件进行管理操作,虽然在此基础之上我们也能够完成进程管理等等更高级复杂的功能,而站在静态视角之上来看我们无非就是对文件系统之上的文件进行管理。
5、内核设计流派
a、两种流派,两种流派指的是要把内核中的每一种功能组织成单个程序还是把内核中要实现的各功能分别组织成各程序而后有一个特权级的总领性的程序来把这些各功能模块组织协调起来。
b、第一种流派:单内核设计,把所有功能集成与同一个程序中
Linux,有人问林纳斯为什么不设计成微内核模式或者重构Linux成微内核模式,林纳斯回答说我不需要他成为最先进的,我只需要他能用。
c、第二种流派:微内核设计,每种功能使用一个单独的子系统实现
Windows,Solaris
d、Linux内核特点
支持模块化:可以把其中非关键性功能或可有可无功能,或者是有替换者产品的功能给其设计成为了内核模块,并且这些内核模块有非常重要的特性,就是支持动态装载和卸载。也就意味着说某一个内核功能需要用到的时候我们直接把其装载进来即可,用不到的时候还可以立即动态卸载。这相当于Linux内核在某些功能上也有了微内核设计的特性,而且避免使得整个内核过于庞大。因此现在的内核就是高度模块化的,除了最主要的核心功能之外,包括文件系统驱动程序等等一切都作为了内核模块存在,而内核模块他的模块文件通常都叫做 xxx.ko(kernel object),所以他叫内核模块也叫内核对象。因此我们linux操作系统之上的对象类文件有两种,第一种叫so(shared object,共享对象),供各种程序之间共享的功能模块。第二种ko就是供内核模块装载和卸载的内核对象。他们站在内核的角度来讲没有共享的概念,只是作为功能的实现我们到底要还是不要而实现的。
e、Linux内核的组成部分(站在编译好的视角)
由核心文件和模块文件组成,这两种文件都存在时我们才可以把其称作为一个完整的内核,那么这些文件到底都在什么地方呢?
首先我们可以用uname -r命令可以看到我们的内核版本
[root@node1 ~]# uname -r 3.10.0-693.el7.x86_64
(1)、核心文件就在/boot目录下:/voot/vmlinuz-VERSION-release
[root@node1 ~]# ls -ld /boot/vmlinuz-3.10.0-693.el7.x86_64 -rwxr-xr-x. 1 root root 5877760 Aug 23 2017 /boot/vmlinuz-3.10.0-693.el7.x86_64
内核官方站点为:www.kernel.org
(2)、模块文件在/lib/modules
[root@node1 ~]# ls -ld /lib/modules/3.10.0-693.el7.x86_64/ drwxr-xr-x. 7 root root 4096 Jun 4 2019 /lib/modules/3.10.0-693.el7.x86_64/
[root@node1 ~]# ls -l /lib/modules/3.10.0-693.el7.x86_64/kernel/ total 16 drwxr-xr-x. 3 root root 17 Jun 4 2019 arch #与平台相关的特有代码,如果是X86系列等都会有自身的专有代码 drwxr-xr-x. 3 root root 4096 Jun 4 2019 crypto #加密解密组件 drwxr-xr-x. 69 root root 4096 Jun 4 2019 drivers #驱动 drwxr-xr-x. 26 root root 4096 Jun 4 2019 fs #文件系统 drwxr-xr-x. 3 root root 19 Jun 4 2019 kernel drwxr-xr-x. 4 root root 249 Jun 4 2019 lib drwxr-xr-x. 2 root root 35 Jun 4 2019 mm #内存管理功能 drwxr-xr-x. 33 root root 4096 Jun 4 2019 net #网络功能 drwxr-xr-x. 11 root root 162 Jun 4 2019 sound #声音相关的驱动程序 drwxr-xr-x. 3 root root 17 Jun 4 2019 virt
(3)、事实上我们涉及到内核组成部分的远不止这两点。我们内核启动以后,我们内核被组织成核心文件和模块,而他众多模块当中有一个子系统叫做驱动程序,于是就有了这样一个问题,如果说在我们当前主机上内核已经被装载至内存中,并且已经运行了起来,我们说过内核启动起来以后最重要的事情就是去装载根文件系统,然后去读取根上的init,然后由init负责管理人间的所有一切俗事,然后内核就退居到幕后处理一切特权级操作,现在问题是他要装载根的话他要不要把根这个文件系统所在的磁盘设备的驱动给装载进来?因此要装载根得装载磁盘的驱动程序,那么请问驱动程序在什么地方呢?在/lib/modules目录下,而/lib/modules目录又在根文件系统上,因此要想加载根文件系统你得先装载驱动,而驱动又在根文件系统上,那这事要怎么玩呢?因此我们这个时候一定不能依赖于这个真正的根上的驱动设备中的这个驱动程序,这个驱动程序内核得自带,那么这个驱动程序到底是怎么自带的呢?直接编译进内核么?所以说我能直接编译进内核,但是又有一个问题,如果这个系统是为个人使用的话肯定容易,因为硬盘接口只有一种类型,但如果你是操作系统发行商,最终能够去安装并使用这个操作系统的用户他的硬盘是各种各样类型的,这就意味着我们就得把市面上所有的硬盘的常见驱动都得编译进内核,而这些驱动加起来可能有几十兆甚至上百兆,但是我们用到的可能只有几兆,因为我们对每一个用户能 用到的只有一个,因此我们现在面临的问题是我们要面对所有用户但是我们所有的用户能用到的只有其中一个,这时候我们就需要借助于中间临时文件系统来实现,也就是说我们可以为每一个用户准备一个特定的中间文件,这个中间文件里面没有太多东西,除了必要去加载这个真正的根设备所在的这个设备驱动程序,而这个驱动程序放在这个中间文件里,中间文件放在了一个由基本设备就能访问的地方,这个时候我们就需要这个基本设备的,这个设备也是一个独立而完整的根文件系统,所以内核启动起来以后,去负责加载一个中间的假的临时的根文件系统,他所扮演的角色也不过是能够从里面装载真正根所在这个设备的驱动程序的。那么这个临时文件从哪儿来的呢?他里面是放当前这个磁盘设备专有的驱动程序呢还是所有驱动呢?其实其中只放了一个,并且是特定的,因此这个文件其实不是操作系统发行商直接在光盘中自带的,而是在你安装操作系统后才临时生成的,在安装操作系统后他能扫描你当前主机硬盘设备的型号,并找到相关的驱动做成这么一个临时的文件,所以他是动态创建生成出来的,所以他只适用于当前主机的当前设备,至于是如何创建出来的,这个你不要管那么多,而这个文件我们通常称之为用于实现系统初始化的基于内存的磁盘设备,我们通常称之为:ramdisk即把内存当磁盘用,当然它只把内存中的某一段空间当磁盘用,而不是整个内存。为什么要当磁盘用呢?因为我们操作系统起来以后我们需要将这段内容给装载到内存里,然后让内核把它当做是磁盘来使用,从而把它识别为是一个根文件系统,他里面有/bin,/sbin,/lib等,关键在于在/lib/modules目录下有当前真正的这个当前设备磁盘的驱动程序,从而借助于他真正能够把这个/给挂载上来,那么我们的临时根已经存在了我们真正的根怎么办呢?我们挂载真正的根时已经有一个根文件系统存在了,这个时候这个临时根一旦挂载根文件系统以后他就会让位,从而这个临时根他的生命也就结束了。那么我们这个临时根是必要的么?因为没有他我们就没法加载驱动,但是如果我们当前主机已经明确知道驱动是什么了,并且我直接自己编译内核,并且已经把当前主机上的这个驱动给装载进来了,那么这个临时根就可以不用了,所以他不是必须的。因此是否需要这个临时根就看你内核和系统是如何制造的。
ramdisk对于CentOS5和CentOS6还是有区别的
在CentOS5上:/boot/initrd-VERSION-release.img
在CentOS6或CentOS7上:/boot/initramfs-VERSION-release.img #之所以要组织成initramfs替换掉initrd就是为了避免双缓冲和双缓存,从而使得达到提效的目的。rd表示ramdisk,而fs表示基于ram的文件系统而不是磁盘,那么他们二者之间到底有多大的区别呢?此前我们操作系统启动起来后用free来看的话我们内存空间中有一段空间被buffer和cache所占据了,如果把它当磁盘的话磁盘为了加速访问一定会有buffer和cache,但当做文件系统就未必了,这就是为什么把rd组织成ramfs的原因,早期叫rd是把内存当磁盘来用,但是他既然是磁盘就必须被缓冲和缓存,所以这带来的结果就是,本来在内存中的数据为了加速它又在内存中再去缓冲和缓存一份,是没必要的,内存中的数据本来就在内存中,为什么磁盘中的数据会有buffer和cache呢?因为磁盘太慢了,CPU太快了,所以我们把磁盘中的数据先临时加载至内存中来访问,这样使得访问时可以快一点,但是本来磁盘就是内存,我们还需要再缓存一次么?肯定是没必要的。
[root@node1 ~]# ls -ld /boot/initramfs-3.10.0-693.el7.x86_64.img -rw-------. 1 root root 20943838 Jun 4 2019 /boot/initramfs-3.10.0-693.el7.x86_64.img
二、CentOS系统的启动流程(以下介绍仅适用于PC架构的主机,并且只适用于MBR类型的架构而不适用于EFI等架构的设计)
1、POST:加电自检
a、对于X86系列的服务器来讲,MBR类型的来讲,我们按下电源开机以后会想方设法的检查一下内存,硬盘等是否存在,这个过程我们称为POST(加电自检)过程,实现POST代码的地方是在主板上,我们主板上一般而言有一个ROM程序,这个ROM程序一般而言是只读的,他是存放在我们ROM芯片上的。如果没有外部手段进行干涉的话他是没办法修改的。而X86平台的CPU被设计成为,一旦通了电你不需要做任何的额外程序他会自动的去找当前主板上的某一个存储位置中的某一个地方,能去读取里面的程序并运行,而这段程序就是刚刚所提到的ROM程序。这个自动去找的这个功能是CPU硬件设计时就植入的,找到ROM程序后我们会干什么呢?就是来检查我们各个硬件设备是否存在的。
b、ROM程序中最有代表性的就是CMOS(互补金属氧化物),CMOS中有个基本输入输出系统叫做BIOS(Basic Input and Output System),他也是固化在我们当前主机主板上的ROM芯片中的代码,其实我们整个操作系统中的可被CPU访问的存储空间可线性寻址的物理存储空间有两部分组成:ROM + RAM。也就是说我们CPU可访问的物理空间不光有物理内存(RAM),还有ROM芯片空间。因为他要通过ROM去加载自检程序。
2、Boot Sequence:引导过程
a、一旦加电自检完成了接下来就到了我们的引导过程,说到底就是到何种设备上去加载我们的操作系统,比如以光盘启动,U盘启动,硬盘启动等,也就是按次序查找各引导设备,这个次序是我们定义好了的,第一个有引导程序的设备即为本次启动要用到的设备。
b、bootloader:而引导程序就叫做bootloader,也就是引导加载器,或者是称为启动加载器,这是一个程序,是我们安装在硬盘或光盘或U盘上的程序
Windows:这个引导加载器对Windows来讲叫做ntloader。
Linux:
LILO:对Linux而言这个引导加载器有很多种实现,最早叫做LILO(Linux LOader),但是早期研发LILO的时候没有考虑太多,使得LILO无法加载大硬盘,你的内核或者加载的系统如果位于1024柱面以后的分区上他加载不了,因为那时候硬盘很小,柱面也没有那么多,但是后来现在的硬盘动辄几万个柱面,而不仅仅是1024个柱面的问题,因此在那个时代以后LILO就被后继者GRUB取代了。因此现在LILO已经很少见了,但是又说LILO是最常用的是因为在安卓手机上就用的是LILO。因为其底层跑的都是Linux内核,在Linux内核之上跑了一个JAVA虚拟机,在JAVA虚拟机之上跑了一个安卓程序,所以安卓程序是JAVA代码写的。
GRUB(Grand Uniform Bootloader):统一引导加载器,对于GRUB而言他有两个版本,对于CentOS5和CentOS6来讲他用的是GRUB 0.X的版本,但是对于CentOS7而言他用的是GRUB 1.X的版本,虽然这只是两个版本的差异,但事实上1.X是完全重写的,他和0.X只是看起来很像,但是二者之间有着巨大的差异的。因此0.X的版本而言后来就被命名为Grub Legacy(Grub 传统版),现在1.X的版本就被称作为Grub2。
c、那么这个引导程序有什么用呢?他主要是提供一个菜单,允许用户选择要启动的系统或不同的内核版本,然后把用户选定的内核装载到内存(RAM)的特定空间中,然后进行解压,展开,而后把系统的控制权移交给内核;这个时候内核就成为了整个操作系统中拥有至高无上管理权限的程序。
d、那么bootloader到底装到哪儿呢?对于MBR类的程序他是装在MBR中的,但是EFI或者UEFI的除外,因为UEFI出现的时间不短了并且也用的更多了,这个下去了解,顺便了解什么是GPT。他们是MBR设计的新版本,专门设计出来用来取代MBR的设计体系架构,是由inter牵头研发的。
e、MBR:Master Boot Record,即主引导记录
(1)、对于我们MBR来讲,MBR一共是512字节,他是硬盘的最前面的一个扇区,这512字节中前446字节放的是bootloader,也就是我们的LILO或者GRUB。而后的64个字节是我们的分区表。也就是文件系统分区表。最后的两个字节是四个十六进制数字:55AA,如果是55AA表示MBR是有效的,bootloader是有效的,如果不是55AA就表示其无效。我们发现bootloader一共是446个字节,英文字符中一个字符就是一个字节,那么446个字节就表示写一段代码不能超过446个字符,因此这段空间太小了,所以这个设计需要非常精巧才可以。后来GRUB程序员又发明了一种非常精巧的设计方式,因为我们想表现的给用户提供的菜单还能有图片,还能有菜单还要有其它强大的功能,因此在这446个字节几乎是不可能实现的。
512bytes:
446bytes:bootloader
64bytes:fat(文件系统分区表)
2bytes:55AA
(2)、因此GRUB就把它的程序分成了两段,所以GRUB有两个阶段:
1)、1st stage:bootloader,第一阶段是放在bootloader中的,我们称之为1st stage阶段
2)、2nd stage:Partition,第二阶段是放在磁盘的分区上的,也就是/boot分区上,在这个分区上有个/boot/grub目录,这个被称为2nd stage阶段
那么他们是什么意思呢?一般来讲,我们说过bootloader最主要的目的是提供菜单和把用户选定的内核加载到内存中,但是有了GRUB以后这个功能被打破了,他采用了另外的一种方式,本来我们bootloader第一步是加载内核到内存进行解压等操作,但是对于GRUB来讲我们现在第一步的目的不是加载内核而是加载它的第二阶段,他的第二阶段也就在磁盘上,把第二阶段加载到内存中,而第二阶段在磁盘分区上这段空间就不受限制了,因此把这段代码加载后就可以在操作系统启动之前启动一个操作系统(或者说是特定程序),这个程序不受MBR的限定,因此他可以实现非常复杂的功能,他可以提供一个背景图,提供一个菜单,甚至是提供一个交互式接口给我们,于是这不能
以为说我们就一定万事大吉了,因为最终你还是要启动操作系统,因此当我们选定了操作系统以后由第二阶段再去加载内核,所以他把整个过程分成了三阶段而不是两阶段,不是由bootloader直接加载内核,而是由bootloader先加载他的第二阶段,由其第二阶段提供一个非常丰富功能的接口以后由这个第二阶段来加载内核文件,所以他把它做成了三段化,但是其实不仅仅是三段化,严格意义上来讲他是做成了四段话,因为在这个过程中还有一个隐藏阶段,这个隐藏阶段也在磁盘分区上,他叫做文件系统驱动接口。也叫文件系统驱动(filesystem driver)。这个驱动我们称作1.5阶段,那么这第1.5阶段有什么用呢?这就涉及到一个非常复杂的问题了,后面我们来展开专门讲GRUB的时候再来细细的描述。
f、假设说我们GRUB装好了,我们操作系统启动已经加电自检完成了,也找到了MBR,在MBR中它现在也从第一阶段加载至第二阶段,而且第二阶段我们也已经选定了用户要启动的某一个内核,那么下一步我们就开始加载内核
3、Kernel阶段
a、对于Kernel阶段来讲,一旦GRUB将其装载到内存解压并展开之后Kernel就可以完成后续的一些功能了,这些功能主要是进行自身初始化,相当于他要开始接管这个花花江山了,并且要接管这个皇宫,因此他要去事先了解一下拿一拿账目
b、因此接下来的步骤如下:
(1)、第一步他要去探测可识别到的所有硬件设备;
(2)、接下来他会加载硬件驱动程序,有可能会借助于其它ramdisk加载驱动,
(3)、接下来以只读方式挂载根文件系统,以只读的原因是因为万一内核有BUG那么我们的根文件系统就翻车了;
(4)、运行用户空间的第一个应用程序:/sbin/init
init程序的类型:
CentOS 5和之前:SysV init
配置文件:/etc/inittab
CentOS 6:Upstart
配置文件:/etc/inittab(在CentOS 6中这个文件其实是没用的,但是CentOS6为了能够兼容CentOS 5他也一样提供了这个配置文件并通过此配置文件来决定系统运行级别这个概念)
/etc/init/*.conf
CentOS 7:Systemd
配置文件:/usr/lib/systemd/system/*,/etc/systemd/system/*
(5)、一旦启动了init内核就不管了,所有的后续操作都是由init加载运行用户空间的应用程序来完成各种各样的工作,只有当这一应用程序一般要完成模式切换或者我们系统发生中断时,这些需要特权级操作内核才会参与。
c、ramdisk:我们内核在初始化时其中有一个步骤有可能加载硬件驱动时有可能加载真正根文件系统所在设备的驱动程序,而这个设备的驱动程序加载有可能会用到ramdisk,而我们ramdisk这个程序其实并不是操作系统发行版上直接提供的而是在安装系统完成以后动态创建探测当前硬件设备然后自动创建的。
对ramdisk而言他也是linux的内核特性之一:使用缓冲和缓存来加速对磁盘上的文件访问,因此我们在free命令中经常看到大量的buffer和cache所使用的磁盘空间,但是这里假设的前提是硬盘是真正的慢速或低速的机械式硬盘,就算是固态硬盘(只要不是PCI-E接口的)也会比内存慢的多的多,所以为了能保证磁盘上经常被访问的数据足够快速因此我们需要给他做缓冲和缓存,但是ramdisk是把内存当磁盘来用了,所以这样得到的结果就是我们还得把内存上磁盘中的数据再缓存一次,这样会导致双缓冲,都在内存中的话这样是没什么用的,因此我们需要把ramdisk换成ramfs,一旦他是一个文件系统就不需要再次缓冲了,因此这是一种提速机制。所以我们说过在CentOS5上用的是initrd,而在CentOS 6和7上是initramfs,而这两类文件都是可以通过工具创建的,在操作系统安装完以后有工具自动创建的。
CentOS 5:initrd
创建工具:mkinitrd
CentOS 6和7:initramfs
创建工具:dracut,为了向下兼容他也可以用mkinitrd工具,不过他也是dracut封装创建的
二、系统初始化流程总结(内核级别)
1、POST (加电自检)--> BootSequence(读取bios中的BootSequence从而决定哪一个磁盘设备是我们接下来的引导加载次序中的设备)--> BootLoader(读相应的BootLoader,对于MBR来讲一般是放在MBR中的,但是BootLoader一般是无法驱动那些高级软设备的,比如LVM,也驱动不了软RAID,这使得你的内核文件一定不能够放在逻辑卷上,只能放在基本磁盘分区上,因此BootLoader代码很小,为了简介起见使得BootLoader本身无法驱动逻辑卷,很显然他就没法访问逻辑卷上的文件,此时你的Kernel只能放在基本磁盘分区上) --> Kernel(加载内核,即从磁盘分区上读取内核,因此BootLoader这个程序要和你的磁盘分区打交道,因为你的内核是放在某个分区上的,因为有时候内核无法加载真正根文件系统所在设备驱动因此有时候需要借助ramdisk来完成真正根文件系统设备驱动程序的加载) -->rootfs( 接下来内核会将真正的根文件系统所在的设备mount到根上,因此我们随时说的根就在内核中就是这个道理。假设我们根是sda3此时内核就会把sda3自动mount到根上。假设我们sda3上有bin,sbin,lib等等文件夹,他们本来没有根目录,mount后就有了/bin,/sbin/,/lib等目录。但是为了防止内核中有BUG而使文件系统损坏我们此时采用的是只读的方式加载的) --> /sbin/init(然后内核直接就来找/sbin/init程序,即开始运行用户空间中的第一个程序,所以这个程序要干什么事,怎么做,要取决于init程序的配置文件要怎么做)
三、接下来我们来说一下我们系统空间级别的引导流程
1、接下来我们首先来描述一下CentOS 5的init这个程序的触发过程
a、CentOS 5:SysV init我们来描述一下他要做什么
b、在这之前我们先来描述一下运行级别的概念,那么运行级别是什么呢?比如windows的安全模式,假如你安装了某个显卡驱动然后一启动电脑就黑屏,此时你不用重装系统,可以进入安全模式,安全模式的情况下其实windows是使用的系统自带的很多驱动,这样就能进入系统了,相当于是以维护模式的形式进入系统。因此运行级别的设定是为了系统的运行或维护等目的而设定的机制;
c、运行级别一共有7个
0:关机,shutdown,init 0
1:单用户模式(single user),root用户,无需认证;维护模式,万一root密码忘了的话直接启动1级别他就会绕过认证而且直接以root用户登录,此时直接改密码就可以了
2:多用户模式(mutli user),带网络功能的的安全模式,但是不会启动NFS,相当于也是一种维护模式
3:多用户模式(mutli user),完全功能模式,但是只是文本界面不会启动图形界面
4:预留级别,目前无特别使用目的,但习惯同3级别功能使用
5:多用户模式(mutli user),完全功能模式,但是会启动图形界面
6:重启,reboot
默认级别:3,5
级别切换:init #
[root@node2 ~]# init 3
级别查看:
who -r命令
[root@node2 ~]# who -r run-level 3 2020-03-18 01:29
也可以使用专用命令runlevel
[root@node2 ~]# runlevel N 3
前面表示上一个运行级别,N表示没级别,这个3表示当前级别
d、整个init要运行起来大体要通过其配置文件来决定要做什么,对CentOS5来讲是/etc/inittab,他决定了我们我们整个init在初始化时要做哪些事,并且在传统的CentOS 5中的这个inittab中的定义是每一行一种操作,这种操作就是为了启动后台各种服务,能让操作系统是一个完整的系统并打印一个登陆提示符给用户从而用户能登陆的,如果是启动图形界面的话图形界面也是init来激活的。那么init要启动什么级别的界面都得通过其配置文件来定义,具体方式我们下节再讲。