Centos系统启动流程:
预备知识:
Linux系统的组成部分: 内核+根文件系统
内核的功能: 进程管理、内存管理、网络协议栈、文件驱动、驱动程序
IPC: Inter Process Communication
消息队列: semerphor、shm
socket
运行中的系统环境可分为两层: 内核空间、用户空间
用户空间: 应用程序(进程或线程)
内核空间: 内核代码(系统调用)
内核设计流派:
单内核设计: 把所有功能集成于同一个程序
包含: Linux
微内核设计: 每种功能使用一个单独的子系统实现
包含: windows、solaris
Notice: 虽然说单内核设计没有微内核设计先进,但是微内核设计为了协调各个模块,在效率上却没有单内核上高。
Notice: Linux虽然是单内核模块,单同时吸取了一定的微内核设计的有点(模块化)
Linux内核特点:
支持模块化: XXX.ko 文件(kernel object)
支持模块运行时动态装载和卸载
组成部分:
核心文件: /boot/vmlinuz-VERSION-release
ramdisk:
CentOS5: /boot/initrd-VERSION-release.img
CentOS6,7: /boot/initramfs-VERSION-release.img
模块文件: /lib/modules/VERSION-release
Notice: 红帽系列的产品,只要主版本号更换,那么使用的内核就都是一样的
Notice: release是编译内核的人在后期编译完后手动加进去的
启动分区: /boot
rootfs: FHS(/bin、/sbin、/lib、/etc、/proc、/sys、/dev、/boot)
关键文件: init(用于引导整个根文件系统(rootfs)的启动)
ramdisk:
Linux核心特性之一: 使用缓冲和缓存来加速磁盘上的文件访问
当内核启动后,需要去读取硬盘上的根文件系统,但是此时因为只加载了内核,没有对应的硬盘驱动,显然是无法读取根文件系统的,这个时候就需要借助ramdisk,ramdisk是一个假的系统,里面存放了一个系统应有的东西(包括/bin、/sbin等等),ramdisk会被加载到内存当中,然后内核此时就可以通过ramdisk去读取到对于的驱动,从而加载整个硬盘,当内核读取完驱动后就会把ramdisk干掉。(ramdisk会自己关闭,但是可能会关闭不全)
市面上的硬件各种各样,因此为了兼容需要非常多种的驱动,显然如果把这些驱动通通编译进我们使用的系统是很不理想的,虽然解决了硬件种类繁多的问题,但是我们为了使用一个驱动而编译了一大批的驱动是糟糕的,因此为了解决这个问题,各大种类的硬盘驱动是存放在安装盘里的,安装盘会自动扫描需要的硬盘驱动,然后仅仅安装了该硬盘对应的驱动在系统中。
我们可以发现ramdisk是因为内核无法读取到安装在硬盘中的硬盘驱动而产生的一个中间物品,所以如果把硬盘驱动直接编译进了内核,就不在需要ramdisk了
CentOS 5: initrd
工具程序: mkinitrd
CentOS 6, 7: initramfs
工具程序: dracut, mkinitrd
ramdisk的核心原理: 在内存上虚拟出硬盘,这样就把该段内存当做硬盘使用,当然如果断电,这些数据是会消失的。因此访问ramdisk中硬盘的速度是和访问内存的速度是一样的,这就引出了CentOS5中采用initrd的一个缺点: CentOS 5中采用的ramdisk和我们平常使用硬盘的方式模拟的一模一样,而我们平常在使用硬盘的时候,为了提高访问硬盘的速度,就会在内存中增加缓存,但是如果是在ramdisk中明显是完全没有必要的,因为ramdisk的硬盘本来就是用内存模拟的,所以它的速度本来就跟内存一样快,所以根本没有必要在ramdisk的硬盘和内存之间增加缓存,这将会大大的浪费一段内存空间,所以在CentOS6和CentOS7中采用了initramfs,ramdisk中就不再有这一段缓存了。
Centos系统启动流程:
POST --> BootSequence(BIOS) --> BootLoader(MBR) --> Kernel --(可能需要借助ramdisk,具体需要看内核是否是自己编译的)--> rootft(先以只读的方式打开文件系统,以防万一文件系统中有漏洞等等)--->/sbin/init -->设置默认运行级别 --> 运行系统初始化脚本,完成系统初始化 --> 关闭对于级别下需要停止的服务,启动对于级别下需要开启的服务 --> 设置登陆终端 -->可能会启动图形界面
1. POST: 加电自检
检查各硬件是否OK,例如检查内存是否插好等等。
ROM: CMOS
BIOS: Basic Input and Output System
ROM: 只读存储器
在CPU所能读取到的整个内存包含了ROM和RAM(主板上的ROM显示器上的ROM等等会被CPU当做和RAM一起的内存)
ROM是只读存储器,CPU只能对其进行读取,而不能往里面写数据,例如BISO就存储在主板的RAM中。
2. Boot Sequence
按次序查找歌引导设备,第一个有引导程序的设备即为本次启动要用到的设备
bootloader: 引导加载器,程序
windows: ntloader
Linux:
LILO--->Linux Loader(安卓手机使用的就是LILO)
GRUB: Grand Uniform Bootloader
GRUB 0.x
GRUB 1.x
> Notice: GRUB 1.x和GRUB 0.x是两个完全不一样的版本,GRUB 1.x中,整个GRUB进行了重新的编写,故Grub
> 0.x有称为Grub Legacy。而Grub 1.x又称为grub2
MBR: Master Boot Record
UEFI、GPT
3. kernel
自身初始化:
探测可识别到的所有硬件驱动
加载硬件驱动程序; (有可能会借助于ramdisk加载驱动)
以制度方式挂载根文件系统
运行用户空间的第一个应用程序: /sbin/init
CentOS7为/sbin/systemd
init程序类型:
CentOS 5: SysV init
配置文件: /etc/inittab
CentOS 6: Upstart
配置文件: /etc/inittab
/etc/init/*.conf
CentOS 7: Systemd
配置文件: /usr/lib/systemd/system
/etc/systemd/system
一些细碎的知识点:
如何查看内核的版本:
1. 使用命令: uname -r
2. 查看 /lib/modules/ 目录下的目录的名字名
[root@localhost ~]# ls /lib/modules/
2.6.18-308.el5
[root@localhost ~]#
内核查看示例:
[root@localhost ~]# cd /lib/modules/2.6.18-308.el5/
[root@localhost 2.6.18-308.el5]# ls
build kernel modules.ccwmap modules.ieee1394map modules.isapnpmap modules.pcimap modules.symbols source weak-updates
extra modules.alias modules.dep modules.inputmap modules.ofmap modules.seriomap modules.usbmap updates
[root@localhost 2.6.18-308.el5]# cd kernel/
[root@localhost kernel]# ls
arch crypto drivers fs lib net sound
[root@localhost kernel]# cd fs/
[root@localhost fs]# ls
autofs4 cifs cramfs ecryptfs ext3 fat fscache gfs2 hfsplus jbd2 lockd nfs nfsd squashfs vfat
cachefiles configfs dlm exportfs ext4 freevxfs fuse hfs jbd jffs2 msdos nfs_common nls udf xfs
[root@localhost fs]#
// 在这里可以查看到内核所支持的文件系统,Notice: 虽然只显示了ext3,但是其已经兼容了ext2
系统初始化流程(内核级别):
POST --> BootSequence(BIOS) --> BootLoader(MBR) --> Kernel --(可能需要借助ramdisk,具体需要看内核是否是自己编译的)--> rootft(先以只读的方式打开文件系统,以防万一文件系统中有漏洞等等)--->/sbin/init(用户空间的开始创建,详细见下面)
/sbin/init:
CentOS 5: SysV init
运行级别: 为了系统的运行或维护等目的而设定的机制; 用来拯救你的系统,例如: 如果新装的显卡驱动无法使用,我们就可以进入指定的运行级别来减少启动新装的显卡驱动,而让其使用自带的显卡驱动的方式进入系统。然后进入后在对系统进行修复。 既: 当系统如果出现某种意外的话,我们可以通过运行级别来减少电脑开启的功能,从而达到进入系统来拯救你的电脑。就像是windows中的安全模式一样。
0-6 : 7个运行级别
0: 关机(shutdown)
1: 单用户模式(single user),既: root用户模式(维护模式),这种模式下进入root用户是无需密码认证的,所以如果密码忘了,可以进入该模式
2: 多用户模式(multi user),会启动忘了功能,但不会启动NFS; 维护模式
NFS: network file system,网络文件系统,可以远程访问目标主机的文件,例如windows中的共享文件
3: 多用户模式(mutil user),完全功能模式,文本界面(如果你因为驱动而启动不了桌面环境,就可以切换到这里,哈哈,就是上面那个问题)
4: 预留级别: 目前无特别适宜目的,但习惯以3同级别功能使用
5: 多用户模式(multi user),完全功能模式,图形界面
6: 重启,reboot
默认级别: 3或5
级别切换: init #
#表示0-6
级别查看: who -r
runlevel
使用示例:
[root@localhost ~]# runlevel
N 3
// 第一个数字为上一次的启动级别,第二个数字为目前的启动级别
// N表示没有启动级别,既: 3就是第一次的启动级别,如果是who -r表示的为S
从CentOS 5开始(#^.^#)
CentOS 5的init配置文件: /etc/inittab
…… ……
id:3:initdefault:
# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit
l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6
…… ……
文件说明:
每行定义一种action以及与之对应的process
id:runlevels:action:process
id: 一个任务的表示符,可以随意取名,但是一定要唯一
runlevels: 在哪些运行级别下启动此任务; 可以有的格式: 一个数字,或者多个数字,也可以为空,表示所有级别
示例: 1表示在运行级别1启动,12表示在运行级别1和运行级别2驱动,如果什么都没有写,例如: si::sysinit:/etc/rc.d/rc.sysinit则表示在所有的级别都会启动。
action: 在声明条件下启动此任务,关于action的条件请往后看
process: 启动的任务,命令,例如: s1:3:wait:/sbin/shutdown -h now,这样一来当以启动命令行界面的时候,系统就会关机
action:
wait 等待切换至此任务所在的级别时启动一次
respawn 一旦任务终止,就重新启动之
initdefault 设定默认运行级别,此时process省略
sysinit 设定系统初始化方式,此处一般指定为/etc/rc.d/rc.sysinit脚本
示例说明:
id:3:initdefault:
表示系统的默认运行级别为3,既: 开机时进入文件界面,如果是 id:5:initdefault: 则表示开机进入图形界面
si::sysinit:/etc/rc.d/rc.sysinit
表示系统初始化的时候去执行/etc/rc.d/rc.sysinit脚本
sysinit的部分内容解释:
if [ -f /etc/sysconfig/network ]; then
. /etc/sysconfig/network ----------> 启动网络
fi
if [ -z "$HOSTNAME" -o "$HOSTNAME" = "(none)" ]; then
HOSTNAME=localhost ---------> 设置主机名
fi
l3:3:wait:/etc/rc.d/rc 3
表示进入3级别的时候,去执行脚本/etc/rc.d/rc 3
/etc/rc.d/rc部分内容解释:
> # Now find out what the current and what the previous runlevel are. argv1="$1" ---> /* 将传递进来的第一个参数给了argv1* /
> set `/sbin/runlevel`
> …… ……
> # Get first argument. Set new runlevel to this argument. [ -n "$argv1" ] && runlevel="$argv1" --->/*判断是argv1字符串的长度是否非0,如果argv1有长度(既: 有argv1字符),就让runlevel=argv1,从上面可以得知: argv1=传递进来的第一个参数,所以rc该脚本接受一个参数,该参数为运行级别runlevel*/
> …… ……
> # First, run the KILL scripts. for i in /etc/rc$runlevel.d/K* ; do /* 这个for循环把rc#.d下的所有K开头的文件(程序)都停止了 */
> …… ……
> $i stop --> /*核心代码*/
> …… ……
> done
>
> …… ……
> # Now run the START scripts. for i in /etc/rc$runlevel.d/S* ; do /* 这个for循环把rc#.d下的所有S开头的文件(程序)都开启了 */
> …… ……
> $i start --> /* 核心代码 */
> …… ……
> done
Notice: #表示运行级别,例如: rc3.d表示运行级别为3的文件
总结: /etc/rc.d/rc 该文件需要传递一个参数(运行级别),而后rc就会关闭/etc/rc$runlevel.d/下K开头的所有文件,开启/etc/rc$runlevel.d/下S开头的所有文件。
// 查看/etc/rc3.d下的文件
> [root@localhost ~]# ll /etc/rc3.d/
> …… ……
> lrwxrwxrwx 1 root root 24 Mar 17 02:37 K02avahi-dnsconfd -> ../init.d/avahi-dnsconfd lrwxrwxrwx
> 1 root root 24 Mar 17 02:39 K02NetworkManager -> ../init.d/NetworkManager
> …… ……
> lrwxrwxrwx 1 root root 22 Mar 17 02:38 S97yum-updatesd -> ../init.d/yum-updatesd lrwxrwxrwx 1 root root 22
> Mar 17 02:37 S98avahi-daemon -> ../init.d/avahi-daemon
> …… ……
> [root@localhost ~]#
/*
* 可以发现所有的文件都以S#或者,K#开头,去除S#和K#后才是它们本来的文件名
* Notice: #表示一个数字
*
* 因为rc该脚本是利用for循环启动S开头的文件,关闭K开头的文件,所以S开头的文件中数字越小的越早被开启,K开头的文件中数字越小的越早被关闭。
* 从这一特性可以分析出: 被依赖开启的服务需要让S#的数字小一点,而依赖他人的服务需要S#的数字大一点,关闭的时候就反过来,被依赖的服务需要让K#的数字大一点,从而晚点关闭,而依赖他人的服务需要K#的数字小一点,先关闭
*
* 通过ll命令可以发现所有的原文件指向的是/etc/init.d/目录下的文件,所以可以通过编写脚本存放在/etc/init.d来手动开启某些开机服务
*/
示例: 编写一个开启启动的脚本
预备知识:
// 查看/etc/init.d/下任意一个文件,可以发现一定有以下内容
#!/bin/bash ---> 这是脚本的固定格式
#
# /etc/rc.d/init.d/acpid ---> 脚本的名称,不一定要有
#
# Starts the acpi daemon ---> 脚本功能的简要说明,不一定要有
#
# chkconfig: 345 26 74 --->chkconfig要读取的内容,一定要有
# description: Listen and dispatch ACPI events from the kernel ---> 脚本功能的消息说明,不一定要有
# processname: acpid ---> 进程名字,不一定要有
…… ……
chkconfig的详解:
chkconfig: # # #
第一个数字表示在哪些级别,该任务需要开启,剩下的所有级别将会关闭,如果是 - 表示所有级别下都是关闭状态(示例: chkconfig: - 26 74)
第二个数字为S开头的文件所设置的数字大小,可以查看 /etc/rc3.d/下有个S26acpid文件
第三个数字为K开头的文件所这是的数字大小
开始编写: (编写一个开机会显示一句话的脚本)
1. 在/etc/init.d/下编写脚本
[root@localhost init.d]# vim myscript // 小贴士: 末尾没有带.sh的文件,vim不会有语法高亮显示,可以在编写完#!/bin/bash后保存关闭,再重新用vim进入就可以看到高亮的语法显示了
> 脚本内容:
#!/bin/bash
#
# /etc/init.d/myscript
#
# My Test Script, can echo some world when start computer
#
# 在哪些级别开启 S文件数字 K文件数字
# chkconfig: 234 01 99
# description: 这是一个自己写的测试服务脚本
# 获取本脚本的名字
prog=$(basename $0)
# 如果没有传递任何参数,就显示下面的使用帮助信息
if [ $# -lt 1 ]; then
echo "Usage: $prog {start|stop|status|restart}"
exit 1
fi
# 根据传递进来的参数,打印出对应的话
if [ "$1" == "start" ]; then
echo "welcome to use $prog"
elif [ "$1" == "stop" ]; then
echo "Stop $prog finish"
elif [ "$1" == "status" ]; then
if pidof $prog &> /dev/null; then
echo "$prog is running"
else
echo "$prog is stoped"
fi
elif [ "$1" == "restart" ]; then
echo "$prog restart finish"
else
echo "Usage: $prog {start|stop|status|restart}"
exit 2
fi
2. 给该脚本添加执行权限
[root@localhost init.d]# chmod +x myscript
3. 把该脚本添加给chkconfig管理,关于chkconfig的命令,往下查看
[root@localhost init.d]# chkconfig --add myscript
[root@localhost init.d]#
4. 可以查看我们自己写的myscript服务,可以发现只有234级别时开启的,跟我们在脚本中用hkconfig配置的一样
[root@localhost init.d]# chkconfig --list myscript
myscript 0:off 1:off 2:on 3:on 4:on 5:off 6:off
[root@localhost init.d]#
5. 也可以查看/etc/rc3.d/下的文件,可以发现我们的myscript文件为S01开头的文件,跟我们在脚本中用hkconfig配置的一样
[root@localhost init.d]# ls /etc/rc3.d/ | grep myscript
S01myscript
[root@localhost init.d]#
5.1 查看/etc/rc0.d/ 下的文件,因为我们设定了myscript只在234级别开启,所以在0级别下,myscript就是K文件
[root@localhost init.d]# ll /etc/rc0.d/ | grep myscript
lrwxrwxrwx 1 root root 18 Mar 17 22:16 K99myscript -> ../init.d/myscript
[root@localhost init.d]#
6. 脚本服务体验
// 可以用service来管理我们的脚本服务
[root@localhost init.d]# service myscript start
welcome to use myscript ---> 可以发现这是我们自己设定的脚本输出
[root@localhost init.d]#
/*
* 故: 启动一个服务的方式可以有两种:
# /etc/init.d/SERVER {start|stop|restart|status}
# service SERVER {start|stop|restart|status}
*/
> 这里是引用
chkconfig命令:
查看服务信息: chkconfig --list [name]
添加服务: chkconfig --add name
删除服务: chkconfig --del name
修改指定的链接类型:
chkconfig [--level LEVELS] name <on|off|reset>
如果LEVELS不写,默认为2345
Notice: 正常级别下,最后启动的一个服务S99local没有链接至/etc/init.d下的某脚本,而是链接至了/etc/rc.d/rc.local(此文件还有个链接,为: /etc/rc.local)脚本,如果说有希望写服务脚本或者说有希望开机启动的程序,可以直接放置在此脚本中(每个命令单独成行)
/etc/inittab文件的其它内容:
…… ……
# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6
…… ……
(1)mingetty命令用户启动终端,因此,如果删除上述的一行命令,那么我们能切换的中断将减少,从上述内容中就可以看出为什么我们可以启用F1-F6,6个虚拟终端。
(2)mingetty会调用login程序,该程序可以显示登陆信息
(3)打开虚拟中断的程序除了mingetty还可以有getty等
系统初始化脚本/etc/rc.d/rc.sysinit所能完成的其它功能:
(1)设置主机名
(2)设置欢迎信息
(3)激活udev和selinux
(4)挂载/etc/fstab文件中定义的所有文件系统
(5)检测根文件系统,并以读写方式重新挂载根文件系统(之前只以只读的方式挂载跟文件系统,这次当检测完根文件系统没有问题后就会重新挂载)
(6)设置系统时钟
(7)根据/etc/sysctl.conf文件来设置内核参数
(8)激活lvm及软raid设备
(9)激活swap设备
(10)加载额外设备的驱动
(11)清理操作
总结: (用户空间的启动流程): /sbin/init(/etc/inittab)
设置默认运行级别 --> 运行系统初始化脚本,完成系统初始化 --> 关闭对于级别下需要停止的服务,启动对于级别下需要开启的服务 --> 设置登陆终端 -->
可能会启动图形界面,具体需要看是如何设定的
CentOS 6
init程序; upstart,但依然为/sbin/init配置文件,但是不同类型的内容,被拆分成了不同文件
/etc/init/*.conf
为了兼容CentOS 5,CentOS 6下依旧有/etc/inittab文件,但该文件仅仅用于定义默认运行级别
CentOS 7
init程序: systemd,配置文件: /usr/lib/systemd/system/* 和 /etc/systemd/system/*
CentOS 7完全兼容SysV脚本机制,英雌service命令依旧可以使用,不过建议使用systemctl命令来控制服务
systemd的启动机制:
以往的CentOS 5和CentOS 6中,开始是因为需要启动种种服务,因此,他们就启动的非常慢,而在CentOS 7中,所有的服务默认是不启动的,直接就开机,而当后期我们第一次去访问对应的服务时,CentOS 7才会去启动对于的服务
// 查看/etc/inittab文件,可以发现,该文件里除了注释如也
[root@node-3 ~]# cat /etc/inittab
# inittab is no longer used when using systemd.
#
# ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
#
# Ctrl-Alt-Delete is handled by /usr/lib/systemd/system/ctrl-alt-del.target
#
# systemd uses 'targets' instead of runlevels. By default, there are two main targets:
#
# multi-user.target: analogous to runlevel 3
# graphical.target: analogous to runlevel 5
#
# To view current default target, run:
# systemctl get-default
#
# To set a default target, run:
# systemctl set-default TARGET.target
#
[root@node-3 ~]#
/*
* 同时我们也可以发现systemd取消了以往的级别概念,采用了TARGET.target机制来兼容以往的级别概念
*/
// 获取目前的target
[root@node-3 ~]# systemctl get-default
multi-user.target ---> 多用户模式(一个target一种名字)
[root@node-3 ~]#
加粗样式