Linux 启动流程、GRUB、init、SysV 服务与内核初步

目录

  1. [一、Linux 系统启动流程之一:运行级别与 GRUB](#一、Linux 系统启动流程之一:运行级别与 GRUB)
  2. [二、Linux 系统启动流程之二:内核与 init](#二、Linux 系统启动流程之二:内核与 init)
  3. [三、Bash 与 SysV 服务脚本](#三、Bash 与 SysV 服务脚本)
  4. [四、Linux 内核编译与系统裁减之一](#四、Linux 内核编译与系统裁减之一)
  5. 五、系统裁减与函数库(第十五章要点)
  6. 六、任务计划与信号捕捉(第十六章)
  7. [七、日志系统 syslog/rsyslog](#七、日志系统 syslog/rsyslog)
  8. [八、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/messagesdmesg、必要时进单用户模式修复

1.2 运行级别(runlevel)

级别 含义
0 halt,关机
1 单用户模式,直接以管理员身份切入(s, S, single)
2 多用户,无 NFS
3 多用户,文本模式
4 保留
5 多用户,图形模式
6 reboot,重启

查看runlevelwho -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 grubls -lh /boot/grub;红帽 5 可用 grubgrub-crypt 命令。
  • 安装图形界面工具示例:yum install gimpgimp & 打开 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=400bs=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 引导:

  1. root (hd0, 后按两次 TAB 列出磁盘;选硬盘后 root (hd0,1) 再 TAB 看分区。
  2. cat /boot/vm TAB 确认 vmlinuz 存在;cat /boot/initrd TAB 确认 initrd 存在。
  3. cat /sbin/init TAB 确认根分区(有 /sbin/init 的分区为根所在)。
  4. 依次执行(按实际分区和文件名改):
    • 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-386
    • boot

关键 :如何确定 /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

内核初始化大致步骤:

  1. 设备探测
  2. 驱动初始化(可能从 initrd/initramfs 加载模块)
  3. 以只读方式挂载根文件系统
  4. 启动第一个用户态进程 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 主要任务

  1. 激活 udev 和 SELinux
  2. 根据 /etc/sysctl.conf 设定内核参数
  3. 设定时钟、键盘映射
  4. 启用交换分区
  5. 设置主机名
  6. 检测并以读写方式重新挂载根文件系统
  7. 激活 RAID 和 LVM
  8. 启用磁盘配额
  9. 根据 /etc/fstab 检查并挂载其它文件系统
  10. 清理过期锁和 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_OBJECTSLD_WARNLD_BIND_NOWLD_LIBRARY_VERSIONLD_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 vfatmodprobe -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 -acat /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/hostnamesysctl -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 内核编译步骤(简述)

  1. 配置:make menuconfig / make gconfig / make kconfig,生成 .config。
  2. 编译:make
  3. 安装模块:make modules_install
  4. 安装内核: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/cdrommount /dev/cdrom /media/cdromyum groupinstall "Development Tools" "Development Libraries" -y。从服务器下载内核:lftp 172.16.0.1get linux-2.6.28.10.tar.gz。解压:tar xf linux-2.6.28.10.tar.gz -C /usr/src,做链接 linuxlinux-2.6.28.10cd linux。配置:make gconfig (Gnome,需安装图形开发库)、make kconfig (KDE)、make menuconfig(文本菜单)。

screenscreen 开新屏,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

  • 关机命令 (红帽):shutdownhaltrebootpoweroffinit 0 关机,init 6 重启。
  • busybox:不到 1M,可模拟数百个命令,常用于裁减与嵌入式。
  • mount -n :挂载时不更新 /etc/mtab;cat /proc/mounts 可看当前挂载。

5.4 initrd 修改与根文件系统(原稿步骤摘要)

  1. 拷贝 vmlinuz 到目标 /mnt/boot/vmlinuz。
  2. zcat /boot/initrd-xxx.img | cpio -id 展开 initrd。
  3. 修改其中脚本(如 mkrootdev 的根设备为 /dev/hda2)。
  4. 脚本中注释可 vim .,+20s@#@g
  5. 删除 lib 下 dm-* 等不需要文件。
  6. find . | cpio -H newc --quiet -o | gzip -9 > /mnt/boot/initrd.gz 重新打包。
  7. grub-install --root-directory=/mnt /dev/hda,编辑 /mnt/boot/grub/grub.conf(default、timeout、title、root、kernel、initrd)。
  8. 根文件系统: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 等)。
  9. 根只读时 touch /tmp/a.txt 会失败,需 mount -n -o remount,rw /;关机可用 exec /sbin/halt -p(exec 替换当前进程)。
  10. 建 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.XXmktemp -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,格式为"分 时 日 月 周 用户 任务"。
  • 用户 croncrontab -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.pymysqldump,可能找不到命令(因为不在 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.hourly02 4 * * * root run-parts /etc/cron.daily22 4 * * 0 root run-parts /etc/cron.weekly42 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.priorityaction(文件路径、@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@HOSTssh -l USER HOSTssh USER@HOST 'COMMAND'
  • scpscp 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 rsassh-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 传文件、密钥免密
相关推荐
2501_915921432 小时前
Fastlane 结合 AppUploader 来实现 CI 集成自动化上架
android·运维·ci/cd·小程序·uni-app·自动化·iphone
倚肆2 小时前
Kafka TopicBuilder 主题配置详解
java·服务器·kafka
天空属于哈夫克32 小时前
企微外部群自动化触达,破解私域增长难题
运维·自动化·企业微信
匀泪2 小时前
云原生(Keepalived 核心功能配置与实验)
服务器·云原生
陆业聪2 小时前
世界模型:让机器学会「脑补」
linux·服务器·unix
之歆2 小时前
Linux 各种软件安装
linux·运维·服务器
蜡笔小炘2 小时前
Haproxy -- 动/静/混合态算法实验
运维·服务器·vim·haproxy
政安晨2 小时前
政安晨【人工智能项目随笔】OpenClaw(AI人工智能助手)集成SMB服务器实现自动化音效素材处理实战
运维·自动化·自动化运维·samba·ai-agent·openclaw·音频素材openclaw处理
huohaiyu2 小时前
数据链路层与网络协议全解析
服务器·网络·数据链路层