RedHat / Centos Linux 系统运维与管理实践技巧荟萃
磁盘分区相关
Linux fdisk 磁盘分区工具以及安装 GRUB 实战:
准备工作,前置知识
演示环境基于 centos6.5 虚拟机,假设系统上已经有一块分好区,并挂载文件系统的硬盘,分区信息如下所示:
[root@centos6-5vm 桌面]# fdisk -l
Disk /dev/sda: 42.9 GB, 42949672960 bytes
255 heads, 63 sectors/track, 5221 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
Device Boot Start End Blocks Id System
/dev/sda1 * 1 64 512000 83 Linux
Partition 1 does not end on cylinder boundary.
/dev/sda2 64 5222 41430016 8e Linux LVM
默认情况下,fdisk 将以 dos 兼容模式,显示磁盘分区信息,在 dos 兼容模式下,以“柱面”(cylinders)为单位来设置每个分区的大小,
在上面的输出信息中,磁盘 sda 有2个分区:sda1 与 sda2 ,
总大小是 42949672960 字节;
5521个柱面,每柱面 7779328 字节(约 7.7 MBytes);
每扇区 512 字节(逻辑大小与物理大小相等),一个磁道有 63 个扇区;
83886080 个扇区;1331525 个磁道;
分区 sda1 从 1 号柱面开始到 64 号柱面结束;
分区 sda2 从 64 号柱面开始到 5222 号柱面结束;
(刚好 5221 个柱面)
以默认的柱面为单位来显示,以及设置每个磁盘分区的大小虽然很方便,但是 fdisk 工具的开发者推荐使用“扇区”(sectors)为单位来显示,指定,修改磁盘分区大小,
因此,你在对一块刚买回来的硬盘(等同于下面我们即将在虚拟机中添加的第二块硬盘),使用 fdisk 进行分区时,该工具会给出警告信息说,以柱面为单位的 dos 兼容模式已不推荐使用,你可以添加 -c选项,关掉 dos 兼容模式,
并且再添加 -u 选项,切换到以扇区为单位来显示,调整每个分区的大小
(补充内容:在现代硬盘的物理结构中,每个硬盘往往有多个盘片;每个盘片分两面;每面按照同心圆划分为若干个磁道;每个磁道划分为若干扇区。假设一个硬盘有2个盘片,每个盘“面”分65536个磁道,每个磁道1024个扇区,那么容量为
2 * 2 * 65536 * 1024 * 512 = 128 GBytes)为了隐藏盘片,磁道,柱面这些复杂的硬件物理结构,现代硬盘普遍使用一种叫做LBA(Logical Block Address)的方式,将硬盘的总扇区数从0开始编号,直到最后一个扇区,称为逻辑扇区号。
现在假设在Linux下的应用程序要读取某个大小为4096字节的文件,4096字节为多数操作系统分页的大小,也就是说操作系统读写硬盘上的文件时,以4096字节为单位,即便文件的实际字节数不足4096字节,它也会被操作系统按4KBytes 分页的对齐方式存储在硬盘上。应用程序通过类UNIX的系统调用 read 请求操作系统内核读取该文件,作为内核模块(以及模式)一部分的“文件系统驱动”定位该文件的逻辑扇区号从1000~1007共8个512字节的扇区,8 * 512 = 4096;然后文件系统驱动再向硬盘驱动程序发出读这8个扇区的命令;硬盘驱动通过计算机系统的总线,以及总线上的SATA控制器(早期是IDE)发出读取命令;作为操作系统内核一部分的硬盘驱动应该知道每个SATA主控制器的I/O端口号,X86 处理器支持寻址最多65536个I/O端口,它提供两条专门的指令“in”(写)和“out”(读)用于对I/O端口的操作。对于早期有IDE接口的计算机系统而言,其2个IDE通道分别为IDE0和
IDE1;每通道上可以连接2个设备,分别为Master(主盘)与Slave(从盘),因此一个PC中最多可以有4个IDE设备。假设前面要读取的文件位于IDE0的主盘上,这也是第一个安装的硬盘的正常位置;在PC中,IDE0通道(或其上的控制器)的I/O端口地址为0x1F0~0x1F7;以及 0x376~0x377,通过向这些端口地址发送指令就能够与IDE硬盘通信。其中:
0x1F3~0x1F6 这4字节的端口地址用于写入LBA地址(即要读取的逻辑扇区号) ,第1000号逻辑扇区的LBA地址为16进制的0x000003E8,即需要往 0x1F3和0x1F4分别都写入0x00;往0x1F5写入0x03;往0x1F6写入0xE8;
0x1F2这1字节的端口地址用于写入需要读取的扇区数,比如8;
0x1F7这1字节的端口地址用于写入要执行的操作的16进制命令码,读取为0x20;
综上所述,发往IDE控制器的命令可能包括如下:
out 0x1f3, 0x00
out 0x1f4, 0x00
out 0x1f5, 0x03
out 0x1f6, 0xe8
out 0x1f2, 0x08
out 0x1f7, 0x20
除此之外,作为内核模块的硬盘驱动还要通过类似上面的通信机制,告诉IDE控制器,需要将文件数据读取到的内存地址范围,最后由read系统调用逻辑控制处理器从该内存地址范围读取数据。
所以,我们指定以扇区为基本单位,显示硬盘 sda 的分区情况:
[root@centos6-5vm 桌面]# fdisk -l -c -u
Disk /dev/sda: 42.9 GB, 42949672960 bytes
255 heads, 63 sectors/track, 5221 cylinders, total 83886080 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
Device Boot Start End Blocks Id System
/dev/sda1 * 2048 1026047 512000 83 Linux
/dev/sda2 1026048 83886079 41430016 8e Linux LVM
可以看到,输出的总扇区数量,与前面我们推测计算的相符,为 83886080 个扇区
注意,此时的 sda1 分区,从第 2048 个扇区开始,到第 1026047 个扇区结束;
sda2 分区,从第 1026048 个扇区开始,到第 83886079 个扇区结束;
第 1 个扇区到第 2047 个扇区保留未使用(总共 512 * 2047 = 1048064 字节,约 1 Mbytes 的磁盘空间)
你可能会说,以扇区为单位来设置磁盘的分区大小,不就变成要指定扇区的“数量”?而且要将希望设置的以 Mbytes ,Gbytes 为单位的大小,先除以 512 字节,换算成扇区数量 ,不是很麻烦 ?
不用担心,虽然我们显式的告诉 fdisk 以扇区为基本单位,但是 fdisk 也支持在设定分区大小的过程中,以千字节(Kbytes),百万字节(Mbytes,普遍的错误称呼为“兆”字节),以及 十亿字节(Gbytes)为基本单位,来指定分区大小,fdisk 会自动换算成扇区数量
有了上面的前置知识后,我们在 centos6.5 虚拟机上,添加第二块硬盘(不习惯的童鞋,请当作你从电脑城买回一块真实的物理硬盘)
添加完成后,保存 vmware workstation 软件对该虚拟机的设置,
注意,此时在虚拟机中,还不能识别新添加的 5 GB 硬盘,需要重启虚拟机后,虚拟机中的 fdisk ,以及其它磁盘分区管理工具,才能正确识别该硬盘
重启虚拟机后,系统已经可以识别新的硬盘,不过该硬盘是“全新的”,没有分区表,没有启动分区,没有主分区,没有文件系统信息,只有最基本的容量以及物理结构信息,此后我们将基于该硬盘,用不同工具对其进行分区实战,当然,利用了
vmware workstation 软件的 “快照”这一神奇的功能:
[root@centos6-5vm 桌面]# fdisk -c -u -l /dev/sdb
Disk /dev/sdb: 5368 MB, 5368709120 bytes
255 heads, 63 sectors/track, 652 cylinders, total 10485760 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
接下来,以 fdisk 对 sdb 进行分区
[root@centos6-5vm 桌面]# fdisk /dev/sdb
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0xc686cd1d.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.
Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)
WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
switch off the mode (command 'c') and change display units to
sectors (command 'u').
Command (m for help):
fdisk 提示设备 sdb 没有任何合法的 DOS 分区表,或者 Sun,SGI 或 OSF 磁盘标签,因此,fdisk 初次检测新磁盘时,它将会用一个磁盘标识符(这里是 0xc686cdld)创建一个 DOS 磁盘标签;
并且,用户在 fdisk 的交互模式下所做的任何设置,改动,都将保留在内存中,直到用户在交互模式按下 w 键,才会将变更写入磁盘,
(如果想放弃所做改动,可以在交互模式按下 q 键)
同时,我们关掉 dos 兼容模式,并且以扇区为基本单位调整分区大小:
Command (m for help): c
DOS Compatibility flag is not set
Command (m for help): u
Changing display/entry units to sectors
Command (m for help): p
Disk /dev/sdb: 5368 MB, 5368709120 bytes
255 heads, 63 sectors/track, 652 cylinders, total 10485760 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xc686cd1d
Device Boot Start End Blocks Id System
Command (m for help):
眼尖的童鞋可能会发现,在使用 fdisk -l 列出系统上所有识别到的磁盘时,该工具并不会对新添加的磁盘创建磁盘标识符(Disk identifier);只有你实际要对某个新磁盘进行分区时,fdisk 就会创建磁盘标识符并且映射到一个 DOS 磁盘标签
Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 1
First sector (2048-10485759, default 2048):
Using default value 2048
Last sector, +sectors or +size{K,M,G} (2048-10485759, default 10485759): +100M
Command (m for help): p
Disk /dev/sdb: 5368 MB, 5368709120 bytes
255 heads, 63 sectors/track, 652 cylinders, total 10485760 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x9c234ada
Device Boot Start End Blocks Id System
/dev/sdb1 2048 206847 102400 83 Linux
Command (m for help): w
The partition table has been altered!
Calling ioctl() to re-read partition table.
Syncing disks.
[root@centos6-5vm 桌面]#
我们首先以 n 键,新建立一个分区;以 p 键,指定该分区为主分区;数字键 1 指定该分区号为 1 (即该磁盘上的第一个分区);接着,可以看到 fdisk 要求以扇区数量来设置新建分区的大小,并且,fdisk 仅允许从第 2048 个扇区开始划分大小,换言之,前面包含 2047 个扇区的大约 1 Mbytes 磁盘空间,不属于任何分区;
当然,不同的分区工具对此的定义也不同,例如,可以使用 GNU 的 parted 分区工具,来查看 parted 是如何利用磁盘前面的 2047 个扇区;
如果我们直接回车,那么将使用默认设置,即从第 2048 个扇区开始,划分空间,
可以清楚的看到,fdisk 支持指定扇区数量(+sectors),或者 Kbytes,Mbytes,Gbytes(+size{K,M,G})
此处将其划分为 100 Mbytes 的大小(+100M)
该大小足够作为存放 GRUB(引导加载程序)以及 Linux 内核映像,虚拟磁盘映像的“启动分区”了
再次按下 p 键,将显示创建的分区信息:
设备 /dev/sdb1,没有启动标志(后面会演示如何设置启动分区的标识),从第 2048 个扇区开始,到第 206847 个扇区结束,分区标识符(Id 列)为 83,对应为
Linux 系统的分区;
可以在 fdisk 交互模式按下字母 l 键,查看所有操作系统使用的分区对应的 Id:
Command (m for help): l
0 Empty 24 NEC DOS 81 Minix / old Lin bf Solaris
1 FAT12 39 Plan 9 82 Linux swap / So c1 DRDOS/sec (FAT-
2 XENIX root 3c PartitionMagic 83 Linux c4 DRDOS/sec (FAT-
3 XENIX usr 40 Venix 80286 84 OS/2 hidden C: c6 DRDOS/sec (FAT-
4 FAT16 <32M 41 PPC PReP Boot 85 Linux extended c7 Syrinx
5 Extended 42 SFS 86 NTFS volume set da Non-FS data
6 FAT16 4d QNX4.x 87 NTFS volume set db CP/M / CTOS / .
7 HPFS/NTFS 4e QNX4.x 2nd part 88 Linux plaintext de Dell Utility
8 AIX 4f QNX4.x 3rd part 8e Linux LVM df BootIt
9 AIX bootable 50 OnTrack DM 93 Amoeba e1 DOS access
a OS/2 Boot Manag 51 OnTrack DM6 Aux 94 Amoeba BBT e3 DOS R/O
b W95 FAT32 52 CP/M 9f BSD/OS e4 SpeedStor
c W95 FAT32 (LBA) 53 OnTrack DM6 Aux a0 IBM Thinkpad hi eb BeOS fs
e W95 FAT16 (LBA) 54 OnTrackDM6 a5 FreeBSD ee GPT
f W95 Ext'd (LBA) 55 EZ-Drive a6 OpenBSD ef EFI (FAT-12/16/
10 OPUS 56 Golden Bow a7 NeXTSTEP f0 Linux/PA-RISC b
11 Hidden FAT12 5c Priam Edisk a8 Darwin UFS f1 SpeedStor
12 Compaq diagnost 61 SpeedStor a9 NetBSD f4 SpeedStor
14 Hidden FAT16 <3 63 GNU HURD or Sys ab Darwin boot f2 DOS secondary
16 Hidden FAT16 64 Novell Netware af HFS / HFS+ fb VMware VMFS
17 Hidden HPFS/NTF 65 Novell Netware b7 BSDI fs fc VMware VMKCORE
18 AST SmartSleep 70 DiskSecure Mult b8 BSDI swap fd Linux raid auto
1b Hidden W95 FAT3 75 PC/IX bb Boot Wizard hid fe LANstep
1c Hidden W95 FAT3 80 Old Minix be Solaris boot ff BBT
1e Hidden W95 FAT1
几个我们耳熟能详的分区,例如 windows 的 FAT16,FAT32,NTFS(Id 为 7);
Linux 的 swap 交换分区(虚拟内存),其 Id 为 82;
Linux 的 ext 系列分区,其 Id 为 83;
Linux 的 LVM 分区,其 Id 为 8e;
记得,到此为止所做的改动,仅仅是在 fdisk 的进程地址空间中生效,因此我们按下 w 键,通知 fdisk 将这些信息实际写入磁盘,可以看到, fdisk 执行 ioctl()
系统调用重载分区表(应该还有最重要的 sync() 系统调用,同步到磁盘,这里没有显示)
有一个相关的命令 sync ,用于手动将内存中的数据同步到硬盘,一般而言,只有在需要立即同步写入硬盘时,才执行该命令,否则应用程序会根据情况,在合适的时间自动执行 sync() 系统调用写入磁盘:
[root@centos6-5vm 桌面]# sync --help
用法:sync [选项]
强迫将已更改的数据写入磁盘,并更新超级块。
--help 显示此帮助信息并退出
--version 显示版本信息并退出
请向[email protected] 报告sync 的错误
GNU coreutils 项目主页:
我们预定将这个新创建的分区作为该磁盘上的启动分区,用来引导操作系统,因此,需要将该分区设置为“可启动的”:
Command (m for help): m
Command action
a toggle a bootable flag
Command (m for help): a
Partition number (1-4): 1
Command (m for help): w
The partition table has been altered!
Calling ioctl() to re-read partition table.
Syncing disks.
[root@centos6-5vm 桌面]# fdisk -l -c -u /dev/sdb
Disk /dev/sdb: 5368 MB, 5368709120 bytes
224 heads, 19 sectors/track, 2463 cylinders, total 10485760 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x9c234ada
Device Boot Start End Blocks Id System
/dev/sdb1 * 2048 206847 102400 83 Linux
[root@centos6-5vm 桌面]#
在交互模式中,按下 a 键,可以设置启动分区标识,保存更改后,再次查看 sdb 的分区表,此时发现,设备 sdb1 在 Boot 列中多了个星号,表明 sdb1 是一个可以启动(引导)的分区
创建分区并划分大小后,需要以特定文件系统对该分区进行格式化,由于 fdisk 工具仅用于分区,因此我们可以使用另一个叫做 mke2fs 的工具对其进行格式化,该工具的详细使用技巧,以后有机会介绍,这里仅演示简单的格式化分区操作:
[root@centos6-5vm 桌面]# mke2fs /dev/sdb1
mke2fs 1.41.12 (17-May-2010)
文件系统标签=
操作系统:Linux
块大小=1024 (log=0)
分块大小=1024 (log=0)
Stride=0 blocks, Stripe width=0 blocks
25688 inodes, 102400 blocks
5120 blocks (5.00%) reserved for the super user
第一个数据块=1
Maximum filesystem blocks=67371008
13 block groups
8192 blocks per group, 8192 fragments per group
1976 inodes per group
Superblock backups stored on blocks:
8193, 24577, 40961, 57345, 73729
正在写入inode表: 完成
Writing superblocks and filesystem accounting information: 完成
This filesystem will be automatically checked every 22 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
mke2fs 默认使用 ext2 文件系统,对分区进行格式化处理,在分区中加入许多与“块”,“块组”相关的统计信息(用于存储文件必需的信息),最后的输出表明,该文件系统每隔三个月,或者每经历 22 次“挂载”后,将调用 fsck 工具对其进行检查,看有无错误,丢失的块,可以使用 tune2fs 来调整时间间隔或挂载次数;
使用 GNU parted 工具交叉验证该磁盘的分区与文件系统信息:
[root@centos6-5vm 桌面]# parted -l /dev/sdb
Model: VMware, VMware Virtual S (scsi)
Disk /dev/sdb: 5369MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Number Start End Size Type File system 标志
1 1049kB 106MB 105MB primary ext2 启动
可以看到,1 号分区的文件系统确实是 ext2,并且为启动分区,
从 1049 Kbytes 开始(2047 sectors * 512 bytes = 1049 kbytes)
到 106 Mbytes 结束(206847 sectors * 512 bytes = 105905664 bytes = 106 Mbytes)
sdb1 为启动分区,并不意味着,该分区此时就可以引导操作系统,原因很简单:
一,启动分区上没有任何引导代码,我们需要将诸如 GRUB 的引导加载程序,安装
到该分区上,作为该磁盘的 MBR (主引导记录)
二,启动分区上没有 Linux 内核映像与虚拟磁盘映像
下面简单演示在 sdb1 上安装 GRUB,关于 Linux 内核映像,其实只要复制一个当前磁盘上 Linux 内核映像的副本,到 sdb1 的某个目录(通常为 /boot)即可,而在进行上述操作之前,需要先将 sdb1 挂载到当前运行的 Linux 内核维护的树形文件系统逻辑目录
(假设挂载到 /mnt/boot ,这样,当用户访问 /mnt/boot,内核就知道并会映射到第 2 块磁盘的第 1 个分区,即 /dev/sdb1 ):
[root@centos6-5vm 桌面]# mkdir /mnt/boot
[root@centos6-5vm 桌面]# mount /dev/sdb1 /mnt/boot
[root@centos6-5vm 桌面]# ll /mnt/boot
总用量 12
drwx------. 2 root root 12288 11月 11 10:35 lost+found
只要能在挂载的目录下查找到“lost+found”子目录,就表示操作挂载成功
[root@centos6-5vm 桌面]# grub-install --root-directory=/mnt /dev/sdb
Probing devices to guess BIOS drives. This may take a long time.
Installation finished. No error reported.
This is the contents of the device map /mnt/boot/grub/device.map.
Check if this is correct or not. If any of the lines is incorrect,
fix it and re-run the script `grub-install'.
(fd0) /dev/fd0
(hd0) /dev/sda
(hd1) /dev/sdb
在上面操作中,使用 grub-install 命令来安装 GRUB;
--root-directory= 指定挂载的目标目录;后接源磁盘,而不是磁盘分区,因此这里是 /dev/sdb;
“Installation finished. No error reported”表明成功安装;
/mnt/boot/grub/device.map 这个文件中,存储着 GRUB 识别到的磁盘与 Linux 内核识别到的磁盘之间的映射关系;例如,Linux 内核的所谓 /dev/sdb,对 GRUB 而言,就是 hd1;如果发现该文件的映射不正确,重新执行 grub-install 命令,则可以解决这个问题;
再次查看 /mnt/boot,以及安装生成的 grub 子目录:
[root@centos6-5vm 桌面]# ll /mnt/boot
总用量 14
drwxr-xr-x. 2 root root 1024 11月 11 10:56 grub
drwx------. 2 root root 12288 11月 11 10:35 lost+found
[root@centos6-5vm 桌面]# ll /mnt/boot/grub/
总用量 274
-rw-r--r--. 1 root root 45 11月 11 10:56 device.map
-rw-r--r--. 1 root root 13392 11月 11 10:56 e2fs_stage1_5
-rw-r--r--. 1 root root 12632 11月 11 10:56 fat_stage1_5
-rw-r--r--. 1 root root 11760 11月 11 10:56 ffs_stage1_5
-rw-r--r--. 1 root root 11768 11月 11 10:56 iso9660_stage1_5
-rw-r--r--. 1 root root 13280 11月 11 10:56 jfs_stage1_5
-rw-r--r--. 1 root root 11968 11月 11 10:56 minix_stage1_5
-rw-r--r--. 1 root root 14424 11月 11 10:56 reiserfs_stage1_5
-rw-r--r--. 1 root root 512 11月 11 10:56 stage1
-rw-r--r--. 1 root root 126108 11月 11 10:56 stage2
-rw-r--r--. 1 root root 12036 11月 11 10:56 ufs2_stage1_5
-rw-r--r--. 1 root root 11376 11月 11 10:56 vstafs_stage1_5
-rw-r--r--. 1 root root 13976 11月 11 10:56 xfs_stage1_5
在上面输出中:
stage1 是 GRUB 的第一阶段引导程序;stage2 是第二阶段引导程序;带有 stage1_5 的,是 GRUB 内置的“文件系统驱动”,简单地讲,当这个“1.5 阶段”加载到内存后, GRUB 就应该能识别目标磁盘上的文件系统类型,
注意,此时 Linux 内核及其文件系统驱动还没有进驻内存,GRUB 不可能依赖于 Linux 内核实现的文件系统机制来访问磁盘,GRUB 必须有自己的识别机制,而这就是各种 stage1_5 文件存在的目的,可以看到,GRUB 支持多个操作系统使用的文件系统,包括 Linux 的 ext 系列文件系统,windows 的 fat 系列文件系统,
FreeBSD 的 ufs 文件系统,甚至还有光盘文件系统(iso9660);
GRUB 在启动时会根据 grub.conf 文件中的配置,来决定其行为模式,但是以 grub-install 命令安装时,并不会生成这个配置文件,我们需要手动创建并且编辑里面的内容,当然你也可以直接从第一块硬盘上的 grub.conf 中复制内容,然后将相应的部分修改即可:
假设 /dev/sda1 挂载到 /boot;/dev/sdb1 挂载到 /mnt/boot;这 2 个分区分别为 2 块硬盘各自的启动分区:
[root@centos6-5vm 桌面]# df -hT
/dev/sda1 ext4 485M 37M 423M 9% /boot
/dev/sdb1 ext2 97M 22M 71M 24% /mnt/boot
[root@centos6-5vm 桌面]# cp /boot/grub/grub.conf /mnt/boot/grub/grub.conf
[root@centos6-5vm 桌面]# vim /mnt/boot/grub/grub.conf
default=0
timeout=10
splashp_w_picpath=(hd0,0)/grub/bootimg.xpm.gz
title shayiOS (test operation system VER 1.0)
root (hd0,0)
kernel /vmlinuz-2.6.32-431.el6.i686 ro root=/dev/sda2
initrd /initramfs-2.6.32-431.el6.i686.img
这个是我们自定义的 grub.conf 配置文件; splashp_w_picpath= 用于指定 GRUB 启动时,显示的图片,其存放位置,
注意,这里的 hd0,0 虽然以当前系统而言,它应该是第二块硬盘的第一个分区 hd1,0(即 sdb1),
但是在实际使用中,如果你把第二块硬盘接到一个没有硬盘的系统上,
对于该系统的主板 BIOS 芯片而言,它就成了第一块硬盘 hd0,0
即(sda1)
并且由于 GRUB 使用 BIOS 的磁盘分区驱动,所以这里配置成 hd0,0
后面的 root (hd0,0) 也是同样道理;参考下图:
kernel 条目后面的 root=/dev/sda2 ,用于定义第二块硬盘上的根分区,由于该分区目前还不存在,因此后面会创建,同时在第一个分区的 grub 目录下创建一张图片 bootimg.xpm.gz
并且,将第一块硬盘上第一个分区(以当前系统而言是 sda1)的 Linux 内核映像与虚拟磁盘文件,复制到第二块硬盘的第一个分区 (以当前系统而言是 sdb1 )
即可;
先检查一下,要创建分区所在的磁盘,是否已经挂载到树形文件目录的某个挂载点上,如果是,首先要卸载该磁盘,否则在后续使用 fdisk 分区时,会提示设备忙碌,无法重新写入分区表等错误提示:
[root@centos6-5vm 桌面]# fdisk -c -u /dev/sdb
Command (m for help): w
The partition table has been altered!
Calling ioctl() to re-read partition table.
WARNING: Re-reading the partition table failed with error 16: 设备或资源忙.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.
[root@centos6-5vm 桌面]# df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda1 ext4 485M 37M 423M 9% /boot
/dev/sdb1 ext2 97M 22M 71M 24% /mnt/boot
[root@centos6-5vm 桌面]# umount /mnt/boot
[root@centos6-5vm 桌面]# df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda1 ext4 485M 37M 423M 9% /boot
[root@centos6-5vm 桌面]# fdisk -c -u /dev/sdb
Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 2
First sector (206848-10485759, default 206848):
Using default value 206848
Last sector, +sectors or +size{K,M,G} (206848-10485759, default 10485759): +512M
Command (m for help): w
The partition table has been altered!
Calling ioctl() to re-read partition table.
Syncing disks.
[root@centos6-5vm 桌面]# mke2fs /dev/sdb2
正在写入inode表: 完成
Writing superblocks and filesystem accounting information: 完成
This filesystem will be automatically checked every 38 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
[root@centos6-5vm 桌面]# fdisk -c -u -l /dev/sdb
Device Boot Start End Blocks Id System
/dev/sdb1 * 2048 206847 102400 83 Linux
/dev/sdb2 206848 1255423 524288 83 Linux
在上面输出中,首先让 /dev/sdb 处于挂载状态,此时操作 fdisk 分区时,最后无法写入分区表;卸载 /dev/sdb 后,就可以成功对其进行分区;
出于演示目的,这里仅将 sdb2 划分 512 Mbytes 的空间,在实际使用时,由于 sdb2 是根文件系统,因此尽可能分配多一些空间(除非你愿意将 /etc,/var,/usr,/root,等目录单独分区);
限于篇幅,精简了上面执行 mke2fs 对 sdb2 以 ext2 文件系统进行格式化操作的部分输出信息;
接下来,重新挂载 /dev/sdb1 到 /mnt/boot,然后将第一块硬盘第一个分区上的
Linux 内核映像,虚拟磁盘文件,复制到第二块硬盘的第一个分区:
[root@centos6-5vm 桌面]# mount /dev/sdb1 /mnt/boot
[root@centos6-5vm 桌面]# df -hT
/dev/sda1 ext4 485M 37M 423M 9% /boot
/dev/sdb1 ext2 97M 22M 71M 24% /mnt/boot
[root@centos6-5vm 桌面]# cp /boot/{vmlinuz-2.6.32-431.el6.i686,initramfs-2.6.32-431.el6.i686.img} /mnt/boot/
[root@centos6-5vm 桌面]# ll /mnt/boot/
总用量 19916
drwxr-xr-x. 2 root root 1024 11月 11 22:01 grub
-rw-------. 1 root root 16290996 11月 11 22:40 initramfs-2.6.32-431.el6.i686.img
drwx------. 2 root root 12288 11月 11 10:35 lost+found
-rwxr-xr-x. 1 root root 4002656 11月 11 22:40 vmlinuz-2.6.32-431.el6.i686
如前所述,我们计划在 /mnt/boot/grub(即第二块硬盘第一分区的 grub 目录下 ),创建一张图片,用于在 GRUB 启动时显示背景画面;
GRUB 支持作为启动背景的图片,必须满足下述条件,才能正常显示(其实这里参考了 magedu 上的视频教程,有兴趣的童鞋可以访问相关站点学习):
一,图片分辨率最高支持到 640 * 480 像素
二,颜色深度(色深)为 14 位
三,必须为 xpm 格式
Linux 平台上,有一款优秀的图形图像编辑软件,堪比 windows 上的 photoshop,它就是 gimp (GNU 图像处理程序),限于篇幅,本博文不介绍详细的 gimp 安装与使用,有兴趣的童鞋可以自行搜索文章,教程;
gimp 强大的图形编辑功能,可以一次性满足上面列出的三个条件;
我们使用的测试图片为 AQUA(水叮当舞曲乐队)在 1997 年由环球音乐发售的专辑唱片 CD 的封面,
该乐队与专辑中的歌曲曾经红遍 1997~2001 年的全球各地,
其中《 My Oh My》,《 Barbie Girl》,《 Lollipop 》至今仍旧是令人怀念的动感舞曲,这里顺便向各位推荐,一天繁重的运维工作结束后,放松心情聆听一下,是不错的选择;
下面以截图方式介绍 GRUB 启动图片的整体制作过程:
制作完成以后,将其存储在 /mnt/boot/grub/ 目录下,取名为 bootp_w_picpath.xpm
注意,经过了上面的调整后,我们可以使用 gzip 进一步压缩该图像文件的大小,避免出现 GRUB 无法正常显示图片的情况;
然后编辑 /mnt/boot/grub/grub.conf ,加入相应的条目:
[root@centos6-5vm 桌面]# mv /root/桌面/bootp_w_picpath.xpm /mnt/boot/grub/
[root@centos6-5vm 桌面]# du -k /mnt/boot/grub/bootp_w_picpath.xpm
229 /mnt/boot/grub/bootp_w_picpath.xpm
[root@centos6-5vm 桌面]# gzip -9 /mnt/boot/grub/bootp_w_picpath.xpm
[root@centos6-5vm 桌面]# du -k /mnt/boot/grub/bootp_w_picpath.xpm.gz
30 /mnt/boot/grub/bootp_w_picpath.xpm.gz
[root@centos6-5vm 桌面]# vim /mnt/boot/grub/grub.conf
default=0
timeout=10
splashp_w_picpath=(hd0,0)/grub/bootp_w_picpath.xpm.gz
title shayiOS (test operation system VER 1.0)
root (hd0,0)
kernel /vmlinuz-2.6.32-431.el6.i686 ro root=/dev/sda2
initrd /initramfs-2.6.32-431.el6.i686.img
可以看到,制作完成后的启动图片,大小为 229 Kbytes,经过 gzip 的最高比例压缩后,减少到 30 Kbytes,并且变成以 .gz 为后缀;
保存对 grub.conf 的修改并退出 vim ,此时的第二块硬盘第一分区应该有如下所示的结构:
[root@centos6-5vm 桌面]# ll /mnt/boot/
总用量 19916
drwxr-xr-x. 2 root root 1024 11月 12 00:38 grub
-rw-------. 1 root root 16290996 11月 11 22:40 initramfs-2.6.32-431.el6.i686.img
drwx------. 2 root root 12288 11月 11 10:35 lost+found
-rwxr-xr-x. 1 root root 4002656 11月 11 22:40 vmlinuz-2.6.32-431.el6.i686
[root@centos6-5vm 桌面]# ll /mnt/boot/grub/
总用量 306
-rw-r--r--. 1 root root 28249 11月 12 00:04 bootp_w_picpath.xpm.gz
-rw-r--r--. 1 root root 45 11月 11 10:56 device.map
-rw-r--r--. 1 root root 13392 11月 11 10:56 e2fs_stage1_5
-rw-r--r--. 1 root root 12632 11月 11 10:56 fat_stage1_5
-rw-r--r--. 1 root root 11760 11月 11 10:56 ffs_stage1_5
-rw-r--r--. 1 root root 221 11月 12 00:32 grub.conf
-rw-r--r--. 1 root root 11768 11月 11 10:56 iso9660_stage1_5
-rw-r--r--. 1 root root 13280 11月 11 10:56 jfs_stage1_5
-rw-r--r--. 1 root root 11968 11月 11 10:56 minix_stage1_5
-rw-r--r--. 1 root root 14424 11月 11 10:56 reiserfs_stage1_5
-rw-r--r--. 1 root root 512 11月 11 10:56 stage1
-rw-r--r--. 1 root root 126108 11月 11 10:56 stage2
-rw-r--r--. 1 root root 12036 11月 11 10:56 ufs2_stage1_5
-rw-r--r--. 1 root root 11376 11月 11 10:56 vstafs_stage1_5
-rw-r--r--. 1 root root 13976 11月 11 10:56 xfs_stage1_5
Linux 内核映像;虚拟磁盘文件;grub 目录以及该目录下的 bootp_w_picpath.xpm.gz 启动图片;grub.conf 配置文件;stage1;stage2 这两个核心的 GRUB 启动代码;
最后一步,只需要在第二分区(sdb2)中,构建根文件系统的树形结构,以及制作一个由 Linux 内核创建的第一个用户空间进程( init )读取的配置文件,即
/etc/inittab,以及由该文件所引用的启动脚本即可;这最后一步看起来异常复杂,在此之前,让我们先测试一下到 GRUB 引导 Linux 内核的过程是否正常:
我们在 vmware workstation 软件中,新建一个 RedHat Linux 系列的虚拟机,用来模拟将这块硬盘接到一个没有硬盘的 PC 主板上面,然后尝试从该硬盘的 MBR 来引导系统:
关于新建一个虚拟机的步骤,这里不费笔墨介绍,想必大家都很熟悉了,比较关键的一步是在设置硬盘参数的时候,要选择
“use an existing virtual disk”,换言之,该虚拟机的硬盘,将使用你的系统上其它已建立的虚拟机的硬盘(模拟无硬盘的系统接上一块硬盘并引导的情况),然后,在宿主机(真实机)的文件系统上,找出已建立的虚拟机的“vmdk(虚拟磁盘文件)”存储路径,将新建虚拟机的硬盘文件,定位到这个路径即可:
现在,两个虚拟机使用了宿主机上同一个 vmdk(虚拟磁盘文件),这会导致资源冲突,新建的虚拟机将无法启动,
所以要把“共享” vmdk 的那个旧虚拟机“挂起”(在 vmware workstation 中,选定旧虚拟机的标签,在菜单中选择“vm”-> “power”-> “suspended”或者 “power off”)
最后,进入新建虚拟机的 BIOS,选择从共享的硬盘启动即可,下面是实际测试的效果,可以看到,前面我们的劳动有所回报:
回到主题,在 sdb2 中构建根文件系统:
[root@centos6-5vm 桌面]# mkdir /mnt/sdb2-sysroot
[root@centos6-5vm 桌面]# ll /mnt
总用量 12
drwxr-xr-x. 2 root root 4096 11月 11 10:37 boot
drwxr-xr-x. 2 root root 4096 6月 25 17:27 hgfs
drwxr-xr-x. 2 root root 4096 11月 12 12:12 sdb2-sysroot
[root@centos6-5vm 桌面]# mount /dev/sdb2 /mnt/sdb2-sysroot/
[root@centos6-5vm 桌面]# cd /mnt/sdb2-sysroot/
[root@centos6-5vm sdb2-sysroot]# ll
总用量 16
drwx------. 2 root root 16384 11月 11 22:17 lost+found
上面,我们创建一个一眼就能看出是 sdb2 的根分区的目录,名为 sdb2-sysroot,将 sdb2 挂载并进入该目录,在其中构建根文件系统的抽象树形结构;
再次强调,下面的操作,是把 /etc,/var,/home,等目录都放在与根( / )相同的分区,即 sdb2 中,你也可以把这些子目录,划分独立的分区,如 sdb3,sdb4 等等,
实际上服务器的硬盘正是这么做的,常见的解释是,如果把产生大量日志信息的 /var 目录与根放在同一个分区,海量日志信息会把该分区的根填满,导致系统性能下降(响应速度变慢)甚至停机;
[root@centos6-5vm sdb2-sysroot]# pwd
/mnt/sdb2-sysroot
[root@centos6-5vm sdb2-sysroot]# mkdir -pv {etc,lib,bin,sbin,proc,dev,home,root,tmp,mnt,media,misc,usr/{local,bin,sbin,lib,share},var/{log,run,spool},boot}
[root@centos6-5vm sdb2-sysroot]# ls
bin boot dev etc home lib lost+found media misc mnt proc root sbin tmp usr var
由于 sdb1 的 Linux 内核在启动后,会通过从 sdb2 的 sbin 目录中加载可执行文件 init 至内存,来创建第一个用户空间进程 init,但此刻 sdb2 的 sbin 目录中没有相应的文件;
因此需要将 sda 根文件系统中的 init 可执行文件( /sbin/init ),复制到 sdb2 根文件系统的相应位置,并且检查 init 文件在加载时的动态依赖性,查看它都链接了那些共享库,将这些共享库也复制到 sdb2 根文件系统的相应位置,并且还要“递归”检查这些共享库之间的依赖关系,所有这些操作无非是为了确保 Linux 内核能够正常创建 init 进程并运行:
[root@centos6-5vm sdb2-sysroot]# pwd
/mnt/sdb2-sysroot
[root@centos6-5vm sdb2-sysroot]# cp /sbin/init /mnt/sdb2-sysroot/sbin/
[root@centos6-5vm sdb2-sysroot]# ldd /sbin/init
linux-gate.so.1 => (0x00ab7000)
libnih.so.1 => /lib/libnih.so.1 (0x00710000)
libnih-dbus.so.1 => /lib/libnih-dbus.so.1 (0x008bd000)
libdbus-1.so.3 => /lib/libdbus-1.so.3 (0x0018b000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00a3e000)
librt.so.1 => /lib/librt.so.1 (0x0044f000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x006d4000)
libc.so.6 => /lib/libc.so.6 (0x00458000)
/lib/ld-linux.so.2 (0x00698000)
[root@centos6-5vm sdb2-sysroot]# cp /lib/libnih.so.1 /mnt/sdb2-sysroot/lib/
[root@centos6-5vm sdb2-sysroot]# cp /lib/libnih-dbus.so.1 /mnt/sdb2-sysroot/lib/
[root@centos6-5vm sdb2-sysroot]# cp /lib/libpthread.so.0 /mnt/sdb2-sysroot/lib/
[root@centos6-5vm sdb2-sysroot]# cp /lib/librt.so.1 /mnt/sdb2-sysroot/lib/
[root@centos6-5vm sdb2-sysroot]# cp /lib/libgcc_s.so.1 /mnt/sdb2-sysroot/lib/
[root@centos6-5vm sdb2-sysroot]# cp /lib/libc.so.6 /mnt/sdb2-sysroot/lib/
[root@centos6-5vm sdb2-sysroot]# cp /lib/ld-linux.so.2 /mnt/sdb2-sysroot/lib/
[root@centos6-5vm sdb2-sysroot]# ll ./lib/
总用量 2492
-rwxr-xr-x. 1 root root 142536 11月 12 13:31 ld-linux.so.2
-rwxr-xr-x. 1 root root 1910572 11月 12 13:30 libc.so.6
-rwxr-xr-x. 1 root root 122232 11月 12 13:30 libgcc_s.so.1
-rwxr-xr-x. 1 root root 38768 11月 12 13:28 libnih-dbus.so.1
-rwxr-xr-x. 1 root root 100500 11月 12 13:28 libnih.so.1
-rwxr-xr-x. 1 root root 133312 11月 12 13:29 libpthread.so.0
-rwxr-xr-x. 1 root root 41724 11月 12 13:30 librt.so.1
我们知道,init 进程应该需要创建一个运行在用户空间的子进程,叫做 shell,来实现与终端用户的交互,用户通过 shell 与内核打交道;这里只假设最简单的情况,即 init 创建 BASH 进程,用户使用后者登录系统以及向内核传递要执行的命令等等,
因此,将 sda 的 bash 复制到 sdb2 的 bin 目录(取决于初始化脚本,init 会执行 /bin 下的 bash),检查并复制 sda 中所有 bash 可执行文件依赖的共享库到 sdb2 的相应目录,并且创建指向 bash 的软链接:
[root@centos6-5vm sdb2-sysroot]# pwd
/mnt/sdb2-sysroot
[root@centos6-5vm sdb2-sysroot]# cp /bin/bash /mnt/sdb2-sysroot/bin/
[root@centos6-5vm sdb2-sysroot]# ldd /bin/bash
linux-gate.so.1 => (0x00a70000)
libtinfo.so.5 => /lib/libtinfo.so.5 (0x00473000)
libdl.so.2 => /lib/libdl.so.2 (0x00874000)
libc.so.6 => /lib/libc.so.6 (0x006be000)
/lib/ld-linux.so.2 (0x00698000)
[root@centos6-5vm sdb2-sysroot]# cp /lib/libtinfo.so.5 /mnt/sdb2-sysroot/lib/
[root@centos6-5vm sdb2-sysroot]# cp /lib/libdl.so.2 /mnt/sdb2-sysroot/lib/
[root@centos6-5vm sdb2-sysroot]# cd ./bin/
[root@centos6-5vm bin]# pwd
/mnt/sdb2-sysroot/bin
[root@centos6-5vm bin]# ll
总用量 864
-rwxr-xr-x. 1 root root 874472 11月 12 14:31 bash
[root@centos6-5vm bin]# ln -s bash sh
[root@centos6-5vm bin]# ll
总用量 868
-rwxr-xr-x. 1 root root 874472 11月 12 14:31 bash
lrwxrwxrwx. 1 root root 4 11月 12 14:38 sh -> bash
[root@centos6-5vm bin]# cd
[root@centos6-5vm ~]# pwd
/root
[root@centos6-5vm ~]# chroot /mnt/sdb2-sysroot/
bash-4.1#
bash-4.1#
bash-4.1# exit
exit
[root@centos6-5vm ~]#
使用 chroot 测试 bash 是否能正常工作,至此,实现了一个简化的根文件系统;
接下来,需要将前面复制到 sdb1 的初始虚拟磁盘文件,
即 initramfs-2.6.32-431.el6.i686.img 进行部分的修改,
Linux 内核通常没有自带文件系统驱动,所以 GRUB 把 initramfs 加载到内存;此时 initramfs 正如其名字所暗示的,是内存中的虚拟文件系统;
Linux 内核通过加载的 initramfs 中的“文件系统驱动”,识别 sdb2 的文件系统,
并将 sdb2 以 rootfs (根文件系统)挂载,最后切换到真正的根文件系统,然后,Linux 内核才可以访问并加载 sdb2 的 sbin 目录下的 init 文件,创建相应进程,执行启动脚本;
(在真实情景中,Linux 内核还要加载 /lib/modules/ 目录下的各种模块,这些模块对应各种硬件的驱动,这里为了简化起见,并没有在 sdb2 的 lib 目录下创建 modules 子目录)
至于为什么要修改,
这是因为,我们直接从 sdb1 复制的初始虚拟磁盘文件,其配置是按照 sda 磁盘上的逻辑结构而定的,而 sda 与 sdb 两者的逻辑结构不同;或者也可以直接删除掉 sdb1 上的副本,然后从 sda1 上的原始文件出发,制作一个修改过的副本,直接保存到 sdb1 上,鉴于前面已经进行过复制操作,这里采用后面一种办法:
[root@centos6-5vm boot]# df -hT
/dev/sda1 ext4 485M 37M 423M 9% /boot
/dev/sdb2 ext2 504M 4.0M 475M 1% /mnt/sdb2-sysroot
/dev/sdb1 ext2 97M 5.7M 87M 7% /mnt/boot
[root@centos6-5vm grub]# rm -rf /mnt/boot/initramfs-2.6.32-431.el6.i686.img
在 sda 的 /tmp 目录下,创建一个叫做 sdb1-initramfs 的目录,用于临时存放与修改将要复制到 sdb1 的 initramfs-2.6.32-431.el6.i686.img:
[root@centos6-5vm boot]# mkdir /tmp/sdb1-initramfs
[root@centos6-5vm boot]# cd /tmp/sdb1-initramfs/
[root@centos6-5vm sdb1-initramfs]# pwd
/tmp/sdb1-initramfs
使用 zcat 命令,将 sda1 磁盘上的 initramfs-2.6.32-431.el6.i686.img
输出一份副本,将输出作为 cpio 命令的输入,
用于解压 initramfs-2.6.32-431.el6.i686.img 到当前目录,然后对解压后生成的各种子目录和文件进行编辑修改:
[root@centos6-5vm sdb1-initramfs]# pwd
/tmp/sdb1-initramfs
[root@centos6-5vm sdb1-initramfs]# zcat /boot/initramfs-2.6.32-431.el6.i686.img | cpio -id
76138 块
[root@centos6-5vm sdb1-initramfs]# ls
bin dev emergency init initqueue-finished initqueue-timeout mount pre-trigger proc sys tmp var
cmdline dracut-004-335.el6 etc initqueue initqueue-settled lib pre-pivot pre-udev sbin sysroot usr
[root@centos6-5vm sdb1-initramfs]# file ./init
./init: POSIX shell script text executable
[root@centos6-5vm sdb1-initramfs]# vim ./init
NEWROOT="/"
上面在编辑 init 脚本时,搜索 NEWROOT 字串,找到后将其值改为 "/"
(原始值为 "/sysroot"),
前面提到, Linux 内核通过 GRUB 加载的 initramfs 文件系统驱动,将 sdb2 以 rootfs(根文件系统)挂载 ,但是挂载到哪里呢 ? 上面就指定将其挂载到 /
另外还需要注意一点,Linux 内核此时是以只读模式挂载到根分区(/),因为后面创建的 init 进程首先要调用 fsck 对其进行检查或修复(具体的操作在 init 执行的系统初始化脚本 rc.sysinit 中定义),
例如上一次关闭系统时,根文件系统没有正确卸载,fsck 就可以修复错误,
而 fsck 不能检查或修复以读写模式挂载的文件系统,这会因数据一致性问题导致文件系统损坏,
当 init 调用 fsck 执行检查或修复后,init 将以读写模式重新挂载根文件系统(以及挂载其它在 /etc/fstab 文件中定义的分区-文件系统);
通过查看 init 执行 rc.sysinit 脚本时,输出到 /var/log/boot.log 的日志信息,我们可以验证以上论述:
[root@centos6-5 桌面]# cat /var/log/boot.log
Welcome to CentOS
Starting udev: /bin/chown: invalid group: `root:lp'
Setting hostname [ OK ]
Setting up Logical Volume Management: No volume groups found
[ OK ]
Checking filesystems
/dev/sda6: clean, 15357/6406144 files, 19563127/25600000 blocks
/dev/sda1: clean, 39/102400 files, 50423/409600 blocks
/dev/sda2: clean, 3433/25600000 files, 75276659/102400000 blocks
/dev/sda9: clean, 20/2910656 files, 228736/11638528 blocks
/dev/sda3: clean, 1321/9601024 files, 11571597/38400000 blocks
/dev/sda5: clean, 97915/9601024 files, 13340584/38400000 blocks
/dev/sda7: clean, 4896/6406144 files, 877993/25600000 blocks
[ OK ]
Remounting root filesystem in read-write mode: [ OK ]
日志中的 checking filesystems,实际上就是以 fsck 对只读模式的文件系统进行检查或修复,然后以读写模式重新挂载;
保存修改后退出 vim,重新打包当前目录下的所有文件与子目录,可以通过 gzip 控制压缩的大小,
将生成的文件命名为 initramfs-2.6.32-431.el6.i686.img
并存储到 sdb1 :
[root@centos6-5vm sdb1-initramfs]# find . | cpio -o -H newc --quiet | gzip -9 > /mnt/boot/initramfs-2.6.32-431.el6.i686.img
假设到此为止的操作一切正常,那么 Linux 内核启动后,应该可以创建 init 进程,后者将读取 sdb2 的 etc 目录下的 inittab 文件,确定默认运行级别,以及根据哪个脚本来执行系统启动后的初始化任务:
[root@centos6-5vm ~]# cd /mnt/sdb2-sysroot/etc/
[root@centos6-5vm etc]# pwd
/mnt/sdb2-sysroot/etc
[root@centos6-5vm etc]# vim inittab
id:3:initdefault:
si::sysinit:/etc/rc.d/rc.sysinit
[root@centos6-5vm etc]# mkdir rc.d
[root@centos6-5vm etc]# cd ./rc.d/
[root@centos6-5vm rc.d]# vim ./rc.sysinit
#!/bin/bash
echo -e "\tWelcome to shayiOS Linux !"
/bin/bash
:wq!
[root@centos6-5vm rc.d]# chmod +x ./rc.sysinit
上面配置默认运行级别为3,并且指示 init ,读取 /etc/rc.d/rc.sysinit 这个脚本,执行系统初始化,显示一个简单的欢迎信息,并且启动 bash
除此之外,为了实现能关闭系统,还需要作如下配置:
一,在 sdb2 的 etc/rc.d/ 目录下创建一个脚本名为 rc.shutdown ,编辑该脚本添加进入运行级别 0 时,应该进行的操作:调用 sdb2 的 sbin/halt 程序关机;
二,将 sda 的 halt 程序,以及所有依赖的共享库复制到 sdb2 的 相应位置,
三,在 sdb2 的 etc/inittab 文件中添加一项条目,指示 init 进程,当进入级别 0 (例如用户执行 init 0 命令)时,应该执行的脚本(这里即执行 rc.shutdown)
[root@centos6-5vm ~]# cd /mnt/sdb2-sysroot/etc/rc.d/
[root@centos6-5vm rc.d]# pwd
/mnt/sdb2-sysroot/etc/rc.d
[root@centos6-5vm rc.d]# vim rc.shutdown
#/bin/bash
exec /sbin/halt -p
:wq!
[root@centos6-5vm rc.d]# cp /sbin/halt /mnt/sdb2-sysroot/sbin/
[root@centos6-5vm rc.d]# ldd /sbin/halt
libaudit.so.1 => /lib/libaudit.so.1 (0x00122000)
[root@centos6-5vm rc.d]# cp /lib/libaudit.so.1 /mnt/sdb2-sysroot/lib/
[root@centos6-5vm rc.d]# cd /mnt/sdb2-sysroot/etc
root@centos6-5vm etc]# ls
inittab rc.d sysconfig
[root@centos6-5vm etc]# vim inittab
id:3:initdefault:
si::sysinit:/etc/rc.d/rc.sysinit
l0:0:wait:/etc/rc.d/rc.shutdown
上面的 inittab 文件内容中,最后一项条目就是新添加的“关机脚本”存放路径;
对于真实的 Linux 发行版而言,内核创建 init 进程后,init 首先读取 /etc/inittab 文件中定义的默认运行级别,假设是运行级别 5 ,然后 init 将运行级别 5 传递给 /etc/rc.d/rc 脚本,后者进入 /etc/rc.d/rc5.d/ 目录,然后它根据该目录的内容,调用 init 启动或停止相应服务;
该目录中定义了一系列系统在运行级别 5 时,应该启动(以大写 S 开头)以及关闭(一大写 K 开头)的指向 /etc/rc.d/init.d/ 目录下相应的 bash shell 脚本,或者POSIX 规范 shell 脚本的符号链接,
例如, /etc/rc.d/rc5.d/ 目录下有一个符号链接为 S13rpcbind ,表示在运行级别 5 时,以优先级 13 (优先级数字越小,该服务或守护进程越早启动)来启动 rpcbind 服务,
当然,实际执行启动 rpcbind 服务的脚本,位于 /etc/rc.d/init.d/rpcbind,而该脚本最终调用相应的命令(通常位于 /sbin,/bin,/usr/bin,/usr/sbin)来启动服务,如下所示:
[root@centos6-5vm /]# ls /etc/inittab
/etc/inittab
[root@centos6-5vm /]# file /etc/inittab
/etc/inittab: ASCII English text
[root@centos6-5vm /]# cd /etc/rc.d
[root@centos6-5vm rc.d]# ls
init.d rc rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d rc.local rc.sysinit
[root@centos6-5vm rc.d]# file ./rc
./rc: Bourne-Again shell script text executable
[root@centos6-5vm rc.d]# file ./rc5.d/
./rc5.d/: directory
[root@centos6-5vm rc.d]# cd ./rc5.d/
[root@centos6-5vm rc5.d]# ls
K01smartd K15httpd K60crond K84wpa_supplicant S01sysstat
S12rsyslog K10cups K50dnsmasq K73winbind K89portreserve
S08ip6tables S13rpcbind
[root@centos6-5vm rc5.d]# file ./S13rpcbind
./S13rpcbind: symbolic link to `../init.d/rpcbind'
[root@centos6-5vm rc5.d]# cd /etc/rc.d/init.d/
[root@centos6-5vm init.d]# ls
acpid certmonger functions iptables mdmonitor nfs postfix rpcbind saslauthd sshd wdaemon
atd cpuspeed haldaemon irqbalance messagebus nfslock psacct rpcgssd
[root@centos6-5vm init.d]# file rpcbind
rpcbind: POSIX shell script text executable
再看一个例子:postfix 服务, init 进程最终根据 /etc/rc.d/init.d/postfix
这个脚本文件中的指令,来启动,终止 2,3,4,5 运行级别下的 postfix 服务:
[root@centos6-5vm etc]# head -n 10 /etc/rc.d/init.d/postfix
#!/bin/bash
#
# postfix Postfix Mail Transfer Agent
#
# chkconfig: 2345 80 30
# description: Postfix is a Mail Transport Agent, which is the program \
# that moves mail from one machine to another.
# processname: master
# pidfile: /var/spool/postfix/pid/master.pid
# config: /etc/postfix/main.cf
从上面我们可以获得几项非常关键的信息,首先是 chkconfig: 2345 80 30 这一条目,它表明执行 chkconfig 命令时,后者将在 /etc/rc.d/rcN.d/ 目录下,建立指向该脚本(即 /etc/rc.d/init.d/postfix )的符号链接,
rcN.d 中的 N ,取决于传递给 chkconfig 命令的参数 --level 的值; 并且该服务以优先级 80 启动(S);以优先级 30 终止(K);
换言之,当用户在 shell 终端执行 chkconfig --level 5 postfix on 命令时,
chkconfig 在 /etc/rc.d/rc5.d/ 目录下,创建一个符号链接为 S80postfix;并且指向 /etc/rc.d/init.d/postfix;
类似地,当用户在 shell 终端执行 chkconfig --level 5 postfix off 命令时,
chkconfig 在 /etc/rc.d/rc5.d/ 目录下,首先删除掉原有的 S80postfix 符号链接,然后创建一个 K30postfix 符号链接,
同样指向 /etc/rc.d/init.d/postfix;如下所示:
[root@centos6-5vm etc]# chkconfig --level 5 postfix on
[root@centos6-5vm etc]# ls /etc/rc.d/rc5.d/ | grep postfix
S80postfix
[root@centos6-5vm etc]# file /etc/rc.d/rc5.d/S80postfix
/etc/rc.d/rc5.d/S80postfix: symbolic link to `../init.d/postfix'
[root@centos6-5vm etc]# chkconfig --level 5 postfix off
[root@centos6-5vm etc]# ls /etc/rc.d/rc5.d/ | grep postfix
K30postfix
[root@centos6-5vm etc]# file /etc/rc.d/rc5.d/K30postfix
/etc/rc.d/rc5.d/K30postfix: symbolic link to `../init.d/postfix'
其次是 processname : master
这项条目表明 postfix 服务在启动后的进程叫 master
这就是为什么使用 netstat -an 查看 postfix 服务在启动后监听 TCP 的 25 端口时,对应的 “进程名”为 master;
另外,我们终止 postfix 服务时,不能使用 service master stop ,这样系统无法识别;应该使用 service postfix stop;
接下来的条目 pidfile 与 config,前者是存储 postfix 服务在运行时的进程标识符(PID)的文件路径;后者是 postfix 的配置文件路径;
粗略来讲,在内核创建 init 进程后,后者按如下流程来进行系统初始化:
(以 CentOS6.5 Linux 为例)
一,内核创建 init 进程
二,init 读取 /etc/inittab 确定默认运行级别
三,init 根据 /etc/sysconfig/ 目录的各种配置文件,执行 /etc/rc.d/rc.sysinit 脚本,完成系统初始化 ( init 执行 rc.sysinit 脚本的结果,将相关日志信息输出到 /var/log/boot.log 文件中,需要确认已启动syslog/rsyslog 服务,才能记录信息 )
四,init 根据默认运行级别执行 /etc/rc.d/rcN.d/ 目录下的符号链接,实际执行这些符号链接指向的脚本(位于 /etc/rc.d/init.d/ 目录下),从而启动与停止该运行级别下的服务
五,init 最后执行 /etc/rc.d/rc.local 执行用户指派的特定任务或命令
关于第五点,可以查看 /etc/rc.d/rcN.d/ 目录验证,N 的取值可以是 2,3,4,5,表示在运行级别 2,3,4,5 中应该启动与关闭的服务脚本链接:
[root@centos6-5vm /]# pwd
/
[root@centos6-5vm /]# ls -l /etc/rc.d/rc?.d/*local*
lrwxrwxrwx. 1 root root 11 6月 25 16:58 /etc/rc.d/rc2.d/S99local -> ../rc.local
lrwxrwxrwx. 1 root root 11 6月 25 16:58 /etc/rc.d/rc3.d/S99local -> ../rc.local
lrwxrwxrwx. 1 root root 11 6月 25 16:58 /etc/rc.d/rc4.d/S99local -> ../rc.local
lrwxrwxrwx. 1 root root 11 6月 25 16:58 /etc/rc.d/rc5.d/S99local -> ../rc.local
注意通配符 ? 与 * 在这里的巧妙使用,以上输出表明,运行级别 2,3,4,5 中都有一个优先级为 S99 (即最后启动)的服务脚本符号链接,实际的服务脚本就是 /etc/rc.d/rc.local ,这表明 rc.local 脚本将在所有其它服务都开启后才执行
我们也可以通过重命名 S99local 符号链接,例如改变启动的优先级,来提前执行我们指派给系统的任务。
加载与卸载内核模块相关
内核模块是 linux 内核加载和管理硬件设备驱动程序的其中一种方式;另一种方式为静态内核映像,这些硬件驱动就集成到位于 /boot/vmlinuz-version 的内核映像中,其中 version 是内核版本号,我这个机器上的例子为
/boot/vmliunz-2.6.32-431.el6.i686
有些硬件驱动,例如 IDE 磁盘,SCSI 磁盘,SATA 磁盘等驱动,必须通过 GRUB (引导装载程序),随着内核映像一起,从磁盘上的启动分区( 通常是 /boot/initramfs-* )加载到内存中,否则内核就不能识别磁盘上的其它独立分区,如根分区( / ),也就无法提供基本的文件系统功能;既然无法访问文件系统,也就无法从其子目录,以内核模块的形式来加载其余在系统引导阶段用不上的硬件设备驱动,例如网卡,声卡等驱动;
*****由于 GRUB 在 Linux 内核之前加载到内存,因此它内置了自身实现的磁盘,文件系统驱动来识别计算机上的存储设备及其分区;进一步而言,在 GRUB 自身的磁盘驱动以及文件系统驱动(通常位于 /boot/grub/stage2 )加载到内存前,它使用 BIOS 的磁盘驱动来识别并读写磁盘
整理一下上面的知识点:
/boot 即启动分区,通常为磁盘上的第一个分区,其中包含了:
GRUB 引导加载程序(stage1,stage2,grub.conf 以及其它重要的文件);
Linux 内核映像;
initrd (初始虚拟磁盘,initial ram disk,用于在 Linux 内核的文件系统驱动进驻内存前,识别磁盘上的文件系统)映像;
BIOS 使用自身的磁盘驱动程序,识别并加载 /boot/grub/stage1(准确地讲是识别并加载用户所选启动设备上的0磁道1号扇区,该扇区即 MBR );
GRUB 的 stage1 使用 BIOS 的磁盘驱动程序,识别并加载 /boot/grub/stage2(准确地讲是识别并加载0磁道2号扇区) ,stage2 中包含了 GRUB 自身实现的磁盘驱动程序,此后就用它自己的磁盘驱动识别并加载 /boot 分区中的 Linux 内核映像;
Linux 内核映像中的磁盘驱动程序以及文件系统驱动,可以识别 /boot 分区(以及其它所有磁盘分区),但是在此之前,它需要依靠 initrd 来识别
静态内核映像形式的硬件驱动和内核模块形式的硬件驱动之间的区别在于:通过 modprobe 与 modprobe -r 命令,后者可以“动态”的“按需”加载,卸载;
*****如果要配置作为静态内核映像一部分加载的硬件驱动的行为模式,可以通过 GRUB 或者 LILO 等引导加载程序提供的“内核命令行”来设置引导参数;如下命令可以查看当前的内核引导参数:
[root@centos6-5 ~]# cat /proc/cmdline
ro root=UUID=0d1c20e2-6fb8-4b37-8ced-987df661f7c8 rd_NO_LUKS KEYBOARDTYPE=pc KEYTABLE=us rd_NO_MD crashkernel=128M LANG=zh_CN.UTF-8 rd_NO_LVM rd_NO_DM rhgb quiet
*****如果要配置作为内核模块动态加载的硬件驱动行为模式,可以通过编辑 /etc/modprobe.conf 文件来实现,内核在加载相应模块时会读取该文件中的配置参数,来决定该硬件驱动的工作模式;
(但是我发现在 centos6.5 中,该文件默认不存在,只有在基于 RHEL5 系列的发布版中,才有类似文件)
CentOS 6.5 的内核模块存放目录为
/lib/modules/2.6.32-431.el6.i686/
如前所述,这个目录下存放不需要在系统引导阶段加载的硬件设备驱动程序;
其中,2.6.32-431.el6.i686 为执行命令 uname -r 的结果,取决于你系统的内核版本,这个值可能会有所不同;
每个单独的内核模块,都以 .ko 结尾
参考上面截图,内核模块中有一个是实现类似 iptables 的防火墙日志记录功能,位于目录
/lib/modules/2.6.32-431.el6.i686/kernel/net/netfilter
该目录下的 nf_conntrack.ko 和其家族模块,就是用于记录数据包出入的 IP 地址,端口,状态等信息的模块,在 centos 6.5 中,默认是开启(加载)的;在较低版本的 centos 中,模块的名字可能是以 ip_conntrack 为前缀,需要注意;
nf_conntrack.ko 及其家族模块,会将日志信息输出到基于内存的虚拟文件系统目录下
/proc/net/nf_conntrack
稍后我们将对记录中的每个字段做详细解释;
*****补充知识:与 iptables / netfilter 相关的内核模块,规则表与规则链
我们知道,位于用户态的 iptables 命令行工具,主要用来设置4个表的策略(规则),而相应的内核模块读取这些表中的规则,执行实际的包过滤任务:
filter 表,
/lib/modules/2.6.32-431.el6.i686/kernel/net/ipv4/iptable_filter.ko
将会根据该表中的策略来过滤数据包
nat 表,
/lib/modules/2.6.32-431.el6.i686/kernel/net/ipv4/iptable_nat.ko
将会根据该表中的策略来路由转发数据包,并且执行 SNAT(源地址转换)与 DNAT(目标地址转换)
mangle 表,
/lib/modules/2.6.32-431.el6.i686/kernel/net/ipv4/iptable_mangle.ko
将会根据该表中的策略来修改数据包
raw 表,
/lib/modules/2.6.32-431.el6.i686/kernel/net/ipv4/iptable_raw.ko
将会根据该表中的策略对状态跟踪机制提供支持
我们用后面介绍的 modinfo 命令,可以查看这4个内核模块的详细信息,如下:
在处理各种数据包时,根据防火墙规则的不同介入时机,iptables 共涉及5种默认规则链,其应用时间点分别对应如下:
INPUT链:当接收到防火墙本机地址的数据包(入站)时,应用此链中的规则。
OUTPUT链:当防火墙本机向外发送数据包(出站)时,应用此链中的规则。
FORWARD链:当接收到需要通过防火墙发送给其他地址的数据包(转发)时,应用此链中的规则。
PREROUTING链:当数据包需要进入到 iptables 身后的内网时,应用此链中的规则。
POSTROUTING链:当数据包需要经由 iptables 从内网出去时,应用此链中的规则。
INPUT、OUTPUT链更多的应用在“主机防火墙”中,即主要针对运行 iptables 的服务器本机进出数据的安全控制;而FORWARD、PREROUTING、POSTROUTING链更多的应用在“网络防火墙”中,特别是运行 iptables 的服务器作为网关使用时的情况。
*****在 Linux 2.6.14 版内核以后,
对 netfilter 的显式扩展提供支持的内核模块,这些模块统一存放在目录
/lib/modules/2.6.32-431.el6.i686/kernel/net/netfilter
下,这些以 .ko 为后缀的内核模块,是 netfilter 执行显式扩展的匹配任务时,需要用到的模块,例如 xt_string.ko
该目录中的模块可以同时操作基于 IPv4 和 IPv6 网络层协议的数据包,
而在 Linux 2.6.14 版内核以前,只能操作基于 IPv4 数据包的模块,存放在
/lib/modules/2.6.32-431.el6.i686/kernel/net/ipv4/netfilter 目录下
只能操作基于 IPv6 数据包的模块,存放在
/lib/modules/2.6.32-431.el6.i686/kernel/net/ipv6/netfilter
目录下,当然 Linux 2.6.14 版以后的内核也包括了这2个目录
*****对 iptables 的显式扩展用户接口提供支持的“库模块”,这些模块统一存放在目录
/lib/xtables
下,这些以 .so 为后缀的库模块,被 iptables 用来检查用户输入的规则是否正确,并将其写入内存中,供 netfilter 在执行包过滤时的参考依据,例如和前面模块相对应的 libxt_string.so
在上面这个例子中,首先在设置规则时,通过 iptables 命令的 -m 选项,调用 libxt_string.so 来对用户输入的“基于数据包的应用层字串匹配”的显式扩展规则进行语法检查,无误后写入内存,
然后 xt_string.ko 被加载进内核(和执行 modprobe xt_string.ko 命令的结果一样),
netfilter 根据该条规则调用 xt_string.ko 执行相应的过滤任务。
需要指出,当升级内核时,不能只升级,添加 netfilter 的显式扩展模块,和它相对应的 iptables 库模块也必须添加进来,否则即使 netfilter 可以执行新的数据包过滤功能,你却不能通过 iptables 来指示它如何工作,这一点非常重要
*****补充知识:关于 proc 虚拟文件系统
执行如下命令查看 proc 虚拟文件系统的信息:
[root@centos6-5 ~]# mount | grep "proc"
proc on /proc type proc (rw)
none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)
第一条信息显示,设备文件名为 “proc”,挂载点为 /proc,类型为 proc
简单的讲,所有 /proc 目录下的“文件”,都不存在于实际的磁盘分区上,它是内核构建的虚拟目录,系统重启导致内核重新加载,从而先前在 /proc 中的信息也会丢失;通过向 /proc 目录中的文件读信息,可以获取当前由内核维护的数据,并且是动态更新的,这就是后面我们查看 /proc/net/nf_conntrack 的 IP 记录时,每隔一段时间就变化的原因;
而通过向 /proc 目录中的文件写信息,可以改变内核当前的运行参数;但是这些运行参数将在系统重启后丢失
(原因前面讲过,由于主存储器—内存—为易失性存储器,重新加电导致内核在内存中“重生”,从而由内核维护的 /proc 文件系统丢失)
例如,执行如下命令可以开启内核路由转发的功能,让 linux 主机充当 NAT 路由器,但是系统重启后失效:
[root@centos6-5 ~]# echo "1" > /proc/sys/net/ipv4/ip_forward
要查看系统当前已经加载的模块,使用 lsmod 命令,例如:
[root@centos6-5 netfilter]# lsmod | grep "nf_conn"
nf_conntrack_ipv4 7694 2
nf_defrag_ipv4 1039 1 nf_conntrack_ipv4
nf_conntrack_ipv6 7207 1
nf_defrag_ipv6 8897 1 nf_conntrack_ipv6
nf_conntrack 65661 3 nf_conntrack_ipv4,nf_conntrack_ipv6,xt_state
ipv6 261089 49 ip6t_REJECT,nf_conntrack_ipv6,nf_defrag_ipv6
可以查找到 centos6 6.5 系统默认已经加载的 nf_conntrack.ko 家族模块,一共有3个;注意,第一个字段为模块名,完整名应该是以 .ko 结尾;第二个字段为模块大小;
第三个字段为该模块正在被几个其它的模块使用,仅当这个值为0,即没有被任何其它模块使用时,该模块才可以通过命令 modprobe -r [模块名] 进行动态卸载;
第四个字段为占用该模块的其它模块列表;
注意,modprobe [模块名] 用于加载模块;并且临时生效,系统重启后不会自动加载模块;相同道理,通过 modprobe -r 卸载的模块,系统重启后又会自动加载;
因此,要修改成自动加载,需要编辑 /etc/rc.sysinit ,例如
echo "modprobe ip_vs" >> /etc/rc.sysinit
然后在重启系统时即刻生效;
要修改成自动卸载,将该文件中,对应的命令删除,重启系统时即刻生效;
modinfo [模块名] ,用于查看指定模块的信息,无论该模块当前是处于加载还是卸载状态,都可以查看:
使用如下命令可以显示最后8条 nf_conntrack 的日志记录:
[root@centos6-5 netfilter]# tail -n 8 /proc/net/nf_conntrack
******解压 .tar.gz 压缩包: tar -zxvf $.tar.gz
将 $ 换成要解压的压缩包名
打包成 .tar.gz 压缩包: tar -zcvf $.tar.gz [要打包的目录或文件名]
将 $ 换成打包后的压缩包名
解压 .tar.bz2 压缩包: tar -jxvf $.tar.bz2
将 $ 换成要解压的压缩包名
打包成 .tar.bz2 压缩包: tar -jcvf $.tar.bz2 [要打包的目录或文件名]
将 $ 换成打包后的压缩包名
注意:
一,参数 -v 为显示详细信息
二,参数 -f 必须放在最后面,否则会报错
三,解压 gz 或者 bz2 包时,默认会在当前工作目录下生成解包后的目录
四,对于相同大小的目录或文件,使用 bz2 格式压缩生成的包,比使用 gz 格式压缩生成的包要小,换言之,bz2 压缩格式有较高的压缩比
*****find 搜索命令实战
一,在根目录下查找包含字符串“wireshark”,大小在50~100 MBytes 的属性在
一天内发生改变的文件,以 MBytes 统计每个文件的大小并显示:
[root@centos6-5 ~]# find / -name *wireshark* -a -cmin -1440 -a -size +50M -a -size -100M -type f -exec du -m {} \;
二,在 /data 目录下查找所有 MP4 格式的,大小在10~500 MByres 的在五分钟
内被“看过”的影片,以 MBytes 统计每个影片的大小并显示:
[root@centos6-5 ~]# find /data -name *.mp4 -a -amin -5 -a -size +10M -a -size -500M -type f -exec du -m {} \;
三,在当前目录下查找所有 I 节点为 31531 的硬链接,并将其删除,删除前要求
用户确认:
[root@centos6-5 ~]# find . -inum 31531 -ok rm {} \;
*****GRUB 加密与使用光盘救援模式恢复硬盘 MBR 中损坏的 GRUB
首先, /etc/grub.conf 为经常使用的 GRUB 配置文件,但它实际只是符号链接,指向 /boot/grub/grub.conf 这个真正的 GRUB 配置文件:
[root@centos6-5vm ~]# ll /etc/grub.conf
lrwxrwxrwx. 1 root root 22 6月 25 17:03 /etc/grub.conf -> ../boot/grub/grub.conf
[root@centos6-5vm ~]# file /etc/grub.conf
/etc/grub.conf: symbolic link to `../boot/grub/grub.conf'
[root@centos6-5vm ~]# file /boot/grub/grub.conf
/boot/grub/grub.conf: ASCII text
最简单的 GRUB 加密方式,可以使用 grub-md5-crypt 命令:
[root@centos6-5vm 桌面]# grub-md5-crypt
Password:
Retype password:
e21e4epx1$iGRjdbg112`1``UhFOGiedwfef
将生成的密文,复制到 GRUB 的配置文件中,注意插入命令的位置,在系统名称(title)后面
root@centos6-5vm ~]# vim /etc/grub.conf
grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE: You have a /boot partition. This means that
# all kernel and initrd paths are relative to /boot/, eg.
# root (hd0,0)
# kernel /vmlinuz-version ro root=/dev/mapper/vg_centos65vm-lv_root
# initrd /initrd-[generic-]version.img
#boot=/dev/sda
default=0
timeout=5
splashp_w_picpath=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.32-431.el6.i686)
password --md5 e21e4epx1$iGRjdbg112`1``UhFOGiedwfef
lock
root (hd0,0)
kernel /vmlinuz-2.6.32-431.el6.i686 ro root=/dev/mapper/vg_centos65vm-lv_root rd_LVM_LV=vg_centos65vm/lv_swap rd_NO_LUKS rd_NO_MD crashkernel=128M LANG=zh_CN.UTF-8 rd_LVM_LV=vg_centos65vm/lv_root KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
initrd /initramfs-2.6.32-431.el6.i686.img
~
注意在 password 下面一行的 lock ,作用是验证失败后锁定
编辑后保存退出,重新启动,完成对 GRUB 的加密
友情提醒:上面的 GRUB 加密并非无懈可击,可以通过在启动时按任意键进入 GRUB 启动菜单,然后选择一个设置加密的内核,按下 e 键,进入实时编辑 GRUB 启动选项,就可以看到
password --md5 e21e4pex1$iGRjdb112`1``UhFOGiedwfef
上面这一项,与 grub.conf 中的完全一样,此时,我们只需按下 d 键,删除该行,以及删除其后的
lock 这一行,
然后在 kernel / vmlinuz- 这一行的末尾,添加 single 字串,这样就可以进入“单用户模式”,
对于 RedHat 系列及其衍生版(如 CentOS)Linux 而言,默认会以 root 用户进入单用户模式,而且不需要验证 root 用户的口令(密码),最危险的是,还可以任意变更 root 用户密码,修改后,执行命令 init 5 ,即可以新的 root 用户密码,进入多用户模式(带有 X-window 启动,正常联网功能,gnone 或 KDE 桌面环境等)
这意味着,基于物理接触的***者可以很轻易的破解你的 GRUB 与 root 密码双重保险,而且他不会修改到你的 grub.conf 配置文件,因为所有这些操作都是利用了 GRUB 可以实时编辑启动选项的这一安全漏洞;
要避免***者绕过 GRUB 的密码验证流程,一种解决办法是,将
password --md5 及其后面的哈希密文密码,放在 grub.conf 的全局配置段中,具体而言,就是所有 title 的前面,这样,***者无法避开 GRUB 的密码验证流程,如下截图所示:
或者,我们可以在 BIOS 中设置启动密码,这样,BIOS 会验证用户输入的启动密码,如果验证成功,则将对 CPU 的控制权,转交给磁盘 MBR 上的引导代码,从而加载 GRUB ,如果验证失败, BIOS 将中断启动流程并阻止用户访问 GRUB;
更进一步,应该将存储重要业务资料,个人敏感信息的机箱放在带锁的柜子中,避免硬盘被盗取,或者***者通过打开机箱,操作主板上的 BIOS 放电,重置跳线,来清除 BIOS 启动密码
*****上面提到,可以通过实时编辑 GRUB 的启动选项,进入单用户模式;也可以进入紧急模式,方法是在 kernel / vmlinuz- 这一行末尾,添加 emergency 字串,然后按下 b 键启动,以紧急模式启动时,只会挂载根文件系统 /
对于配备光驱的台式机 PC 或服务器而言,如果 MBR 损坏(例如安装双系统时,GRUB 被后来安装的 windows 覆写),可以将安装盘放入光驱,以救援模式启动,将安装盘上的 GRUB 重新安装到硬盘上来恢复
为了模拟 MBR 损坏的情况,可以使用 dd 命令将硬盘上的 0 磁道 0 柱面前 446个字节的“引导代码”,用零覆盖:
[root@centos6-5vm 桌面]# dd if=/dev/zero of=/dev/sda bs=1 count=446
记录了446+0 的读入
记录了446+0 的写出
446字节(446 B)已复制,0.0162632 秒,27.4 kB/秒
[root@centos6-5vm 桌面]# sync
执行 df 命令,查看分区表中的记录是否正确,因为前面我们只覆写了 MBR 的 512个字节中的前 446个字节,后面 66 个字节的分区表应该是正常的,如果 df 命令不能输出分区信息,说明分区表也丢失,需要用其它的硬盘检测,维修工具来重建分区表:
[root@centos6-5vm 桌面]# df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/mapper/vg_centos65vm-lv_root ext4 35G 7.6G 26G 23% /
tmpfs tmpfs 948M 224K 947M 1% /dev/shm
/dev/sda1 ext4 485M 37M 423M 9% /boot
/dev/sr0 iso9660 3.6G 3.6G 0 100% /media/CentOS_6.5_Final
由于我们使用虚拟机中的光驱,因此连接宿主(host)机上的 安装光盘 ISO 镜像,然后从光驱启动系统,进行 GRUB 的修复:
[root@centos6-5vm 桌面]# init 6
应该避免在救援模式中使用中文字符界面,这可能会导致某些选项和信息出现乱码字符;一般而言,救援模式下,不需要网络支持(根据你的需求选择)
引导加载程序是 BISO 与 操作系统之间的桥梁
用于引导加载 Linux 内核的引导加载程序,可以是 GRUB(GRand Unified Bootloader)或者 LILO(LInux LOader)
正常情况下,当使用光盘或其它介质安装系统时,GRUB 或 LILO 应该已经随系统一同安装到了硬盘上
GRUB 是一个分两阶段执行的引导加载程序:
stage1
位于硬盘的 MBR(准确地讲是硬盘的0磁道上的1号扇区,通常,每个磁道有63个扇区,每扇区512字节) ,作为引导代码,占用446字节存储空间,
stage1 的任务是加载与执行 stage2;
stage2
识别所有磁盘上可用的 Linux 内核;
加载并执行用户选定的内核;提供实时编辑引导配置选项的功能,可以替代因错误配置或损坏而不能启动系统的 /boot/grub/grub.conf 文件;
GRUB 的命令行模式可用于修复磁盘上 MBR 中损坏的引导代码;以及识别所有不在 /boot/grub/grub.conf 文件中,显式指定的 Linux 内核
在系统启动进入 stage2 后,会提示按任意键,再按下 e 键,就可以实时编辑 GRUB 的引导配置选项,此处的内容与 /boot/grub/grub.conf 文件中对应的部分一致;
下面给出两个独立磁盘上,所有引导选项的配置(实例来自于我的2个虚拟机),其中“title”选项那一行后面的文本字串,就是用于显示在 GRUB 主界面的系统内核名称,可以随意命名,但是,“kernel”选项一行后面的文本字串,在磁盘上必须有实际的内核映像文件与其对应,否则无法引导该内核:
title CentOS (2.6.32-431.el6.i686)
password --md5 ********************
lock
root (hd0,0)
kernel /vmlinuz-2.6.32-431.el6.i686 ro root=/dev/mapper/vg_centos65vm-lv_root rd_LVM_LV=vg_centos65vm/lv_swap rd_NO_LUKS rd_NO_MD crashkernel=128M LANG=zh_CN.UTF-8 rd_LVM_LV=vg_centos65vm/lv_root KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
initrd /initramfs-2.6.32-431.el6.i686.img
title CentOS (2.6.32-431.el6.i686)
root (hd0,0)
kernel /vmlinuz-2.6.32-431.el6.i686 ro root=UUID=48020e84-b23b-4009-88ab-4cce351acb82 rd_NO_LUKS KEYBOARDTYPE=pc KEYTABLE=us rd_NO_MD crashkernel=auto LANG=zh_CN.UTF-8 rd_NO_LVM rd_NO_DM rhgb quiet
initrd /initramfs-2.6.32-431.el6.i686.img
在上面2个例子中,
Linux 内核的实际存储位置,应该是在 /boot 路径下,不过这里只需以 / 路径后接内核映像的名称就可以了;另外, root= 后面的字段,应该是你的根文件系统的挂载点,或者所在的磁盘设备文件名,或 UUID;
应该将正常配置的 grub.conf 的内容备份,或者抄写下来,当无法启动系统时,进入 GRUB 的实时编辑引导配置界面,然后对照输入其中的内容,这样就可以正常的引导内核,启动系统了
如果在 stage2 的内核选择菜单画面,按 c 键,则可以进入 GRUB 的命令行模式
在该模式下,可以手动指定一个没有在 grub.conf 配置文件中列出的内核项,并且
GRUB 的命令行模式模仿了 BASH ,支持按下 tab 键进行文件自动搜索,文件名自动补全的功能,以 root (hd0,0) 这个配置项为例,只需要键入 root (h ,然后按下 tab 键,GRUB 即会自动搜索并列出磁盘上所有能被识别的分区,以及文件系统类型,方便用户进行选择;
需要注意的是,在 GRUB 中,hd0 的语义是指, BIOS 设置中的主(master)IDE 硬盘或 SATA,SCSI 硬盘;hd1 是从(slave)IDE 磁盘或第二块 SATA,SCSI 硬盘;
道理很简单,因为运行 GRUB 时,Linux 内核还没有加载到内存,所以不可能使用 Linux 内核定义的 sda 为第一块 SATA 硬盘;hda 为第一块 IDE 硬盘。。。。(并且,GRUB 属于 GNU 的一个项目,它与由 linuz 领导的 Linux 内核团队是两个不同的开发小组,再加上,GRUB 被设计成“跨”操作系统平台的引导加载程序,种种因素造成了2者对存储设备编号的不同表述方式)
另外,GRUB 中的分区号,从0开始计数,因此,Linux 内核识别的第一块 SATA 硬盘的第一个分区(/dev/sda1),以 GRUB 的角度来看,是 hd0,0
例如,假设我们在安装系统时,指定 /dev/sda1 的挂载点为 /boot 文件系统,那么下面这个 grub.conf 中的全局配置项:
splashp_w_picpath=(hd0,0)/grub/splash.xpm.gz
作用是指定进入 GRUB 主界面时,显示的背景图像路径为:
/boot/grub/grub/splash.xpm.gz
类似地,只需键入 kernel /v ,然后按下 tab 键,GRUB 即会搜索并列出 /boot 目录下的所有版本的 Linux 内核,即使它不在 grub.conf 配置文件中定义,我们也可以通过这种方式来指定,并引导该内核;内核文件后面的参数,即
ro root=UUID=0d1c20e2-6fb8-4b37-8ced-987df661f7c8 rd_NO_LUKS KEYBOARDTYPE=pc KEYTABLE=us rd_NO_MD crashkernel=128M LANG=zh_CN.UTF-8 rd_NO_LVM rd_NO_DM rhgb quiet
将作为引导内核时,使用的参数,如一开始提到的,该参数与系统启动完成后,在 shell 提示符下执行命令 cat /proc/cmdline
所得到的结果是一致的;上面的这些参数用于告诉内核,系统的各种配置,例如根文件系统( / )以只读模式挂载,并且指定其所在的磁盘分区的 UUID;系统的键盘类型为标准美国键盘;系统语言为 UTF-8 编码的 zn_CN (简体中文)。。。等等
比较难理解的是 quiet,指定这个参数后,在启动时将不会输出与内核相关的信息;如果想看到在内核启动时的相关信息,那么删除这个参数;
另一个艰涩的参数是 rhgb (Red Hat Graphical Boot),用于“提前”启动 X-Server,如果删除它将减少启动过程耗费的时间
另外几个比较常用的内核引导参数,
一个是 selinux=0 ,我们知道,当前 SELinux 已经直接集成到 Linux 内核中,使用该参数则表明本次启动 Linux 时,关闭 SELinux ;
另外一个是 rdshell ,该参数用于输出在内核启动过程中的调试信息,对于排除在系统启动过程中,与 Linux 内核相关的错误很有帮助,一般可以快速找出启动失败的原因;
还有一个是 confirm,该参数会经由内核传递给它创建的 init 进程,用于指示
init 以确认模式启动,在该模式下,会逐一询问用户是否执行 /etc/rc.d/rc.sysinit 初始化脚本中的每一条命令,确认模式的一个巨大优势在于,它不会改动 rc.sysinit 脚本,并且用户的选择仅在本次启动系统生效,如果想要禁止特定的系统初始化脚本运行,又怕误改 rc.sysinit 导致系统启动失败,那么就应该以 confirm 模式启动
下面是使用 tab 键的实际效果截图:
*****shutdown,halt,reboot,init 0/6 相关知识
init 0/6 调用一系列脚本的符号链接(/etc/rc.d/rc0/6.d/K*)来使系统优雅关机
(“优雅”在这里是指, init 带参数 stop 执行 /etc/rc.d/init.d/ 目录下的实际脚本,来终止服务,守护进程);
以用户执行 init 0 命令为例子,init 会执行 /etc/rc.d/rc0.d/ 目录下的两个特别符号链接:
S00killall 用于关闭那些无法通过该目录下正常的以 K 为前缀的停止脚本关闭的服务进程;
S01halt 用于正确卸载文件系统并且关闭主机电源;这两个符号链接指向 /etc/rc.d/init.d/ 目录下各自的 killall 脚本与 halt 脚本;
reboot (重启)并不执行这些过程,reboot 更像是一个 kernel 级别的命令,不对应用使用脚本,
更为正式的关机命令为 shutdown
shutdown -h [时间,now 为立即] [自定义简短说明]
shutdown -h 调用命令 halt 关闭系统,可以直接执行 halt ,后者执行 sync 系统调用,等待文件系统上,所有的写操作完成,卸载文件系统,然后关闭系统;
自定义的简短说明会在当前的 tty ,pts/n 上,向所有其它tty 以及 pts/n 广播消息,用于通知所有在线用户;
shutdown -r [时间,now 为立即] [自定义简短说明]
shutdown -r 调用命令 reboot 重启系统,一般而言,建议优先执行 init 6 ,在出问题的状况下或强制重启时才考虑使用 reboot,或者 shutdown -r
shutdown 命令的优势在于(相较 halt ,reboot ,init 0/6) 可以自定义关闭或重启系统的时点,以及通知所有用户;
重启系统:init 6 , shutdown -r ,reboot
关闭系统:init 0 , shutdown -h ,halt
上面任何一个命令都能确保正确地卸载文件系统,实际上可以不加区分地使用
*****串行线路,用户终端设备,虚拟终端(tty),getty 进程,更改开启的虚拟终端数量
Linux 内核启动后 ,通过 init 创建 getty 进程(/sbin/mingetty),后者监控每个用户从各自的终端设备通过串行线路连接到系统并请求登录的事件,
因为用户终端设备现在很少见到,因此 UNIX/Linux 引入了 tty (虚拟终端)这一概念来模拟用户终端设备;
默认情况下(以 CentOS 6.5 为例),init 在运行级别 2,3,4,5 分别启动6个 getty 来监控并处理用户的登录请求,这意味着,默认 Linux 就支持6个
不同的用户在线工作
(回想一下 ,linux 是支持多用户与多任务的操作系统,多用户意味着多个虚拟终端;多任务意味着通过在进程间上下文切换来执行任务调度)
实际上,各种 Linux 发行版,都支持多达 60 个以上的虚拟终端,只是 init 进程默认仅在最前面的 6 个虚拟终端(tty1~tty6)上,创建 getty 进程监控登录事件,因此默认“可用的”虚拟终端有 6 个,第 n 个控制台可以通过设备节点
/dev/ttyn 来访问:
[root@centos6-5 桌面]# ls /dev/tty
tty tty11 tty15 tty19 tty22 tty26 tty3 tty33 tty37 tty40 tty44 tty48 tty51 tty55 tty59 tty62 tty9 ttyS3
tty0 tty12 tty16 tty2 tty23 tty27 tty30 tty34 tty38 tty41 tty45 tty49 tty52 tty56 tty6 tty63 ttyS0
tty1 tty13 tty17 tty20 tty24 tty28 tty31 tty35 tty39 tty42 tty46 tty5 tty53 tty57 tty60 tty7 ttyS1
tty10 tty14 tty18 tty21 tty25 tty29 tty32 tty36 tty4 tty43 tty47 tty50 tty54 tty58 tty61 tty8 ttyS2
举例来讲,要让 “第 10 个 tty”(需要修改 init 进程默认创建的 getty 进程 数量为 10)动态显示 /var/log/message 日志文件的内容,则需要先通过 ctrl + alt + F10 组合键,切换到 tty10 上登录,然后切换到当前的 tty
(或者运行 X-window 的 tty),然后在 shell 中执行如下命令,再切换到 tty10,即可查看动态变化的日志内容:
[root@centos6-5vm ~]# tail -f /var/log/messages > /dev/tty10 &
[1] 2256
对于没有启动 X-window 的任何已启动 getty 进程的 tty 而言,登录后将不能使用鼠标设备,此时可以按组合键 shift + PageUp 或 shift + PageDown 来滚动屏幕上的输出信息以方便查看那些在屏幕的显示范围之外的信息
在 RedHat 5.x / CentOS 5.x 中,通过编辑 /etc/inittab 文件,可以更改系统默认启动的虚拟终端数量:
[root@centos6-5 桌面]# vim /etc/inittab
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
在运行级别 5 下,通常 tty1 用于启动 X-window ;要减少 tty 的数量,只需注释掉相应的行即可,要增加 tty 的数量,则添加相应的行;编辑 /etc/inittab 后,执行 init q 命令,强迫 init 进程重读 /etc/inittab ,如果不能生效,则执行 init 6 命令重启系统;然后执行 init n 切换到想要进入的运行级别,
注意,上面的配置项意味着,在运行级别 2,3,4,5 都将启动 6 个 tty;
respawn 动作表明,如果用户退出当前 getty 进程处理的 tty ,那么 getty 进程终止,然后 init 进程通过再次执行命令 /sbin/mingetty ttyn ,
“重生”一个 getty 进程来监控当前 tty 上的用户登录请求;
以 ps -ef | grep "tty" 命令,或者按下 ctrl + alt + F1~Fn 组合键即可验证;
[root@centos6-5 桌面]# ps -ef | grep tty
root 2715 1 0 12:44 tty2 00:00:00 /sbin/mingetty /dev/tty2
root 2717 1 0 12:44 tty3 00:00:00 /sbin/mingetty /dev/tty3
root 2719 1 0 12:44 tty4 00:00:00 /sbin/mingetty /dev/tty4
root 2721 1 0 12:44 tty5 00:00:00 /sbin/mingetty /dev/tty5
root 2723 1 0 12:44 tty6 00:00:00 /sbin/mingetty /dev/tty6
root 2750 2747 8 12:44 tty1 00:15:01 /usr/bin/Xorg :0 -nr -verbose -audit 4 -auth /var/run/gdm/auth-for-gdm-ei8Y4j/database -nolisten tcp vt1
root 17587 3428 0 15:40 pts/4 00:00:00 grep tt
在 RedHat 6.x / CentOS 6.5 中,编辑 /etc/inittab 的办法已经不再有效,发行版将与虚拟终端,ctrl + alt + del 组合键有关的配置文件,组织在 /etc/init/ 目录下,并用各自独立的文件存放:
[root@centos6-5vm /]# ls -l /etc/init/
总用量 68
-rw-r--r--. 1 root root 412 10月 10 2013 control-alt-delete.conf
-rw-r--r--. 1 root root 560 10月 10 2013 plymouth-shutdown.conf
-rw-r--r--. 1 root root 357 10月 10 2013 prefdm.conf
-rw-r--r--. 1 root root 417 10月 10 2013 rc.conf
-rw-r--r--. 1 root root 1046 10月 10 2013 rcS.conf
-rw-r--r--. 1 root root 430 10月 10 2013 rcS-emergency.conf
-rw-r--r--. 1 root root 725 10月 10 2013 rcS-sulogin.conf
-rw-r--r--. 1 root root 726 11月 23 2013 readahead-disable-services.conf
-rw-r--r--. 1 root root 1302 10月 10 2013 serial.conf
-rw-r--r--. 1 root root 473 10月 10 2013 start-ttys.conf
-rw-r--r--. 1 root root 335 10月 10 2013 tty.conf
其中,control-alt-delete.conf 配置文件,用来定义当用户按下 ctrl + alt + del 组合键时,执行的命令,注意,在运行级别 5 上,由于 X 服务器屏蔽了
ctrl + alt + del 组合键,所以这个配置文件中定义的命令只在运行级别 1 2 3 4
中生效;
要修改系统启动的 tty 数量,首先编辑 /etc/init/start-ttys.conf 配置文件:
[root@centos6-5vm /]# vim /etc/init/start-ttys.conf
#
# This service starts the configured number of gettys.
#
# Do not edit this file directly. If you want to change the behaviour,
# please create a file start-ttys.override and put your changes there.
start on stopped rc RUNLEVEL=[2345]
env ACTIVE_CONSOLES=/dev/tty[1-2]
env X_TTY=/dev/tty1
task
script
. /etc/sysconfig/init
for tty in $(echo $ACTIVE_CONSOLES) ; do
[ "$RUNLEVEL" = "5" -a "$tty" = "$X_TTY" ] && continue
initctl start tty TTY=$tty
done
end script
:wq!
上面将启动的 tty 数量修改成只有 2 个
( env ACTIVE_CONSOLES=/dev/tty[1-2]),
保存退出 vim ;注意,虽然在注释中提醒我们:不要直接编辑该文件,而是复制一个该文件的副本,改名为 start-ttys.override ,编辑副本,init 会优先读取副本配置文件中的设置(要改动 /etc/init/ 目录下的其它配置文件,也应该遵循此建议),但是这里我修改的是虚拟机中的配置文件,而且预先创建了“快照”,改动后如果系统故障,则还原到快照的时间点即可,这也是使用 vmware workstation
软件的一大优势
再来,还需要修改 /etc/sysconfig/init 配置文件,回顾前面的内容,我们提到,init 执行 /etc/rc.d/rc.sysinit 脚本时,需要参考 /etc/sysconfig/ 目录下的各种配置文件,才能确定一些变量的“值”:
[root@centos6-5vm /]# vim /etc/sysconfig/init
# What ttys should gettys be started on?
ACTIVE_CONSOLES=/dev/tty[1-2]
:wq!
可以看到,start-ttys.conf 配置文件中用到的环境变量 ACTIVE_CONSOLES ,实际在此处定义,需要确保两个地方的值一致,
保存修改后退出 vim ,执行 init 6 命令重启系统,然后切换到 2,3,4,5 中的任意一个运行级别,执行 ps -ef | grep "tty" 命令,或者按下 ctrl + alt + F1~Fn 组合键验证,系统启动的 tty 数量变为 2 个