CentOS5、6的启动流程
     Linux:kernel + rootfs,Linux系统就是内核加上根文件系统。
 内核之上是库:
 库:函数集合,function,函数具有调用接口,库函数不能单独执行,必须被其他程序调用
     过程调用:procedure,没有返回结果
     函数调用:function,有返回结果
程序:程序调用库或系统调用,完成业务,可以单独执行。
内核设计流派:
     单内核模式:Linux,把所有功能集成于同一个程序
     微内核模式:Windows,Solaris,每种功能使用一个单独子系统实现
Linux内核特点:
     支持模块化:.ko(是内核的模块,相对的有.so,共享对象模块,是共享库的模块文件)
     支持模块的动态装载和卸载;
    组成部分:
         核心文件:/boot/vmlinuz-VERSION-release
             ramdisk:
                 CentOS 5:/boot/initrd-VERSION-release.img
                 CentOS 6:/boot/initramfs-VERSION-release.img
         模块文件:/lib/modules/VERSION-release
 ramdisk是用文件来模拟磁盘,提供最开始的根文件系统,以装载系统启动必须的驱动。        
CentOS 系统启动流程:
     POST:加电自检
         ROM:CMOS,BIOS:Basic Input and Output System
         内存是ROM+RAM组成
     BOOT sequence:按次序查找各引导设备,第一个有引导程序的设备即为本次启动用到的设备
         bootloader:引导加载器,程序
             windows:ntloader
             Linux:LILO——LInux LOader;GRUB——GRand Uniform Bootloader,GRUB有0.X版,叫做GRUB Legacy,GRUB 1.X,叫GRUB2
         bootloader功能:提供一个菜单,允许用户选择要启动系统或不同的内核版本;把用户选定的内核装载到内存中的特定空间中,解压、展开,并把系统控制权移交给内核;
     MBR:(0磁道0扇区)
         446:bootloader
         64:fat
         2:55AA
    GRUB:
         bootloader:1st stage
         disk:2nd stage
kernel:
     自身初始化:
         探测可识别到的所有硬件设备;
         加载硬件驱动程序;(有可能会借助于ramdisk加载驱动)
         以只读方式挂载根文件系统;
         运行用户空间的第一个应用程序:/sbin/init
    init程序的类型:
         SysV:init,CentOS 5
             配置文件:/etc/inittab
         Upstart:init,CentOS 6
             配置文件:/etc/inittab, /etc/init/*.conf
         Systemd:systemd,CentOS 7
             配置文件:/usr/lib/systemd/system, /etc/systemd/system
    ramdisk:
         内核中的特性之一:使用缓冲和缓存来加速对磁盘上的文件的访问
         ramdisk --> ramfs,CentOS5将ramdisk模拟成磁盘,导致内核再次缓冲
         CentOS5:initrd,工具程序:mkinitrd
         CentOS6:initramfs,工具程序:mkinitrd,dracut
系统初始化:
     POST --> BootSequence(BIOS) --> Bootloader(MBR) --> kernel(ramdisk) --> rootfs(只读) --> init
/sbin/init
     CentOS 5:
     运行级别:为了系统的运行或维护等应用目的而设定,共0-6,一个7个级别
         0 :关机
         1 :单用户模式(root,无须登录),single,维护模式;
         2 :多用户模式,会启动网络功能,但不会启动NFS,维护模式;
         3 :多用户模式,正常模式,文本界面
         4 :预留级别,可同3级别
         5 :多用户模式,正常模式,图形界面
        默认级别:3,5
         切换级别:init #
         查看级别:runlevel、who -r
各级别的不同之处,主要在于init按照配置文件进行的初始化的不同
配置文件:/etc/inittab   (CentOS 5)
 每行定义一种action以及与之对应的process
     id : runlevel : action : process
     action:
         wait:切换至此级别运行一次
         respawn:此process终止,就重新启动之
         initdefault:设定默认运行级别,process省略
         sysinit:设定系统初始化方式,此处一般指定为/etc/rc.d/rc.sysinit
示例:   
     id : 3 : initdefault : 
     si :  : sysinit : /etc/rc.d/rc.sysinit
     l0 : 0 : wait : /etc/rc.d/rc 0
     l1 : 1 : wait : /etc/rc.d/rc 1
     ........
     l6 : 6 : wait : /etc/rc.d/rc 6
     说明:rc 0-->意味着读取/etc/rc.d/rc0.d/目录下的文件(脚本链接),文件以K或S开头
         K*:K##*,##运行次序,越小越先运行,K是Kill要关闭的,数字越小的服务,通常为依赖到别的服务。
         S*:S##*,S是Start,要启动的,数字越小越先运行,数字小的服务,通常为被依赖到的服务。
        for srv in /etc/rc.d/rc0.d/K*;do
             $srv stop
         done
         for srv in /etc/rc.d/rc0.d/S*;do
             $srv start
         done
  
chkconfig命令:查看每个服务在不同级别下是启动还是关闭的。
chkconfig --list
    chkconfig --level 3 NetworkManager  off   修改级别下是启动还是关闭,即修改K或S
 添加:
     SysV的服务脚本放置于/etc/rc.d/init.d (/etc/init.d)
         chkconfig --add name
 测试添加自定义的服务:testsrv:
 1)在/etc/rc.d/init.d目录下新建testsrv文件
 2)给testsrv文件增加执行权限:
chmod +x testsrv
 3)加到服务中去:
chkconfig --add testsrv
下图是添加服务前的文件
添加后,查看结果:

删除:
    chkconfig --del name
     chkconfig --del testsrv
修改指定的链接类型
     chkconfig [--level levels] name <on | off | reset>
         --level ####:指定要设置的级别;省略时表示2345
特殊的S99local服务:正常级别下,最后启动的一个服务,没有连接至/etc/rc.d/init.d目录下
 指向的是上级目录的rc.local,是一个脚本文件,可以将不便或不需要写为服务脚本放于/etc/rc.d/init.d目录下,不需要chkconfig管理,但又想开机时自动运行的命令或脚本写入此文件。
 /etc/rc.local也指向/etc/rc.d/rc.local
在init的最后,就是要启动一个供用户登录的会话窗口,使用户可以登录,登录后,启动bash进行交互操作。假设这个登录在inittab中如下:
 tty1:2345:respawn:/usr/sbin/mingetty tty1
 tty2:2345:respawn:/usr/sbin/mingetty tty2
 ......
 tty6:2345:respawn:/usr/sbin/mingetty tty6
     mingetty会调用login程序,以验证用户登录合法性
/etc/rc.d/rc.sysinit:系统初始化脚本,完成以下功能
     (1)设置主机名
     (2)设置欢迎信息
     (3)激活udev和selinux
     (4)挂载/etc/fstab文件中定义的文件系统
     (5)检测根文件系统,并以读写方式重新挂载根文件系统
     (6)设置系统时钟
     (7)激活swap设备
     (8)根据/etc/sysctl.conf文件设置内核参数
     (9)激活lvm及software raid设备
     (10)加载额外设备的驱动程序
     (11)清理操作
总结:(CentOS 5的init)
 /sbin/init --> (/etc/inittab) --> 设置默认运行级别 --> 运行系统初始化脚本、完成系统初始化 -->关闭对应级别下需要关闭的服务,启动需要启动的服务 --> 设置登录终端
CentOS 6:
     init程序为:upstart,由Ubuntu研制,其配置文件:/etc/inittab 、/etc/init/*.conf,主要是后者,即将CentOS5的inittab分割成多个.conf文件。
注意:/etc/init/*.conf文件语法,遵循upstart配置文件语法格式;
网上找的启动流程图:
CentOS5、6忘记密码的解决:
     启动系统时,设置其运行级别1:启动时,提示按任意键进入隐藏的GRUB菜单
     
按e键可以编辑菜单:
 root (hd0,0)指定grub根目录所在设备位置,这里是第一个硬盘的第一个分区,这里root是grub的命令,指定boot所在的分区作为grub的根目录
 kernel是加载内核,指定内核文件,以只读(ro)加载文件系统的根目录,即根分区,root=。。。参数指定文件系统根分区的位置,使用了lvm
 initrd是将.img文件作为虚拟磁盘载入内存,模拟根文件系统,其上有少量驱动程序等,如要使用lvm,必须加载lvm文件系统驱动
 以上指定的分区、设备可以参考下面查看到的信息
通过上面的信息,root(hd0,0)就是指定/boot所在的分区,即/dev/sda1,kernel加载的内核文件 /vmlinuz-2.6.32-754.el6.x86_64,实际上是(hd0,0)/vmlinuz-2.6.32-754.el6.x86_64,这里的根是指的boot分区,后面的参数root=/dev/mapper/vg_study610-lv_root,才是指定文件系统的根分区,其使用的是lvm,看上图,mount信息中根“/”的挂载设备就是这里的参数值。initrd是在没有加载文件系统根目录前,模拟的根文件系统,以提供部分驱动。如要想使用真正的根文件系统,必须加载lvm驱动,这个就要放在initrd的文件中。
这里编辑kernel这一行,这个命令行很长,具体如下:
 kernel /vmlinuz-2.6.32-754.el6.x86_64 ro root=/dev/mapper/vg_study610-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_NO_MD rd_LVM_LV=vg_study610/lv_swap SYSFONT=latarcyrheb-sun16 crashkernel=auto rd_LVM_LV=vg_study610/lv_root  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
 在其后添加空格 1或single,表示运行于level 1级别。然后回车,确认修改,回到上一级菜单,然后按b键,表示启动运行这个内核。
不需要输入密码,直接以root登录了,然后可以修改密码。
GRUB(Boot Loader):
 grub legacy(grub1)
     stage 1:mbr
     stage1_5:mbr之后的扇区,让stage1中的bootloader能识别stage2所在的分区上的文件系统
     stage2:磁盘分区(/boot/grub/)
     配置文件:/boot/grub/grub.conf  <-- /etc/grub.conf
     stage2及内核等通常放置于一个基本磁盘分区;
     功用:
         (1)提供菜单,并提供交互式接口
             e:编辑模式,用于编辑菜单
             c:命令模式,交互式接口
         (2)加载用户选择的内核或操作系统
             允许传递参数给内核|
             可隐藏此菜单
         (3)位菜单提供了保护机制
             为编辑菜单进行认证
             为启用内核或操作系统进行认证
如何识别设备:
     (hd#,#):第一个#表示磁盘号,第二个#表示第#个分区,都是从0开始编号。
grub的命令行接口:
     find (hd#,#)/PATH/TO/SOMEFILE:查找特定文件
     root (hd#,#):指定根目录
     kernel /PATH/TO/KERNEL_FILE:设定本次启动时用到的内核文件;额外还可以添加许多内核支持使用的cmdline参数,如:
         init=/path/to/init , selinux=0
     initrd l /PATH/TO/INITRAMFS_FILE:设定为选定的内核提供额外文件的ramdisk
     boot:引导启动选定的内核    
手动在grub命令行接口启动系统:
     grub>root (hd0,0)
     grub>kernel /vmlinuz-VERSION-RELEASE ro root=/dev/DEVICE
     grub>initrd /initramfs-VERSION-RELEASE.img
     grub>boot
grub配置文件:/boot/grub/grub.conf
     配置项:
         default=#:设定默认启动的菜单项;菜单项(title)标号从0开始
         timeout=#:指定菜单项等待选项选择的时长
         splashimage=(hd#,#)/PATH/TO/XPM_PIC_FILE:指明菜单项背景图片文件路径
         hiddenmenu:隐藏菜单
         password [--md5] STRING:菜单编辑认证
         title TITLE:定义菜单项“标题”,可出现多次,引导不同的内核
             root (hd#,#):grub查找stage2及kernel文件所在设备分区:为grub的“根”
             kernel /PATH/TO/VMLINUZ_FILE [PARAMETERS] :启动的内核
             initrd /PATH?TO?INITRAMFS_FILE:内核匹配的ramfs文件
             password [--md5] STRING:启动选定的内核或操作系统时进行认证
grub-md5-crypt:grub提供的加密工具
安装grub:grub-install命令
     (1)grub-install
         grub-install --root-directory=ROOT /dev/DISK
     (2)grub
         grub>root (hd#,#)
         grub set (hd#)
实验1:新增加一块硬盘,在此硬盘上安装grub并设置init为bash。(即为新盘安装系统)
     (1)新增一块硬盘

         (2)对新增的磁盘做分区
使用fdisk /dev/sdb进行分区
sdb1为boot分区,sdb2为swap分区,sdb3为根分区。
     (3)安装文件系统:
    (4)将sdb1挂载到当前系统中,sdb1作为新系统的boot分区
     (5)安装grub
 运行grub-install:
     grub-install --root-directory=/mnt /dev/sdb
这里需要注意的是--root-directory的指定,这里是指定boot的根,实验的boot分区是挂载在/mnt/boot下,所以/mnt/boot应该为boot的根,但是grub安装程序会自动在根下创建boot子目录,所以,如果--root-directory指定为/mnt/boot的话,最终会创建/mnt/boot/boot/grub,所以根目录指定不包括boot子目录。如下的测试:
 在grub目录下,可以看到stage1,stage1_5和stage2,stage1此时已经安装到了sdb磁盘的MBR,grub中缺少配置文件,手动创建一个。
 同时,在boot的根目录下,此时还没有内核文件与ramfs文件,拷贝当前系统的文件到此目录下:

 这里要注意的是:第一条 root (hd0,0)为何是(hd0,0),因为最终这块磁盘要单独作为系统使用,这就成为了第一磁盘,第二条kernel中的root,是最终单独使用磁盘时,作为根目录的磁盘,即前面分区的sdb3,最终成为sda3。
 然后,要成为文件系统的根,需要按照FHS,创建相应的目录结构:
拷贝bash程序到对应目录,作为新系统启动后运行的进程,一个程序要运行,不但要拷贝程序本身,还要有对用的共享库,测试拷贝的bash是否可用,可以将根切换到新系统:chroot /mnt/sysroot
修改一下grub.conf配置文件,将kernel命令添加参数init=/bin/bash,即限定kernel后的init是bash,而不是默认的init。
     (6)新建虚拟机,其硬盘使用刚才的磁盘。
     (7),启动新建的虚拟机

启动失败,原因是拷贝的内核中启动了selinux,在配置菜单中添加禁止selinux的参数:

 系统引导成功。
实验2:本机grub损坏,但是系统还在运行中。
     (1)备份MBR,然后破坏MBR,但不破坏分区表,即MBR中的前446被破坏:
此时,重启系统就会出错。
     (2)使用grub-install修复:
 如此,便修复了MBR。
     (3)还可以进入grub命令交互界面:
 指定boot根:root (hd0,0)
 修复: setup (hd0)
可以看到,setup检测到stage1不存在了,然后安装执行了stage1,在MBR,然后在随后的27个扇区安装的stage1_5。
如此,也修复完毕。
实验3:本机grub损坏,重启系统了,此时需要使用使用修复模式(Rescue)进行修复,需要另外的启动盘,如光盘、U盘等。
 破坏MBR:
重新启动后:
链接ISO文件,从光驱启动:

 最后一项回车:
选择Rescue a CentOS system,或者在第一个界面按Esc,进入命令行,键入:linux rescue

可以看到,已经发现了已安装的系统,并将其挂载到/mnt/sysimage下。
按回车,运行shell,在shell下,切换root到原来的系统
再次启动,可以引导。
CentOS6的界面:



以上方法恢复后,重新启动引导后出现如下警告:
等待重新加载后,启动成功。
Linux Kernel:
     单内核模式、但充分借鉴了微内核模式的优先,为内核引入模块化机制。
     内核组成部分:
         kernel:内核核心,一般为bzImage,通常在/boot目录下,名称为vmlinuz-VERSION-RELEASE
         kernel object:内核对象,一般放置于/lib/moudles/VERSION-RELEASE/目录下
         内核对象可以做成模块功能的,有三种选择:
             [ ]:N ,不选择,不编译此功能
             [M]:M,选择编译成模块
             [*] :Y,编译成内核核心部分。
         辅助文件:ramdisk,两种
             initrd 和 initramfs
 运行中的内核:uname命令
     uname [option]...
         -n:显示节点名称
         -r:显示VERSION-RELEASE号
    lsmod :查看已经装载的内核模块,此命令读取的是/proc/modules文件的内容。
查看某模块详细信息:modinfo [option] 模块名
 参数:-n、-p、-a、-d、-l
装载或卸载模块命令:modprobe 
 装载:modprobe [-C config-file] [moudlename] [module parameters...]
     配置文件:/etc/modprobe.conf,/etc/modprobe.d/*.conf
 卸载:modprobe [-r] modulename...
查询模块信息时,能够看到依赖关系,这个依赖关系是保存在/lib/modules/VERSION-RELEASE/modules.dep文件中,实际使用是将其编译成二进制,即modules.dep.bin文件。
depmod命令:内核模块依赖关系文件及系统信息映射文件的生成工具。
 装载或卸载内核模块的其他命令:
     insmod [filename] [module option]
     rmmod modulename
/proc目录:
     内核把自己内部状态信息及统计信息,以及可配置参数通过proc伪文件系统加以输出。
     参数:
         只读:输出信息
         可写:可接受用户指定“新值”来实现对内核某功能或特性的配置,主要在 /proc/sys目录
         sysctl命令用于查看或设定此目录中诸多参数
             sysctl -w path.to.parameter=VALUE
         echo命令通过重定向的方式也可以修改大多数参数的值;
             echo "VALUE" > path/to/parameter
sysctl命令,配置文件为/etc/sysctl.conf
     (1)设置某参数: sysctl -w path.to.parameter=VALUE
     (2)通过读取配置文件设置参数:sysctl -p [/path/to/conf_file]
 内核中的路由转发:是否开启功能是配置在如下位置的:
     /proc/sys/net/ipv4/ip_forward
 默认为0,没有开启路由转发功能
 想修改为永久有效,可以编辑/etc/sysctl.conf:
 将net.ipv4.ip_forward=0改为=1
 修改后不会立即生效,需要重读配置文件
常用的几个参数:
     net.ipv4.ip_forward   :路由转发
     vm.drop_caches    :回收缓存
     kernel.hostname    :主机名
/sys目录:
     sysfs:输出内核识别出的各硬件设备的相关属性信息,也有内核对硬件特性的设定信息;有些参数是可以修改的,用于调整硬件的工作特性。
 udev通过此路径下输出的信息动态为各设备创建所需要设备文件;udev是运行在用户空间程序;专用工具:udevadmin、hotplug
 udev为设备创建设备文件时,会读取其事先定义好的规则文件,一般在/etc/udev/rules.d及/lib/udev/rules.d目录下。
ramdisk文件的制作:
     mkinitrd [OPTION...] [<initrd-image>] <kernel-version>
         mkinitrd /boot/initramfs-$(uname -r).img  $(uname -r)
     dracut [OPTION]... <image>  <kernel-version>
         dracut /boot/initramfs-$(uname -r).img  $(uname -r)
ramdisk文件是一个gz压缩文件,修改后缀名后,解压缩,再看文件类型,是cpio归档文件,将其展开,基本上是简化的根系统。sbin/目录下有switch_root,就是内核完成启动后最后的根切换要执行的程序。
编译内核:
     前提:
         (1)准备好开发环境;
         (2)获取目标主机上硬件设备的相关信息;
         (3)获取到目标主机系统功能的相关信息,例如要启用的文件系统;
    开发环境:
         Development Tools和Server Platform Development两个包组
     目标主机硬件设备相关信息:
         CPU:cat /proc/cupinfo 或lscpu 或x86info -a (这个需要安装yum install x86info)
         PCI设备:lspci -v 或 lsusb -v或lsblk 
     了解全部硬件设备信息,hal-device
     编译时可以使用现有配置文件模板,在/boot目录下有当前运行系统编译时的配置文件,可以作为新系统编译时的配置模板:/boot/config-2.6.32-754.el6.x86_64
     开始:
         (1)下载新版本内核源码:如linux-3.10.67.tar.gz   
         (2)解压到/usr/src下:tar xf linux-3.10.67.tar.gz -C /usr/src
         (3)做一个链接:理ln -sv linux-3.10.67.tar.gz linux
         (4)拷贝/boot/config-2.6.32-754.el6.x86_64到/usr/src/linux/下,命名为.config
         (5)make menuconfig,对新编译的版本进行配置

        (6)开始编译,make ,还可以启动多线程make -j 4
         因为make是终端相关的,如果一不小心关闭了终端,结果就悲剧了,所以可以使用screen,打开虚拟屏幕
 使用Ctrl+ad退出。
 执行make -j 4开始编译。此时终端断开,进入后编译依然存在。
 screen -ls查看存在的虚拟屏幕,使用screen -r 编号 重新进入
         (7)make modules_install
         (8)make install
           安装bzImage为/boot/vmlinuz-VERSION-RELEASE
           生成initramfs文件
           编辑grub配置文件
         (9)重启系统,并测试新内核
