目录
- [一、Linux 系统启动流程之一:运行级别与 GRUB](#一、Linux 系统启动流程之一:运行级别与 GRUB)
- [二、Linux 系统启动流程之二:内核与 init](#二、Linux 系统启动流程之二:内核与 init)
- [三、Bash 与 SysV 服务脚本](#三、Bash 与 SysV 服务脚本)
- [四、Linux 内核编译与系统裁减之一](#四、Linux 内核编译与系统裁减之一)
- 五、系统裁减与函数库(第十五章要点)
- 六、任务计划与信号捕捉(第十六章)
- [七、日志系统 syslog/rsyslog](#七、日志系统 syslog/rsyslog)
- [八、SSH 与 dropbear](#八、SSH 与 dropbear)
📌 本章核心概念一览
| 概念 | 一句话 |
|---|---|
| 启动流程 | POST → BIOS → MBR(bootloader) → Kernel → initrd/initramfs → ROOTFS → /sbin/init |
| 运行级别 | 0 关机、1 单用户、2 多用户无 NFS、3 多用户文本、4 保留、5 图形、6 重启 |
| GRUB | Stage1(MBR) → Stage1_5 → Stage2(/boot/grub),由 grub.conf 配置 |
| chroot | 把指定目录当作根目录运行程序,用于隔离、急救、initrd 切换根 |
| init | PID=1,读 /etc/inittab,执行 rc.sysinit、按级别启停服务、开终端 |
| SysV 服务 | /etc/rc.d/init.d 脚本 + rc#.d 下 K*/S* 链接,chkconfig 管理 |
| 内核模块 | insmod/modprobe 加载,rmmod/modprobe -r 卸载,lsmod/modinfo 查看 |
| at / cron | at 一次任务,cron 周期任务;crontab -e 编辑用户任务 |
| syslog/rsyslog | facility.priority → action;rsyslog 为 syslog 增强版 |
| SSH | 22/tcp,openssh C/S;ssh-keygen、ssh-copy-id、scp 常用 |
一、Linux 系统启动流程之一:运行级别与 GRUB 🚀
1.1 从加电到 init 的总体流程
POST → BIOS(Boot Sequence) → MBR(bootloader, 446字节) → Kernel → initrd/initramfs → (ROOTFS) /sbin/init (/etc/inittab)

生活例子:启动像"起床流程"------闹钟响(BIOS) → 看课表选今天穿啥(GRUB 选内核) → 穿衣服(内核加载) → 出卧室(initrd 挂根) → 到客厅(根文件系统) → 家长喊你(init 读 inittab 开始干活)。
1.1.1 为什么需要 initrd/initramfs?(更深入)
内核刚启动时,往往还没有:
- 访问根分区所在磁盘/阵列/NFS 所需的驱动
- 访问根分区所需的文件系统模块(ext3/ext4/xfs...)
- 自动发现真实根分区(UUID/LVM/RAID)并挂载的逻辑
所以需要一个"过渡环境":
- initrd/initramfs:包含必要模块 + 初始化脚本
- 作用:加载驱动/模块 → 找到根分区 → 先只读挂载 →
switch_root/chroot到真实 ROOTFS →exec /sbin/init
新增生活例子:initrd/initramfs 像"出门前塞进包里的应急工具包"------门锁坏了(根分区驱动/模块没就绪)你进不了家(ROOTFS),先用工具包把门打开,再进屋按正常流程生活(启动 init、服务、终端)。
排障思路(按层定位):
- BIOS/MBR:提示找不到启动盘 → 看启动顺序、磁盘是否识别
- GRUB :进
grub>/grub rescue>→ 检查 (hd0,0) 是否为 /boot,能否cat /boot/vmlinuz* - Kernel :Kernel panic / 找不到 root → 检查 grub 的
root=、initrd 是否匹配当前内核、根分区是否存在 - init/rc.sysinit :卡在某服务/挂载 → 看
/var/log/messages、dmesg、必要时进单用户模式修复
1.2 运行级别(runlevel)
| 级别 | 含义 |
|---|---|
| 0 | halt,关机 |
| 1 | 单用户模式,直接以管理员身份切入(s, S, single) |
| 2 | 多用户,无 NFS |
| 3 | 多用户,文本模式 |
| 4 | 保留 |
| 5 | 多用户,图形模式 |
| 6 | reboot,重启 |
查看 :runlevel、who -r。
- S / s:Single user mode,为单用户模式作准备,不直接给用户选。
- 多数桌面 Linux 默认 runlevel 5 (图形界面);多数服务器 默认 3(字符界面)。runlevel 1 和 2 多用于调试。
- 可理解为类似 Windows 的 Normal、safemode、command prompt only;优势 是可用
init命令在系统空闲时切换 runlevel,关机会切到 0 或 6 以关闭所有进程。
对比:runlevel 3 与 5(服务器 vs 桌面)
| 对比项 | runlevel 3(多用户文本) | runlevel 5(多用户图形) |
|---|---|---|
| 默认场景 | 服务器、无显示器主机 | 桌面、工作站 |
| 是否起 X | 否 | 是(X11 + 登录管理器) |
| 资源占用 | 较低 | 较高(显存、内存、CPU) |
| 典型用途 | 生产服务、SSH 运维 | 日常办公、图形软件 |
⏹️ 0 关机
🧑🔧 1 单用户
👥 2 多用户无NFS
🖥️ 3 多用户文本
🪟 5 图形
🔁 6 重启
生活例子:运行级别像"公司工作模式"------0 是下班关灯、1 是只有老板在修电脑、3 是正常上班无大屏、5 是开会开投影、6 是全员重启再上班。
为什么要有运行级别?
不同场景需要不同的"服务组合":修系统时希望尽量少服务 (单用户),跑服务器时不需要图形(3),桌面用户要图形(5),关机和重启要按顺序停服务 (0/6)。用数字统一表示"当前状态",init 和脚本就能按级别批量启停服务,而不用为每种组合各写一套逻辑;同时可以在不关机的条件下用 init N 切换级别,方便运维。
1.3 源码编译安装三步(configure / make / make install)
- ./configure:检查编译环境(编译工具、库、头文件),设定编译选项,生成 Makefile。
- make:编译。
- make install:安装到系统。
1.4 内核模块目录与 chroot
- 内核模块目录:
/lib/modules/内核版本号/,如cd /lib/modules/2.6.32-504.el6.x86_64/,其下kernel/为内核模块,drivers/net/为网卡驱动。 - 查看内核占用:
du -sh /lib/modules/2.6.32-504.el6.x86_64/(例:107M)。 - chroot :在指定目录下当作根目录运行命令。
chroot 示例:构造最小根环境并进入
bash
mkdir /test/virroot -pv
mkdir /test/virroot/bin
cp /bin/bash /test/virroot/bin/
chroot /test/virroot/ # 此时会报错,缺库
mkdir /test/virroot/lib
ldd /bin/bash
cp /lib64/libtinfo.so.5 /test/virroot/lib/
cp /lib64/libdl.so.2 /test/virroot/lib/
cp /lib64/libc.so.6 /test/virroot/lib/
cp /lib64/ld-linux-x86-64.so.2 /test/virroot/lib/
chroot /test/virroot/
chroot 命令说明:change root directory。指定新根后,进程看到的"/"变为新目录,用途包括:增强安全性(限制用户只能访问新根)、隔离环境做开发与测试、系统急救、从 initrd 切换根到真实根。
生活例子:chroot 像"在样板间里办公"------你推门进去后,眼前的"客厅""卧室"都是样板间的,不是真房子;你在里面跑的 ls、bash 也是样板间自带的,和外面系统隔开。
使用场景:构建最小根环境、安装新系统前在目标分区 chroot 装包、initrd 里挂载真实根后 chroot 再 exec /sbin/init。
- 语法:
chroot 目录 [要执行的指令]。 - 例:
chroot target /bin/sh以 target 为根运行 shell,exit 或 Ctrl+D 退出回到本机;chroot target /bin/ls运行 target 里的 ls 后立即返回本机。 - 注意:需 root;若直接
chroot target不跟命令,默认会找 target 下的/bin/bash。若程序依赖动态库,需用ldd查看并把库拷到新根对应路径(如newRoot/lib),否则运行会报错。
例子:用 chroot 运行自己编译的程序
bash
mkdir newRoot
gcc main.c # 生成 a.out,功能例如输出 hello
ldd a.out # 查看依赖,输出示例:
# linux-gate.so.1 => (0xb8034000)
# libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7eab000)
# /lib/ld-linux.so.2 (0xb801a000)
cp a.out newRoot
mkdir newRoot/lib
cp /lib/tls/i686/cmov/libc.so.6 newRoot/lib
cp /lib/ld-linux.so.2 newRoot/lib
# newRoot 内容:a.out lib/
su
chroot newRoot /a.out # 即可正确运行
若没有其他库,只拷 a.out 也能运行;例如静态编译的 busybox ,其 /bin/busybox 就不依赖其他库。
对比:chroot 里用动态链接 vs 静态编译
| 对比项 | 动态链接 + 拷 so | 静态编译(如 busybox -static) |
|---|---|---|
| 准备 | 需 ldd 查依赖并拷齐 so | 无需拷库,一个二进制即可 |
| 体积 | 多个 so,总体积可能大 | 单文件大,但无额外 so |
| 维护 | 升级 glibc 要同步 so | 与系统库无关,兼容性好 |
| 典型用途 | 通用 chroot、临时环境 | 最小根、initrd、嵌入式、急救盘 |
为什么需要 chroot?
一是安全 :把不可信用户限制在"假根"里,看不到真系统的 /etc、/usr,降低越权风险。二是隔离 :编译、测试、装包在独立目录树里做,不污染主机。三是启动与急救:initrd 挂上真实根后必须"换根"再 exec init,否则 init 看到的还是 initrd 的 /;系统坏了挂载根分区后 chroot 进去修,用的就是真实根上的命令和配置。
1.5 GRUB 与 grub.conf
Bootloader :MBR 中 446 字节,负责加载内核。常见有 LILO、GRUB(GRand Unified Bootloader)。
对比:LILO 与 GRUB
| 对比项 | LILO | GRUB |
|---|---|---|
| 配置 | 改配置后需重写 MBR | 读配置文件,改完不用写 MBR |
| 文件系统 | 不认文件系统,用扇区号 | 认常见文件系统,用路径 |
| 菜单与交互 | 简单 | 支持菜单、编辑、命令行 |
| 现状 | 老系统可见 | 主流,GRUB2 更常见 |
- Stage1:在 MBR。
- Stage1_5:介于 MBR 与 Stage2 之间。
- Stage2 :在
/boot/grub/,提供菜单与加载内核。
grub.conf 示例 (/etc/grub.conf 常为 /boot/grub/grub.conf 的符号链接):
bash
default=0 # 默认启动第 0 个 title
timeout=5 # 等待用户选择的秒数
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
password redhat
password --md5 $1$HKXJ51$B9Z8A.X//XA.AtzU1.KuG.
title Red Hat Enterprise Linux Server (2.6.18-308.el5)
root (hd0,0)
kernel /vmlinuz-2.6.18-308.el5 ro root=/dev/vol0/root rhgb quiet
initrd /initrd-2.6.18-308.el5.img
password --md5 $1$HKXJ51$B9Z8A.X//XA.AtzU1.KuG.
title Install Red Hat Enterprise Linux 5
root (hd0,0)
kernel /vmlinuz-5 ks=http://172.16.0.1/workstation.cfg ksdevice=eth0 noipv6
initrd /initrd-5
password --md5 $1$FSUEU/$uhUUc8USBK5QAXc.BfW4m.
- root (hd0,0):GRUB 中硬盘为 hd#,分区为 (hd#,N),表示内核所在设备。
- kernel :内核路径及传给内核的参数(如
ro root=...)。 - initrd:ramdisk 镜像路径。
为什么 kernel 和 initrd 要一一对应?
initrd 里打包的是当前内核版本需要的模块(如 ext4、dm、LVM)和脚本;不同内核版本模块名、路径可能不同,脚本里调用的工具也可能依赖该版本。用 2.6.18 的内核配 2.6.32 的 initrd,可能缺驱动或脚本报错,导致找不到根、起不来。所以升级内核后要重做或选用匹配的 initrd,grub.conf 里两行也要一起改。
💽 MBR
1️⃣ Stage1
🧩 Stage1_5
2️⃣ Stage2
/boot/grub/
🐧 kernel
🧰 initrd
- 查看 grub 相关:
ls /boot/grub/、rpm -q grub、ls -lh /boot/grub;红帽 5 可用grub、grub-crypt命令。 - 安装图形界面工具示例:
yum install gimp,gimp &打开 gimp。
为什么 GRUB 要分 Stage1 / Stage1_5 / Stage2?
MBR 只有 446 字节给 bootloader,放不下菜单和文件系统驱动。Stage1 只做"从哪读下一步";Stage1_5 放在 /boot 所在分区,负责识别该分区文件系统并加载 Stage2;Stage2 才提供菜单、读 grub.conf、加载 kernel 和 initrd。这样既满足"从 MBR 就能起链",又能用大一点的 Stage2 做复杂逻辑和多种内核选择。
破坏 grub 模拟与恢复 :dd if=/dev/zero of=/dev/sda count=1 bs=400 会破坏 MBR(原稿为 bs400,实际常用 bs=400 或 bs=446);恢复需用安装盘或 rescue 模式重装 grub。操作前务必 sync 同步磁盘。
1.6 sync 命令
- sync :把内存缓冲区数据立即写入磁盘,更新超块信息。
- buffer :解决写磁盘效率,写盘时先写 buffer,再择机落盘;cache:解决读磁盘效率。
对比:buffer 与 cache
| 对比项 | buffer(缓冲) | cache(缓存) |
|---|---|---|
| 主要方向 | 写:数据先落 buffer 再写盘 | 读:数据从盘读到 cache 再给程序 |
| 目的 | 合并写、减少小块 I/O | 减少重复读盘、加速访问 |
| 掉电影响 | 未 sync 的 buffer 可能丢 | 只影响性能,不产生"写丢" |
- 若不手动
sync就断电,可能丢数据。sync 会 flush 文件系统 buffer,数据真正写盘并释放 buffer。系统也会周期性自动 sync,非正常关机前建议手动执行一次。 - 语法:
sync(选项);关机和写盘后建议执行sync。
为什么写盘后要 sync?
写文件时数据先进内存 buffer,内核稍后再写回磁盘。若这期间断电或直接拔电,buffer 里未落盘的数据会丢。sync 会把这些缓冲刷到磁盘,减少"文件写了但重启后没看到"的风险;在虚拟机/ U 盘操作后、脚本里大量写盘后,建议显式 sync,再关机或拔设备。
1.7 grub 命令行修复启动(grub> 手动引导)
当 GRUB 损坏但硬盘数据完好、出现 grub> 时,可手工指定内核与 initrd 引导:
root (hd0,后按两次 TAB 列出磁盘;选硬盘后root (hd0,1)再 TAB 看分区。cat /boot/vmTAB 确认 vmlinuz 存在;cat /boot/initrdTAB 确认 initrd 存在。cat /sbin/initTAB 确认根分区(有 /sbin/init 的分区为根所在)。- 依次执行(按实际分区和文件名改):
root (hd0,1)/* 假设 /dev/hda2 为 /boot */kernel /boot/vmlinuz-2.6.15-26-386 ro root=/dev/hda3/* 假设 /dev/hda3 为 / */initrd /boot/initrd.img-2.6.15-26-386boot
关键 :如何确定 /boot、/、/sbin 所在分区------用 cat /boot/vm TAB、cat /boot/initrd TAB、cat /sbin/init TAB 试探;出现对应文件名即说明该分区正确。只需四个命令:root、kernel、initrd、boot。grub 下输入 help 可看全部命令(如 cat 查看文件内容)。
grub 命令选项(原稿):--batch 批处理;--boot-drive= 指定 stage2 引导驱动器;--config-file 指定配置文件;--device-map= 设备映射文件;--help;--install-partition= 指定 stage2 安装分区;--no-config-file;--no-pager;--preset-menu;--probe-second-floppy;--read-only 等。
1.8 yum 常用用法(原稿摘录)
- 安装一个:
yum -y install httpd - 安装多个相似:
yum -y install httpd* - 安装多个不相似:
yum -y install httpd php php-gd mysql - 卸载:
yum -y remove httpd/httpd*/httpd php php-gd mysql - 找不到包时:先 search 。例如要执行
iostat查 CPU 与存储设备状态,发现没有该命令;执行yum install iostat又说找不到包,可用:yum search iostat查与 iostat 相关的安装包;若只记得一部分名称,例如装与 png 有关的:yum search png | grep png可找到如 libpng 等包名。
为什么 yum install 有时"找不到包"?
yum install 要的是完整包名 (或可匹配的包名)。很多命令来自"子包"或名字和你想的不一样(如 iostat 在 sysstat 里)。用 yum search 关键词 会按描述和包名搜索,能列出相关包,再从中选要装的;记不清包名时这是标准做法。
二、Linux 系统启动流程之二:内核与 init ⚙️
2.1 runlevel 与 who -r
- runlevel:打印当前运行级别(以及上一级别)。
- who -r:显示当前运行级别。
2.2 uname 命令
- uname:打印系统信息(内核版本、架构、主机名等)。
| 选项 | 含义 |
|---|---|
| -a | 全部信息 |
| -m | 机器类型 |
| -n | 主机名 |
| -r | 内核发行号 |
| -s | 系统名 |
| -v | 内核版本信息 |
| -p | 处理器类型 |
| -i | 硬件平台 |
| -o | 操作系统名 |
示例输出(原稿):
text
# uname # 单独使用相当于 uname -s
Linux
# uname -a
Linux localhost 2.6.18-348.6.1.el5 #1 SMP Tue May 21 15:34:22 EDT 2013 i686 i686 i386 GNU/Linux
# uname -m
i686
# uname -n
localhost
# uname -r
2.6.18-4-686
# uname -s
Linux
# uname -v
#1 SMP Tue May 21 15:34:22 EDT 2013
# uname -p
i686
# uname -i
i386
# uname -o
GNU/Linux
# uname --version
uname (GNU coreutils) 5.97
2.3 内核初始化与 /sbin/init
内核初始化大致步骤:
- 设备探测
- 驱动初始化(可能从 initrd/initramfs 加载模块)
- 以只读方式挂载根文件系统
- 启动第一个用户态进程 init(PID 1)
为什么内核先以只读挂载根?
根文件系统在挂上前无法保证完整性(可能上次异常断电)。先以 ro 挂载,内核和 initrd 里的脚本可以安全读配置、加载模块;再由 rc.sysinit 做检查(如 fsck)后执行 remount,rw,避免在文件系统不干净时写入导致进一步损坏。
/sbin/init 读取 /etc/inittab。不同发行版 init 实现不同:
- 传统 SysV:/etc/inittab 直接驱动。
- upstart:Ubuntu 等,事件驱动,d-bus。
- systemd:RHEL7+、多数现代发行版。
对比:SysV init / upstart / systemd
| 对比项 | SysV init | upstart | systemd |
|---|---|---|---|
| 驱动方式 | 顺序执行 inittab、rc 脚本 | 事件驱动(设备、服务事件) | 单元依赖 + 并行 |
| 配置 | inittab + rc#.d 链接 | /etc/init/*.conf | /etc/systemd/system/ 等 |
| 并行 | 基本串行 | 可并行 | 大量并行 |
| 典型系统 | RHEL5、老发行版 | 旧版 Ubuntu | RHEL7+、主流发行版 |
inittab 行格式 :id:runlevels:action:process
| 字段 | 含义 |
|---|---|
| id | 标识符 |
| runlevels | 在哪些级别执行 |
| action | 何时/如何执行 |
| process | 要运行的程序或脚本 |
常见 action:initdefault(默认级别)、sysinit(系统初始化)、wait(进入该级别时执行并等待)、respawn(进程退出则重启)。
对比:inittab 中常见 action
| action | 执行时机 | 进程退出后 | 典型用途 |
|---|---|---|---|
| wait | 进该级别时执行一次 | 不自动再启 | rc 0--6、一次性任务 |
| respawn | 进该级别时启动 | 退出即重新拉起 | getty、保持登录终端 |
| once | 进该级别时执行一次 | 不等待、不重启 | 一次性后台任务 |
| sysinit | 系统最早阶段 | 不适用 | rc.sysinit |
| initdefault | 不执行,仅指定默认级别 | 不适用 | 设定默认 runlevel |
为什么虚拟终端要用 respawn?
getty/mingetty 在用户登出(exit)后就退出;若不用 respawn,退一次该终端就没了。设为 respawn 后,进程一退出 init 就立刻重新拉起,保证 1--6 号虚拟终端始终有登录界面,用户可随时再登录。
示例:
id:3:initdefault:默认运行级别 3。si::sysinit:/etc/rc.d/rc.sysinit系统初始化脚本。
🚦 /sbin/init
📄 读 /etc/inittab
🎯 initdefault
🧰 sysinit rc.sysinit
🏃 wait rc 0-6
🛑 K* stop
✅ S* start
2.4 /etc/rc.d/rc.sysinit 主要任务
- 激活 udev 和 SELinux
- 根据 /etc/sysctl.conf 设定内核参数
- 设定时钟、键盘映射
- 启用交换分区
- 设置主机名
- 检测并以读写方式重新挂载根文件系统
- 激活 RAID 和 LVM
- 启用磁盘配额
- 根据 /etc/fstab 检查并挂载其它文件系统
- 清理过期锁和 PID 文件
按级别启停服务(逻辑等价于):
bash
for I in /etc/rc3.d/K*; do $I stop; done
for I in /etc/rc3.d/S*; do $I start; done
先执行 K* 关闭,再执行 S* 启动;数字为顺序,越小越先执行。
对比:K 与 S 在 rc#.d 中的角色**
| 对比项 | K*(Kill/Stop) | S*(Start) |
|---|---|---|
| 执行参数 | 脚本被调用时传 stop | 脚本被调用时传 start |
| 何时跑 | 离开某级别、或进入该级别前"关掉不该有的" | 进入某级别时"启动该级别要的" |
| 数字 | KK:关的顺序,小号先关 | SS:开的顺序,小号先开 |
| 示例 | rc0.d/K22httpd → 关机时关 httpd | rc3.d/S55httpd → 进 3 时开 httpd |
为什么先执行 K 再执行 S ?**
进入某 runlevel 时,要把不属于该级别的服务关掉(K*),再按该级别把该开的服务开起来(S*)。若先开 S* 再关 K*,会有一段时间"新旧服务并存",可能抢端口、抢设备。先统一关再统一开,状态更干净;关机/重启级别(0/6)也是先 K* 停服务,再做 umount、halt/reboot。
为什么 rc.sysinit 要做这么多步?
内核只挂上根并启动 init,其余"像一台正常机器"的事都要用户态做:设主机名、挂其它分区、起 swap、起 udev/LVM/RAID、清锁和 PID 等。rc.sysinit 把这些只做一次的全局初始化集中在一起,和"按级别启停服务"的 rc N 分开,逻辑更清晰,也方便裁减和排错。
2.5 ldd 命令
- ldd :打印程序或库所依赖的共享库列表。
- ldd 不是可执行程序 ,而是一个 shell 脚本;原理是设置环境变量(如
LD_TRACE_LOADED_OBJECTS、LD_WARN、LD_BIND_NOW、LD_LIBRARY_VERSION、LD_VERBOSE等),让 ld-linux.so (ELF 动态库装载器)先于程序执行并只显示依赖、不真正跑程序。可测试:export LD_TRACE_LOADED_OBJECTS=1后执行任意程序(如 ls),会只显示依赖。 - 等价用法:
/lib/ld-linux.so.2 --list program(相当于ldd program)。 - 选项(原稿):-v 详细信息;-u 打印未使用的直接依赖;-d 执行重定位并报告丢失对象;-r 执行数据对象和函数重定位并报告丢失。
- 例:
ldd /bin/bash。
为什么 chroot 里跑程序要拷 so?
程序运行时由 ld-linux.so 按 RPATH/默认路径找动态库;chroot 后"/"变了,它只在新根下找,找不到就报 "cannot open shared object file"。所以要把 ldd 列出的 so 按路径拷到新根的对应位置(如 /lib、/lib64),新根下的程序才能正常跑。
2.6 内核设计风格与 initrd/initramfs
- 单内核:如 Linux,支持模块化(.ko)。
- 微内核:如 Windows、Solaris。
- 内核模块目录:
/lib/modules/内核版本号/。 - Red Hat 5 :ramdisk → initrd;Red Hat 6:ramfs → initramfs。
对比:initrd 与 initramfs
| 对比项 | initrd (ramdisk) | initramfs (ramfs) |
|---|---|---|
| 实现 | 块设备,需文件系统 | 基于 tmpfs,直接 cpio |
| 典型格式 | 镜像文件,可挂成块设备 | cpio+gzip 打包 |
| 修改/扩展 | 需按块设备操作 | 解包即目录,改完再打包 |
| 常见发行版 | RHEL5、老系统 | RHEL6+、主流现用 |
对比:单内核与微内核
| 对比项 | 单内核(如 Linux) | 微内核(如部分 Windows 设计) |
|---|---|---|
| 核心逻辑 | 核心与驱动多在同一空间 | 核心极小,驱动等跑在用户态服务 |
| 性能 | 系统调用路径短,性能好 | 跨进程通信多,开销相对大 |
| 稳定性 | 驱动崩可能拖垮内核 | 驱动崩不直接拖垮核心 |
| 模块化 | 通过 .ko 模块扩展 | 天然以服务为单位扩展 |
启动链 :
MBR(bootloader) → Kernel → initrd(initramfs) → (ROOTFS) → /sbin/init(/etc/inittab)
三、Bash 与 SysV 服务脚本 📜
3.1 inittab 与 rc.d 结构
- l0:0:wait:/etc/rc.d/rc 0 等:进入某级别时执行
/etc/rc.d/rc N。 - /etc/rc.d/rc#.d/ 下 K * 与 S * 均为链接,指向 /etc/rc.d/init.d/(或 /etc/init.d)下的服务脚本。
为什么用 K/S 链接而不是直接放脚本?**
同一份 init.d 脚本要在多个 runlevel 里复用:在 3 可能是"开",在 0/6 是"关"。用链接 + 不同名字(K22xxx、S77xxx)可以:同一脚本在 rc3.d 是 S77(start),在 rc0.d 是 K22(stop);数字表示顺序,便于控制谁先谁后,且增删服务只需改链接,不用改 init.d 里的脚本本身。
SysV 服务脚本 支持参数:start | stop | restart | status,以及 reload | configtest 等。
3.2 服务脚本共同特征(chkconfig、description)
脚本内通常包含:
- # chkconfig: runlevels SS KK
runlevels 表示在哪些级别默认建 S* 链接,其余级别建 K*;SS 为启动顺序,KK 为关闭顺序。 - # description: 说明
简要描述服务。
为什么要有 SS 和 KK 两个数字?
启动时可能有依赖:例如网络要在 NFS 之前起来,所以网络服务用较小的 S 号先执行。关闭时顺序往往要反过来:先关依赖别人的(如应用),再关被依赖的(如网络)。SS 控制"进该级别时 S* 谁先谁后",KK 控制"离开该级别时 K* 谁先谁后",这样同一脚本在不同时机能以正确顺序启停。
3.3 chkconfig 命令
| 用法 | 作用 |
|---|---|
| chkconfig --list | 查看所有独立守护进程的启动设定 |
| chkconfig --list SERVICE_NAME | 查看某服务(下次启动生效) |
| chkconfig --add SERVICE_NAME | 在 rc#.d 下创建链接 |
| chkconfig --del SERVICE_NAME | 移除链接 |
| chkconfig [--level RUNLEVELS] SERVICE_NAME on|off | 设定某级别开/关;省略级别时默认 2345 |
3.4 守护进程类型
- 独立守护进程:直接受 runlevel 控制。
- xinetd:超级守护进程,管理瞬时守护进程;瞬时守护进程不直接关联运行级别。
对比:独立守护进程与 xinetd 瞬时守护进程
| 对比项 | 独立守护进程(如 httpd、sshd) | xinetd 管理的瞬时守护进程 |
|---|---|---|
| 是否常驻 | 常驻内存,一直监听 | 平时不跑,有连接时由 xinetd 拉起 |
| 与 runlevel | rc#.d 里 S* 直接控制 | 由 xinetd 按需启动 |
| 资源 | 占常驻内存 | 无连接时不占进程 |
| 适用 | 高并发、常访问的服务 | 低频服务(如 telnet、tftp) |
3.5 自定义 SysV 服务脚本示例(myservice)
bash
#!/bin/bash
#
# chkconfig: 2345 77 22
# description: Test Service
#
LOCKFILE=/var/lock/subsys/myservice
status() {
if [ -e $LOCKFILE ]; then
echo "Running..."
else
echo "Stopped."
fi
}
usage() {
echo "`basename $0` {start|stop|restart|status}"
}
case $1 in
start)
echo "Starting..."
touch $LOCKFILE ;;
stop)
echo "Stopping..."
rm -f $LOCKFILE &> /dev/null ;;
restart)
echo "Restarting..." ;;
status)
status ;;
*)
usage ;;
esac
放入 /etc/rc.d/init.d/myservice 并执行 chkconfig --add myservice 后,可用 find ./ -name "*myservice*" 查看生成的链接,例如(在 /etc/rc.d 下):
text
./rc1.d/K22myservice
./init.d/myservice
./rc2.d/S77myservice
./rc5.d/S77myservice
./rc3.d/S77myservice
./rc0.d/K22myservice
./rc4.d/S77myservice
./rc6.d/K22myservice
即在 2345 级别为 S77,在 016 级别为 K22。
生活例子:SysV 服务像"楼层总闸"------每个房间(运行级别)有一排开关(K* 和 S*);进来时先关 K* 再开 S*,出去时关 S*;chkconfig 就是贴标签决定"这个房间要开哪几盏灯"。
3.6 /etc/rc.d/rc.local 与 inittab 完成的任务
- 系统在对应运行级别脚本执行之后 会执行 /etc/rc.d/rc.local(准确说是"应执行的一个脚本"),不方便写成独立服务的开机自启内容可写在此文件。
/etc/inittab 完成的任务(原稿):1)设定默认运行级别;2)运行系统初始化脚本;3)运行指定运行级别对应目录下的脚本;4)设定 Ctrl+Alt+Del 组合键的操作;5)定义 UPS 电源在电源故障/恢复时执行的操作;6)启动虚拟终端(2345 级别);7)启动图形终端(5 级别)。
为什么还要 rc.local?
不是所有开机要做的事都适合做成"服务"(带 start/stop/status、受 chkconfig 管理)。例如改一次内核参数、跑一段一次性脚本、挂个临时设备等,写成独立服务太重。rc.local 是"在所有级别脚本跑完后执行一次"的兜底脚本,把这类零散需求集中在一个文件里,不改动 rc#.d 和 init.d。
四、Linux 内核编译与系统裁减之一 🔧
4.1 内核模块管理命令
| 命令 | 作用 |
|---|---|
| lsmod | 查看已加载模块 |
| insmod /PATH/TO/MODULE_FILE | 加载模块(需路径,不自动解决依赖) |
| modprobe MOD_NAME | 加载模块(按依赖加载) |
| modprobe -r MOD_NAME | 卸载模块 |
| rmmod MOD_NAME | 卸载模块 |
| modinfo MOD_NAME | 查看模块信息 |
| depmod /PATH/TO/MODULES_DIR | 生成模块依赖信息(原稿写"移除"为笔误,应为依赖扫描) |
对比:insmod 与 modprobe
| 对比项 | insmod | modprobe |
|---|---|---|
| 参数 | 需写完整 .ko 路径 | 写模块名(不带 .ko) |
| 依赖 | 不处理,需自己按顺序加载 | 按 depmod 依赖自动加载 |
| 卸载 | rmmod 需自己考虑依赖顺序 | modprobe -r 按依赖逆序卸 |
| 使用建议 | 明确知道单模块、无依赖时用 | 日常推荐,省心 |
insmod 选项 (原稿):-f 不检查内核版本强制载入;-k 自动卸除;-m 输出载入信息;-o 指定模块名;-p 测试能否载入;-s 写入系统记录;-v 详细;-x 不汇出外部符号;-X 汇出所有外部符号。参数为内核模块文件路径。insmod 需绝对路径,且不自动解决依赖。
modprobe 选项 (原稿):-a/--all 载入全部;-c/--show-conf 显示模块设置;-d debug;-l/--list 列出可用模块(读取 /lib/modules/uname -r);-r 移除;-t 指定类型;-v 详细。示例 :modprobe -c 查看 alias 等;modprobe -l 列出模块路径(如 /lib/modules/2.6.18-348.6.1.el5/kernel/net/netfilter/xt_statistic.ko);modprobe vfat 挂载 vfat 模块(模块名不带 .ko/.o 后缀);modprobe -r 模块名 与 rmmod 功能相同。
示例 :insmod /lib/modules/2.6.18-8.el5/kernel/drivers/md/raid1.ko``modprobe vfat、modprobe -r floppy``modinfo cdrom
生活例子:内核模块像"可插拔的扩展卡"------需要 U 盘时插上 modprobe usb-storage,不用就 modprobe -r 拔掉,不用重启整台机器。
为什么内核要支持"模块"而不是全编进内核?
若所有驱动和功能都编进内核,内核会非常大、启动慢、占内存,且换硬件就要换内核。模块化后:常用、必需的编进内核或随 initrd 加载;不常用的(如某款网卡、某个文件系统)按需 insmod/modprobe,不用就不加载,节省资源;更新驱动也只需换 .ko 文件,不必重编整个内核。
为什么有 insmod 还要 modprobe?
insmod 只认"一个 .ko 文件",不处理依赖。很多模块依赖别的模块(如某网卡依赖 mii),若自己按顺序 insmod 容易漏或顺序错。modprobe 会读 depmod 生成的依赖信息,按依赖顺序加载,卸载时也可按依赖逆序卸,所以日常更常用 modprobe。
4.2 /proc、/sys 与内核参数
- /proc、/sys:伪文件系统,用于用户空间访问、监控内核。
- /proc/sys/:不少文件可写,用于调整内核参数。
- 修改方式一:
echo VALUE > /proc/sys/... - 修改方式二:
sysctl -w kernel.hostname=...,与echo写入 /proc/sys 等价。 - 永久生效 :编辑 /etc/sysctl.conf ,然后执行 sysctl -p 加载。
- 查看:
sysctl -a、cat /proc/sys/kernel/hostname等。
示例 :
echo 1 > /proc/sys/vm/drop_caches(注意原稿拼写为 drop_cashes,实际为 drop_caches)
sysctl -w vm.drop_caches=1 与上面等价,能立即生效但不永久 。
sysctl -w kernel.hostname="mylab.magedu.com"
永久生效 :改 /etc/sysctl.conf ,再执行 sysctl -p 加载。
swappiness 等交换内存参数也可在 /proc/sys/vm/ 或 sysctl 中设定。
查看:cat /proc/sys/kernel/hostname、sysctl -a 显示所有内核参数及其值。
为什么要有 /proc 和 /sys?
内核运行时的状态(进程列表、内存、设备树、参数等)需要让用户态程序能读、部分可写,又不能随便让程序直接访问内核内存。用伪文件系统把内核数据映射成"文件",用 read/write 就能查询或修改,接口统一、权限可沿用文件权限,工具(ps、top、sysctl)也只需用普通文件 API。
为什么改内核参数还要 /etc/sysctl.conf?
echo > /proc/sys/... 或 sysctl -w 只对当前运行有效,重启就没了。把需要长期生效的写进 /etc/sysctl.conf,开机时由 rc.sysinit(或 systemd)执行 sysctl -p 加载,就能保证每次启动都是同一套参数(如 net.ipv4.ip_forward、vm.swappiness)。
对比:/proc 与 /sys
| 对比项 | /proc | /sys(sysfs) |
|---|---|---|
| 侧重 | 进程、内核运行状态、部分可调参数 | 设备树、驱动、硬件拓扑、属性 |
| 典型内容 | 进程目录、meminfo、cpuinfo、sys/ 下可调参数 | 块设备、网卡、总线、class 等 |
| 可写 | 部分(如 /proc/sys/) | 部分设备属性可写 |
| 调参 | echo、sysctl 常改 /proc/sys | 有时配合 udev、脚本改 /sys |
对比:内核参数临时改 vs 永久改
| 方式 | 临时生效(当前运行) | 永久生效(重启后仍有效) |
|---|---|---|
| 操作 | echo V > /proc/sys/... 或 sysctl -w key=value |
编辑 /etc/sysctl.conf 后执行 sysctl -p |
| 重启后 | 丢失 | 由 rc.sysinit/systemd 加载 |
4.3 内核编译步骤(简述)
- 配置:
make menuconfig/make gconfig/make kconfig,生成 .config。 - 编译:
make。 - 安装模块:
make modules_install。 - 安装内核:
make install。
二次编译前清理 :make clean;彻底清理(含 .config):make mrproper(需备份 .config 再执行)。
内核功能三种选择(编译时):1)不使用;2)编译成内核模块;3)编译进内核。
为什么内核功能有三种选择?
"不使用"可以减小内核体积、减少攻击面;"编成模块"可以按需加载,灵活省内存;"编进内核"适合启动阶段就必须有的(如根文件系统、磁盘驱动),避免 initrd 还没挂上就缺功能。选哪种取决于:是否每次启动都要、是否依赖很多其它选项、是否想减小内核体积。
准备编译环境示例 (原稿):挂载本地光驱------cd /etc/yum.repos.d/,写 local.repo(baseurl=file:///media/cdrom/Server,enabled=1,gpgcheck=0),mkdir /media/cdrom,mount /dev/cdrom /media/cdrom,yum groupinstall "Development Tools" "Development Libraries" -y。从服务器下载内核:lftp 172.16.0.1,get linux-2.6.28.10.tar.gz。解压:tar xf linux-2.6.28.10.tar.gz -C /usr/src,做链接 linux → linux-2.6.28.10,cd linux。配置:make gconfig (Gnome,需安装图形开发库)、make kconfig (KDE)、make menuconfig(文本菜单)。
screen :screen 开新屏,Ctrl+a d 拆除屏幕,screen -r ID 还原,screen -ls 显示已建立屏幕,exit 退出。
4.4 mkinitrd 与 Shell 变量截取
- 制作 initrd:
mkinitrd /boot/initrd-$(uname -r).img $(uname -r) - 变量截取(原稿):
${parameter#*word}最短匹配删前缀;${parameter##*word}最长匹配删前缀。${parameter%word*}最短匹配删后缀;${parameter%%word*}最长匹配删后缀。
- 例:FILE=/usr/local/src →
${FILE#*/}为 usr/local/src,${FILE##*/}为 src,${FILE%/*}为 /usr/local,${FILE%%/*}为删最长后缀后剩余(此处为空)。
4.5 复制二进制及其依赖库的脚本(原稿)
脚本思路:读入命令名,用 which 定位二进制,拷贝到目标根(如 /mnt/sysroot),并用 ldd 解析依赖,把所需 so 拷到目标根对应路径。
bash
#!/bin/bash
#
DEST=/mnt/sysroot
libcp() {
LIBPATH=${1%/*}
[ ! -d $DEST$LIBPATH ] && mkdir -p $DEST$LIBPATH
[ ! -e $DEST${1} ] && cp $1 $DEST$LIBPATH && echo "copy lib $1 finished."
}
bincp() {
CMDPATH=${1%/*}
[ ! -d $DEST$CMDPATH ] && mkdir -p $DEST$CMDPATH
[ ! -e $DEST${1} ] && cp $1 $DEST$CMDPATH
for LIB in `ldd $1 | grep -o "/.*lib\(64\)\{0,1\}/[^[:space:]]\{1,\}"`; do
libcp $LIB
done
}
read -p "Your command: " CMD
until [ $CMD == 'q' ]; do
! which $CMD &> /dev/null && echo "Wrong command" && read -p "Input again:" CMD && continue
COMMAND=` which $CMD | grep -v "^alias" | grep -o "[^[:space:]]\{1,\}"`
bincp $COMMAND
echo "copy $COMMAND finished."
read -p "Continue: " CMD
done
4.6 微型系统关机脚本 rc.sysdone(原稿)
用于在 runlevel 0 时卸载文件系统并关机:
bash
#!/bin/bash
#
sync
sleep 2
sync
mount | awk '{print $3}' | grep -v -E "\/(dev|proc|sys)?$" | sort -r | while read LINE; do
umount -n -f $LINE
[ $? -eq 0 ] && echo "Unmount $LINE finished." || echo "Can not unmount $LINE."
done
mount | awk '{print $3}' | while read LINE; do
mount -n -o remount,ro $LINE
[ $? -eq 0 ] && echo "Remount $LINE finished." || echo "Can not remount $LINE."
done
exec /sbin/halt -p
(以下为第十五章、第十六章在原文中的内容概要,保留要点与脚本,完整原文已融入各节;如需可再拆为独立小节展开。)
五、系统裁减与函数库(第十五章要点) 📦
5.1 /etc/inittab 详解(原稿)
- 登记项格式:id:runlevels:action:process(四段用冒号隔开)。
- id :唯一标识;runlevels :在该级别执行,可多级连写,空表示所有级别;action :执行条件;process:要运行的程序或命令。
action 常见取值 :respawn (进程终止后马上再启);wait (进入该 runlevel 时执行一次,离开时终止);initdefault (默认运行级别,不能为 0、6);sysinit (系统初始化,开机/重启时执行一次);powerwait / powerfail / powerokwait / powerfailnow (电源故障相关);ctrlaltdel (Ctrl+Alt+Del);boot / bootwait (引导时执行);off (若在运行则发信号等 20 秒后强杀);once(启动一次,不等待结束也不重启)。
RHEL 5.4 示例(原稿):
id:3:initdefault:默认级别 3(命令行)。si::sysinit:/etc/rc.d/rc.sysinit所有级别下执行系统初始化。l0:0:wait:/etc/rc.d/rc 0...l6:6:wait:/etc/rc.d/rc 6进入对应级别时执行 rc N,启用该级别 rcN.d 下 S*、禁用 K*。ca::ctrlaltdel:/sbin/shutdown -t3 -r now三键重启。pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"电源故障 2 分钟后关机。pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"电源恢复则取消关机。1:2345:respawn:/sbin/mingetty tty1...6:2345:respawn:/sbin/mingetty tty6六个虚拟终端,终止即 respawn。x:5:respawn:/etc/X11/prefdm -nodaemon仅级别 5 启动图形(删此行则无图形界面)。
5.2 /etc/rc.d/rc.sysinit 与最小系统
- rc.sysinit 主要做:检测并以读写方式重新挂载根;设定主机名;检测并挂载 /etc/fstab 中其它文件系统;启动 swap;初始化外围硬件;根据 /etc/sysctl.conf 设定内核参数;激活 udev 和 selinux;激活 LVM 和 RAID;清理过期锁和 PID;装载键映射。
- 最小系统仅需:inittab(如 id:3:initdefault、si::sysinit:/etc/rc.d/rc.sysinit)、rc.sysinit(内容可为 echo、insmod、ifconfig、/bin/bash 等最简步骤)。
5.3 关机、busybox、mount -n
- 关机命令 (红帽):
shutdown、halt、reboot、poweroff;init 0 关机,init 6 重启。 - busybox:不到 1M,可模拟数百个命令,常用于裁减与嵌入式。
- mount -n :挂载时不更新 /etc/mtab;
cat /proc/mounts可看当前挂载。
5.4 initrd 修改与根文件系统(原稿步骤摘要)
- 拷贝 vmlinuz 到目标 /mnt/boot/vmlinuz。
zcat /boot/initrd-xxx.img | cpio -id展开 initrd。- 修改其中脚本(如 mkrootdev 的根设备为 /dev/hda2)。
- 脚本中注释可 vim
.,+20s@#@g。 - 删除 lib 下 dm-* 等不需要文件。
find . | cpio -H newc --quiet -o | gzip -9 > /mnt/boot/initrd.gz重新打包。grub-install --root-directory=/mnt /dev/hda,编辑 /mnt/boot/grub/grub.conf(default、timeout、title、root、kernel、initrd)。- 根文件系统:mkdir 如 etc/rc.d/init.d、bin、sbin、proc、sys、dev、lib、root、usr、var、tmp、home 等;拷贝 /sbin/init、/bin/bash 及 ldd 依赖库;建 inittab、rc.sysinit(Welcome 与 /bin/bash 等)。
- 根只读时
touch /tmp/a.txt会失败,需mount -n -o remount,rw /;关机可用 exec /sbin/halt -p(exec 替换当前进程)。 - 建 rc.sysdone(sync;sleep;sync;umount 其它;remount 只读;exec /sbin/halt -p)、rc.reboot,在 inittab 中 l0:0:wait:/etc/rc.d/rc.sysdone、l6:6:wait:/etc/rc.d/rc.reboot;rc0.d/rc6.d 下可做 S99halt、S99reboot 链接到 init.d 脚本;rc 脚本根据 RUNLEVEL 执行 rc$RUNLEVEL.d 下 K* stop、S* start。
5.5 functions、tserver、主机名、网络、颜色
- /etc/rc.d/init.d/functions :提供 success、failure 等函数,用
stty -F /dev/console size取列数,用\033[32m等输出绿色 [ OK ]、红色 [ FAILED ]。 - tserver 示例:chkconfig 35 66 33,description,lockfile=/var/lock/subsys/$prog,start/stop/status/restart;在 rc3.d 下
ln -sv ../init.d/tserver S66tserver。 - 主机名:/etc/sysconfig/network 中 HOSTNAME=...,rc.sysinit 里 source 后
hostname $HOSTNAME。 - 网络:insmod 网卡模块(如 mii.ko、pcnet32.ko),ifcfg-eth0、network 脚本;可配合 agetty 的
-l /bin/bash。 - 颜色变量(原稿):T_RED、T_GREEN、T_YELLOW、T_BLUE、T_CYAN、T_BOLD、T_NORM(\033[1;31m 等)。
5.6 PAM、nsswitch、单用户
- PAM:可插拔认证模块,/etc/pam.d/*。
- nsswitch:名称解析,/etc/passwd、/etc/shadow、/etc/group 及 libnss_files.so、nsswitch.conf;也可接 NIS、LDAP、MySQL。
- 单用户模式:runlevel 1,用于修复与维护。
六、任务计划与信号捕捉(第十六章) ⏰
6.1 脚本小知识点
- 变量长度:
${#VARNAME} - 变量默认值:
${parameter:-word}、${parameter:=word}、${parameter:+word} - 子串:
${parameter:offset}、${parameter:offset:length} - 服务脚本可读配置文件:/etc/sysconfig/与脚本同名的配置文件;脚本中
. /etc/sysconfig/服务名。 - 局部变量:
local VAR_NAME=... - mktemp :创建临时文件或目录,如
mktemp /tmp/file.XX、mktemp -d /tmp/file.XX
6.2 信号与 trap
- trap 'COMMAND' 信号列表:在脚本中捕捉信号并执行命令。SIGINT(2)、SIGTERM(15) 可捕捉;SIGKILL(9) 不可捕捉。
- 例:
trap 'echo "wo not away..."' INT在 Ctrl+C 时打印而不退出;trap 'cleanup' INT在退出前删临时文件并 exit。
为什么 SIGKILL(9) 不能被 trap?
SIGKILL 是内核直接杀进程的"强制终止",不经过进程的信号处理逻辑。若允许用户态 trap SIGKILL,就无法实现"一定能杀死"的语义,系统会无法强制结束卡死或恶意进程。所以只有 SIGTERM、SIGINT 等可被进程捕获或忽略,SIGKILL 是最后手段。
为什么脚本里常用 trap 做 cleanup?
脚本可能创建临时文件、锁文件或打开资源;若用户 Ctrl+C 或脚本某处 exit,没清理就会留下垃圾或锁。在脚本开头 trap 'cleanup' INT TERM EXIT,在退出(包括被信号打断)时统一执行 cleanup,能保证临时文件删除、锁释放,避免"下次运行报错:文件已存在/锁未释放"。
ping 扫描示例(带 trap 清理):
bash
#!/bin/bash
#
NET=192.168.0
FILE=`mktemp /tmp/file.XXXXXXXX`
cleanup(){
echo "quit..."
rm -f $FILE
exit 1
}
trap 'cleanup' INT
for I in {200..254}; do
sleep 2
if ping -c 1 -W 1 $NET.$I &> /dev/null; then
echo "$NET.$I IS UP." | tee -a $FILE
else
echo "$NET.$I IS DOWN." | tee -a $FILE
fi
done
6.3 at:单次定时任务
- at 时间:在指定时间执行随后输入的命令,Ctrl+D 结束输入。
- 时间格式:绝对时间 HH:MM、DD.MM.YY、MM/DD/YY;相对时间
now+#minutes|hours|days|weeks;模糊时间 noon、midnight、teatime。 - at -l (atq):列出作业。at -d JOB_ID(atrm):删除作业。
- 白名单:/etc/at.allow;黑名单:/etc/at.deny。若 at.allow 存在则仅允许其中用户;若都不存在则仅 root 可用。
6.4 cron:周期性任务
- 系统 cron:/etc/crontab,格式为"分 时 日 月 周 用户 任务"。
- 用户 cron :
crontab -e编辑,/var/spool/cron/USERNAME,格式为"分 时 日 月 周 任务"。 - 时间字段:分(0-59)、时(0-23)、日(1-31)、月(1-12)、周(0-7,0 与 7 均为周日)。
- 通配:
*每、,离散、-连续、*/N每 N 单位。 - crontab -l 列出、crontab -r 删除全部、crontab -u USER 管理指定用户。
- 建议命令写绝对路径 ;脚本内显式设置 PATH。执行结果默认以邮件 发给用户;
&> /dev/null可避免发邮件。 - anacron:cron 的补充,用于补跑因关机等原因未执行的周期任务。
为什么 cron 里建议用绝对路径?
cron 执行任务时环境很"干净":PATH 通常只有 /usr/bin:/bin 等少量目录,且没有交互式 shell 的 profile。若脚本里写 python script.py 或 mysqldump,可能找不到命令(因为不在 PATH 或 PATH 里是别的版本)。用绝对路径(如 /usr/bin/python、/usr/bin/mysqldump)或脚本里先 export PATH=... 可避免"手动能跑、cron 不跑"的问题。
为什么 anacron 是 cron 的补充?
cron 假设机器一直开着:到点就跑。若机器那段时间关机(如笔记本合盖、服务器维护),那次的 cron 就错过了,不会补。anacron 按"天"检查:若发现某任务上次跑的时间超过设定(如 7 天),就补跑一次,适合"不保证 24 小时开机"的机器上的日/周任务。
对比:at 与 cron
| 对比项 | at | cron |
|---|---|---|
| 执行次数 | 一次,到点执行后即删 | 周期,按规则反复执行 |
| 配置方式 | at 时间 后输入命令 |
crontab -e 或 /etc/crontab |
| 适用 | 临时任务、延迟执行 | 日/周/月备份、定时脚本 |
对比:系统 cron 与用户 cron
| 对比项 | 系统 cron(/etc/crontab) | 用户 cron(crontab -e) |
|---|---|---|
| 配置文件 | /etc/crontab(多一个"用户"列) | /var/spool/cron/用户名 |
| 格式 | 分 时 日 月 周 用户 命令 | 分 时 日 月 周 命令 |
| 谁可编辑 | root | 各用户编辑自己的 |
| 典型用途 | run-parts 调用 cron.daily 等 | 用户自己的定时任务 |
- 系统 cron 示例 (/etc/crontab):
01 * * * * root run-parts /etc/cron.hourly;02 4 * * * root run-parts /etc/cron.daily;22 4 * * 0 root run-parts /etc/cron.weekly;42 4 1 * * root run-parts /etc/cron.monthly。 - cron 时间示例 (原稿):每天 10/14/16 点
0 0 10,14,16 * *;朝九晚五每半小时0 0/30 9-17 * *;每 3 分钟*/3 * * * *;每 2 小时0 */2 * * *。
🧾 at
一次
🕒 指定时间
🔁 cron
周期
✍️ crontab -e
🚀 执行
生活例子:at 像"订一次外卖,明天中午送到";cron 像"订报纸,每天早 8 点送"。
七、日志系统 syslog/rsyslog 📋
- syslogd :记录非内核日志。klogd :记录内核日志。内核启动信息也会到 /dev/console,并保存到 /var/log/dmesg;
dmesg可查看。 - 配置格式 :
facility.priority→ action(文件路径、@HOST、用户、* 等)。 - facility:auth、authpriv、cron、daemon、kern、mail、syslog、user、local0--local7、* 等。
- priority:debug、info、notice、warning/warn、err、crit、alert、emerg、*、none。
- RHEL6+ 常用 rsyslog(多线程增强),配置文件 /etc/rsyslog.conf、/etc/sysconfig/rsyslog。
- logrotate :负责日志轮转、压缩、删除旧日志;配置 /etc/logrotate.conf,可 include /etc/logrotate.d/;由 crond 每日执行 /etc/cron.daily/logrotate;可手动测试
logrotate -f /etc/logrotate.conf。 - 远程日志:
*.* @192.168.0.10(UDP)、*.* @@192.168.0.10(TCP)。 - 配置示例 (原稿):
mail.info /var/log/mail.log表示 mail 的 info 及以上级别写该文件;auth.=info @10.0.0.1表示 auth 的 info 发往远程主机;*.emerg *表示 emerg 发给所有登录用户。
对比:syslog 与 rsyslog
| 对比项 | syslogd(传统) | rsyslog(增强) |
|---|---|---|
| 能力 | 本地写文件、简单转发 | 多线程、TCP、过滤、模板、队列 |
| 配置 | 格式兼容 | 兼容并扩展语法 |
| 远程 | 多为 UDP | UDP/TCP、可靠传输 |
| 典型系统 | 老系统 | RHEL6+、主流发行版 |
为什么日志要分 facility 和 priority?
不同来源 (facility:kern、mail、auth、cron...)和不同严重程度(priority:debug、info、err、emerg...)混在一起会很难查。用 facility.priority 可以:把 mail 的写到 maillog、把认证的写到 secure、把严重的 emerg 发给所有登录用户;便于按"谁产生的"和"多严重"分类存储和告警,也便于做集中日志时按规则转发。
为什么还要 logrotate?
若日志只追加不轮转,单个文件会无限变大,占满磁盘、影响读写性能,且难以按时间归档。logrotate 按大小或时间切割、压缩、删除旧文件,既保留近期日志便于排查,又控制磁盘占用;配合 postrotate 里发 HUP 让 rsyslog 重新打开文件,不会丢日志。
八、SSH 与 dropbear 🔐
- Telnet:23/tcp,明文,不安全。
- SSH:22/tcp,加密;主机认证 + 用户认证(口令或密钥)。
- OpenSSH:服务端 sshd(/etc/ssh/sshd_config),客户端 ssh(/etc/ssh/ssh_config)。
- ssh-keygen :生成密钥对,默认 /.ssh/id_rsa、/.ssh/id_rsa.pub。
- ssh-copy-id -i ~/.ssh/id_rsa.pub USER@HOST:把公钥写入远程主机用户家目录 .ssh/authorized_keys。
- ssh USER@HOST 、ssh -l USER HOST 、ssh USER@HOST 'COMMAND'。
- scp :
scp SRC DEST,-r 递归,-a 保留属性。 - dropbear :嵌入式常用 SSH 服务端/客户端,轻量;主机密钥在 /etc/dropbear/(dropbear_rsa_host_key、dropbear_dss_host_key),dropbearkey -t rsa|dsa -f /path/to/KEY -s SIZE 生成;客户端为 dbclient。
- 无密码登录 (原稿):客户端
ssh-keygen -t rsa,ssh-copy-id -i ~/.ssh/id_rsa.pub root@IP(或手动 scp id_rsa.pub 到服务器,服务器cat id_rsa.pub >> .ssh/authorized_keys);若出现 "Agent admitted failure to sign using the key" 可在客户端执行 ssh-add。 - ssh 指定端口 :
ssh -2 -p port user@host。
对比:Telnet 与 SSH
| 对比项 | Telnet(23/tcp) | SSH(22/tcp) |
|---|---|---|
| 传输 | 明文 | 加密 |
| 认证 | 仅用户名/密码 | 主机认证 + 用户认证 |
| 安全 | 易窃听、篡改 | 防窃听、防中间人(配合密钥更佳) |
| 使用建议 | 仅内网/调试 | 生产环境唯一推荐 |
对比:OpenSSH 与 dropbear
| 对比项 | OpenSSH(sshd/ssh) | dropbear(dbclient) |
|---|---|---|
| 体积 | 较大、功能全 | 很小,适合嵌入式 |
| 功能 | scp、sftp、端口转发等 | 基础登录与执行 |
| 典型场景 | 服务器、桌面 | 路由器、IoT、裁减系统 |
对比:密码认证与密钥认证
| 对比项 | 密码认证 | 密钥认证(公钥/私钥) |
|---|---|---|
| 传输 | 密码经网络(加密后) | 仅公钥在服务器,私钥不离本机 |
| 暴力破解 | 可能被撞库 | 私钥不泄露则难以仿冒 |
| 自动化 | 需交互或存密码 | ssh-agent 后可免密、适合脚本 |
| 建议 | 可关闭密码登录 | 生产环境推荐仅密钥 |
为什么用 SSH 而不用 Telnet?
Telnet 是明文传输:账号、密码、命令、输出都在网络上可被窃听和篡改。SSH 对连接加密,并做主机认证 (防止中间人冒充服务器)和用户认证(口令或密钥),传输和登录都安全,所以生产环境远程登录一律用 SSH,Telnet 只用于内网调试或老旧设备。
为什么推荐用密钥而不是密码登录?
密码可能被暴力破解、钓鱼或泄露;且每次输入不方便自动化。密钥认证:私钥在本地、公钥在服务器,不经过网络传密码;可设空口令或配合 ssh-agent,一次加载后多台机器免密;脚本、CI、运维自动化都可基于密钥,无需交互输入密码。
小结
- 启动:POST → BIOS → MBR → Kernel → initrd → 根文件系统 → /sbin/init,由 inittab 与 rc.sysinit、rc N 完成级别与服务管理。
- GRUB:grub.conf 配置 default、timeout、title、root、kernel、initrd;损坏时可在 grub> 下手动指定 root、kernel、initrd、boot。
- chroot:切换根目录运行程序,需备齐二进制与依赖库。
- SysV 服务:init.d 脚本 + chkconfig 与 rc#.d 的 K*/S* 链接;rc.local 作收尾脚本。
- 内核模块:modprobe/insmod、rmmod/modprobe -r、lsmod、modinfo;/proc、/sys 与 sysctl 调整内核参数。
- 任务计划:at 单次、cron 周期;trap 在脚本中处理信号。
- 日志:syslog/rsyslog 的 facility.priority → action;logrotate 做轮转。
- SSH:openssh 与 dropbear,密钥认证与 scp 为日常运维必备。
使用场景速查:
| 主题 | 典型场景 |
|---|---|
| 运行级别 | 单用户修 root 密码(init 1)、服务器默认 3、桌面默认 5 |
| GRUB | 加内核参数 single、改 root=、多系统选择 |
| chroot | 系统进不去时挂载根分区后 chroot 修配置、做最小根 |
| chkconfig | 开机不启某服务 chkconfig xxx off、加自定义服务 |
| at/cron | 半夜备份用 cron、临时一次任务用 at |
| trap | 脚本临时文件退出时清理、Ctrl+C 时友好退出 |
| rsyslog | 统一收多机日志、按 facility 分文件 |
| SSH | 远程登录、scp 传文件、密钥免密 |