type: note
title: Linux:定时任务三层排查 + LVM 造卷/灌满/扩容
date: 2026-11-09~2026-11-10
description: 定时任务「写了不执行」分三层查:cron 活着没 / 这条被触发没 / 触发后成没成,证据是 journalctl -u cron 的 CRON 行与重定向 log;日期永不成立(2 月 31 日 / 4 月 31 日);x 位与 Permission denied;cron 环境(绝对路径、重定向);地基链盘→分区→格式化→挂载;LVM 三层 PV/VG/LV(砖/仓库/货位)与造卷七步;灌满 → 扩容两本账:lvextend 改 LVM、resize2fs 改文件系统,df 只读后者,顺序由因果定;ext2 家族与在线扩容限制;/dev 设备 vs /mnt 挂载点、dd 写设备会毁文件系统;附两道收尾面试题与答案要点
tags: Linux, crontab, journalctl, 权限, LVM, PV, VG, LV, lvextend, resize2fs, mkfs, mount, dd, ext4, 面试题, 试点
Linux:定时任务三层排查 + LVM 造卷/灌满/扩容(D4)
> 📌 GitHub:https://github.com/AR-firefly
笔记区
核心观点
**1. 「写了不执行」不是一种病,分三层查**
| 层 | 问什么 | 证据 |
| -- | ------ | ---- |
| ① cron 活着没 | 守护进程在不在(每分钟醒一次看表) | `systemctl status cron` 显示 `active (running)`;或心跳任务产物按分钟增长 |
| ② 这条被触发没 | 时间写法对不对、有没有到点 | `journalctl -u cron` 里的 `CRONpid: (用户) CMD (命令)` 行 |
| ③ 触发后成没成 | 命令本身跑成功没有 | 重定向的 log 原文、产物在不在、mtime 对不对 |
判据:`CMD` 行 **>0** = 触发过;**=0** = 从没触发(时间写法不成立、crontab 没装上、服务没起都归这层)。**触发过 ≠ 成功**。
**2. `crontab -l` 只能证明「写在表里」,证明不了「跑没跑」**------它列的是配置,不是执行结果。命令是 `crontab`(管任务表),不是 `cron`(守护进程名)。
**3. 「语法合法但日期永不成立」是第二层典型坑**:`0 2 31 2 *`(2 月 31 日)、`0 4 31 4 *`(4 月 31 日)都合法,但永不触发。
**4. `Permission denied` 拆三件事:谁 → 对什么 → 干什么**
`/bin/sh: 1: /home/.../backup.sh: Permission denied`:`/bin/sh` 以该用户身份、执行 `backup.sh`、被内核拒绝------因为文件没有 `x` 位。**root 也不能执行没有 x 的文件**,sudo 在这里没用。判据看 `ls -l` 第一列(三处都没有 `x`)。
**5. cron 的环境和终端不一样**:不读 `.bashrc`、PATH 更短 → 命令写绝对路径;没有终端 → 输出不重定向就丢(`>> 文件 2>&1`)。
**6. 地基:一块盘到能存文件 = 认设备 →(可选)分区 → 格式化 → 建挂载点 → 挂载**
-
分区 = 在盘上划一段边界,得到 `/dev/sdb1` 这样的名字,再对**那一段**格式化
-
Windows 对照:「新建简单卷」= 分区,「格式化 NTFS」= `mkfs`,「分配盘符 D:」= **挂载到目录**(Linux 用目录代替盘符)
**7. LVM = 把「分区」换成可伸缩的一层**
| 层 | 是什么 | 记法 |
| -- | ------ | ---- |
| PV 物理卷 | 设备被登记进 LVM,切成固定 4M 小块(PE) | 砖 |
| VG 卷组 | 多个 PV 的块汇成一个池子,统一分配 | 仓库 |
| LV 逻辑卷 | 从池子里舀出一块,**这才是能格式化、能挂载的「分区」** | 货位 |
Windows 的「存储空间」是同一套:物理磁盘 → 存储池 → 虚拟磁盘。
**8. 扩容是两本账,`df` 只读文件系统那本,顺序由因果定**
-
LVM 的账:这块地方多大(`lvs` 看)------ `lvextend` 改这本
-
文件系统的账:ext4 在 `mkfs` 那一刻记下「我多少块」------ `resize2fs` 改这本
-
只 `lvextend` 时 `df` **不变**(中间态,不是失败);`resize2fs` 之后才变
-
**顺序不能反**:文件系统扩容以「设备当前大小」为上限,设备没先变大,`resize2fs` 无处可扩 → 先 `lvextend`,后 `resize2fs`
**9. ext2 家族与命令命名**
-
ext2(无日志)→ ext3(+日志)→ ext4(+extents/大容量),同属 `e2fsprogs`,所以扩文件系统叫 `resize2fs`(`2fs` 来自 ext2 时代的工具名,ext4 也用它)
-
**ext2 不支持在线扩容**:`resize2fs` 报 `Kernel does not support online resizing`,必须先 `umount` → `resize2fs` → `mount`;ext3/ext4 可在挂载中扩
-
命名规律:`<层><动作>`(`pvcreate`/`vgcreate`/`lvcreate`;`pvs`/`vgs`/`lvs` 是摘要列出,`pvdisplay` 是详细);「变大」各层办法不同:PV 用 `pvresize`、VG 用 `vgextend`、LV 用 `lvextend`
-
`mkfs` 与 `resize2fs` 不是一回事:mkfs **重铺**(清空)、resize2fs **改账本**(保留数据)
**10. `/dev` 是盘本身,`/mnt` 是打开后的文件夹**
-
`/dev/vg_d4/lv_d4`(=`/dev/mapper/vg_d4-lv_d4`)是**设备**:块设备文件(`ls -l` 第一列 `b`)
-
`/mnt/d4` 是**挂载点**:`mount` 把设备接到这个目录,之后看到的内容就是设备里的内容
-
`dd of=/dev/...` 直接写设备 = 绕过文件系统往盘面糊数据,把账本(超级块/inode 表)一起覆盖 → 文件系统坏(本次踩到,`df` 显示 `64Z`)
-
`dd of=/mnt/d4/fill` = 交文件系统分配空块、自己记账 → 正确用法;`of=` 指向目录报 `Is a directory`
关键方法/流程
**定时任务三层排查**
| 步 | 做什么 | 判据 |
| -- | ------ | ---- |
| 1 | `crontab -l` 看内容、五段、命令 | 只证明「写了」 |
| 2 | `systemctl status cron` | `active (running)` = 守护进程活着 |
| 3 | `journalctl -u cron --since "-20 min" \| grep 关键字`(或 `grep -c` 数条数) | >0 = 触发过;0 = 没触发 |
| 4 | 查命令绝对路径、脚本 x 位、输出重定向、cron 的 PATH | 缺 x → `Permission denied`;没重定向 → 报错没人收 |
| 5 | 修完:手动跑一次 → 等一次真实触发 → 看产物 mtime / 日志 | 产物 mtime = 触发时刻 = 真跑了 |
**LVM 建卷七步**
-
造假盘:`fallocate -l 512M ~/lab/d4r/disk.img`
-
挂成设备:`sudo losetup -f -P --show ~/lab/d4r/disk.img` → `/dev/loopN`(`-f` 找空闲、`-P` 扫分区表、`--show` 打印设备名)
-
`sudo pvcreate -y /dev/loopN`
-
`sudo vgcreate vg_d4 /dev/loopN`(第一个参数是卷组名,后面是 PV 设备)
-
`sudo lvcreate -L 200M -n lv_d4 vg_d4`(`-L` 绝对大小、`-n` 名字、末尾是卷组)
-
`sudo mkfs.ext4 -F /dev/vg_d4/lv_d4`(写在 LV 上,不是 loopN;`-F` 强制)
-
`sudo mkdir -p /mnt/d4` → `sudo mount /dev/vg_d4/lv_d4 /mnt/d4` → `df -h /mnt/d4`
**扩容两步(顺序不能反)**
-
改 LVM 账:`sudo lvextend -L 400M /dev/vg_d4/lv_d4` → `lvs` 变,`df` 不变
-
改文件系统账:`sudo resize2fs /dev/vg_d4/lv_d4`(ext4 可在挂载中在线扩)→ `df` 变
实战记录
**记录 1:三层判断在两条不同现场上都成立**
| 现场 | 心跳/同步 | 备份/同步 | 月报 | 结论 |
| ---- | --------- | --------- | ---- | ---- |
| 现场 A(`~/lab/d4`) | `d4/tick` 30 分钟内 29 条 | `d4/backup` 5 条(每 5 分钟)→ 触发了但失败 | `d4/monthly` 0 条(`0 2 31 2 *`) | 三条各归一类 |
| 现场 B(`~/lab/d4r`) | `beat.log` 每分钟一行且持续增长 | `sync` 每分钟有 `CMD` 行,`sync.log` 全是 `Permission denied` | `report` 搜不到 `CMD` 行、`report.log` 不存在(`0 4 31 4 *`) | 同上,证据齐 |
判据:journal 里 `CRONpid: (用户) CMD (...)` 行数 >0 只证明「启动过」;产物/日志才证明「成没成」。
**记录 2:两次 `x` 位修复,路径不同、判据一致**
```
cat ~/lab/d4/backup.log
/bin/sh: 1: /home/ar-26710/lab/d4/backup.sh: Permission denied (×8)
ls -l ~/lab/d4/backup.sh → -rw-r--r--(没有 x)
chmod u+x ~/lab/d4/backup.sh → -rwxr--r--
ls -l --time-style=+%T ~/lab/d4/backup/
src-latest.tar.gz 20:47:17 ← 手敲那次
src-latest.tar.gz 20:50:01 ← cron 自己跑的那次;backup.log 停在 8 行不再增长
```
重开那天同样:`ls -l ~/lab/d4r/sync.sh` → `-rw-r--r--`;`chmod u+x` 后 `-rwxr--r--`;`~/lab/d4r/sync.sh; echo $?` → `0`;此后 `sync.out` 按分钟增长、`sync.log` 停在旧行不再增。
**记录 3:造卷 → 挂载 → 灌满 → 扩容(主链)**
```
fallocate -l 512M ~/lab/d4r/disk.img
sudo losetup -f -P --show ~/lab/d4r/disk.img → /dev/loop2
sudo pvcreate -y /dev/loop2 → successfully created
sudo vgcreate vg_d4 /dev/loop2 → 508M(#PV 1, #LV 0)
sudo lvcreate -L 200M -n lv_d4 vg_d4 → 200M
sudo mkfs.ext4 -F /dev/vg_d4/lv_d4 → 51200 个 4k 块 / 51200 inode
sudo mount /dev/vg_d4/lv_d4 /mnt/d4
df -h /mnt/d4 → 172M(200M 扣掉日志 16M + inode 表)
sudo dd if=/dev/zero of=/mnt/d4/fill bs=1M → No space left on device
df -h /mnt/d4 → 172M, Used 168M, Avail 0, 100%
sudo lvextend -L 400M /dev/vg_d4/lv_d4 → 200.00 MiB (50 extents) → 400.00 MiB (100 extents)
df -h /mnt/d4 → 仍 172M / 100%(两本账不同步)
sudo resize2fs /dev/vg_d4/lv_d4 → now 102400 (4k) blocks long
df -h /mnt/d4 → 359M, Avail 174M, 50%
```
**踩坑记录**
| 坑 | 现象 | 原因 |
| -- | ---- | ---- |
| `dd if = /dev/zero`(`=` 带空格) | `Unrecognized operand 'if'` | shell 把 `if=` 按空格拆开 |
| `dd of=/mnt/d4`(写挂载点目录) | `Is a directory` | `of=` 是「往哪写数据」,不是「往哪个目录放」 |
| `dd of=/dev/vg_d4/lv_d4`(写设备) | 文件系统坏,`df` 显示 `64Z` | 绕过文件系统直接写盘面,账本一起被盖 |
| `lvextend -n ... bs=1M` | `Invalid path for Logical Volume` | 混进 `lvcreate`/`dd` 的参数;`lvextend` 只认「大小 + 卷设备」 |
| `mkfs` 不加 sudo | `Permission denied while trying to determine filesystem size` | 要 root |
| `mkfs` 在挂载中 | `is mounted; will not make a filesystem here!` | 挂载中不许重铺;要重铺先 `umount`(这是保护,不是故障) |
| 改完 LV 不 `resize2fs` | `df` 不变 | 文件系统账没改 |
| ext2 上在线扩 | `Kernel does not support online resizing` | ext2 不支持在线扩,必须 `umount` 后扩 |
线索区(学完后合上材料,自问自答)
**Q: 一条定时任务没跑,第一步查什么?**
A: 分三层:cron 活着没 / 这条被触发没 / 触发后成没成。证据分别是 `systemctl status cron`、journal 的 `CRONpid: CMD` 行、重定向的 log 与产物。
**Q: `crontab -l` 能看到任务,能不能说明它跑过?**
A: 不能。只证明写在表里;跑没跑要看 journal 的 CMD 行。
**Q: `0 2 31 2 *` / `0 4 31 4 *` 为什么永远不跑?**
A: 语法合法但日期永不成立(2 月/4 月没有 31 日),属于「从不触发」。
**Q: 脚本报 `Permission denied`,加 sudo 有用吗?**
A: 没用。缺的是文件的 `x` 位,root 也不能执行没有 x 的文件;判据看 `ls -l` 第一列。
**Q: cron 里命令为什么必须写绝对路径?**
A: cron 不读 `.bashrc`,PATH 比终端短,相对路径找不到命令。
**Q: 一块新盘到能存文件,要几步?**
A: 认设备 →(可选)分区 → `mkfs` 格式化 → 建挂载点 → `mount`。
**Q: PV / VG / LV 各是什么?**
A: PV = 设备切成 4M 小块的砖;VG = 砖汇成的池子;LV = 从池子舀出的一块,能格式化、能挂载的「分区」。
**Q: 为什么 `lvextend` 完 `df` 还是旧的?**
A: `df` 读文件系统自己记的大小,那个数在 `mkfs` 时就写定了;`lvextend` 只改 LVM 记的大小,要 `resize2fs` 改文件系统的账。
**Q: 为什么必须先 `lvextend` 再 `resize2fs`?**
A: 文件系统扩容以「设备当前大小」为上限;设备没变大,`resize2fs` 无处可扩。
**Q: ext2 能在线扩容吗?**
A: 不能,报 `Kernel does not support online resizing`,必须先 `umount`;ext3/ext4 才能在线扩。
**Q: `mkfs` 和 `resize2fs` 的区别?**
A: mkfs 重新铺文件系统(会清空数据);resize2fs 只改已有文件系统的大小(保留数据)。
**Q: `dd of=/dev/vg_d4/lv_d4` 和 `dd of=/mnt/d4/fill` 差在哪?**
A: 前者绕过文件系统直接写盘面(账本一起被覆盖,fs 坏);后者由文件系统分配空块、自己记账。
总结(50字以内)
定时任务不跑分三层查(cron 活着 / 触发没 / 执行成没),各拿证据;LVM 走 PV→VG→LV→mkfs→mount,扩容两本账:先 lvextend 改 LVM,再 resize2fs 改文件系统。
复习卡片
| 概念 | 必学/了解 | 一句话 | 场景 |
| ---- | --------- | ------ | ---- |
| 三层判断(cron / 触发 / 执行) | 必学 | 写了不执行先分层,各层各有证据 | 任何定时任务不跑 |
| `journalctl -u cron` | 必学 | `-u` 按服务;触发留 `CRONpid: (用户) CMD (命令)` | 判有没有被触发 |
| `grep -c` | 必学 | 只输出匹配行数,不打印内容 | 数触发次数 |
| `crontab -l / -e / -r` | 必学 | list 读 / edit 改 / remove 全删;命令是 crontab 不是 cron | 管任务 |
| `systemctl status cron` | 必学 | `active (running)` = 守护进程活着 | 三层第一层 |
| cron 环境(PATH、重定向) | 必学 | 不读 `.bashrc`、绝对路径、`>> log 2>&1` | 排查执行失败 |
| `x` 位与 `Permission denied` | 必学 | 缺 x 位谁都不能执行;判据 `ls -l` 第一列 | 脚本跑不起来 |
| 盘→分区→格式化→挂载 | 必学 | 分区划边界、mkfs 铺账本、mount 接目录 | 任意新盘 |
| `mkfs.ext4` | 必学 | 写超级块 + inode 表 + 块位图;与 resize2fs 区别看数据保不保 | 格式化 |
| PV / VG / LV | 必学 | 砖 / 仓库 / 货位;LV 才是「分区」 | LVM 三层 |
| `pvcreate` / `vgcreate` / `lvcreate` | 必学 | 层+动作;vgcreate 第一个参数是卷组名,lvcreate 用 `-n 名字 -L 大小` | 建卷 |
| `pvs` / `vgs` / `lvs` | 必学 | 三层各一张摘要清单;要 sudo 才读得到元数据 | 查看 |
| `lvextend` | 必学 | 改 LVM 账;`-L 400M` 目标、`-L +200M` 增量;没有 `-n` | 扩容第一步 |
| `resize2fs` | 必学 | 改 ext 家族文件系统账;ext2 要离线,ext4 可在线 | 扩容第二步 |
| ext2 / ext3 / ext4 | 必学 | 无日志 / 加日志 / 加 extents 与更大容量 | 选文件系统 |
| `df -h` | 必学 | 读文件系统那本账,不是设备的账 | 验证扩容 |
| `/dev` 设备 vs `/mnt` 挂载点 | 必学 | `/dev` 是盘本身,`/mnt` 是打开后的文件夹 | 写文件、dd |
| `dd if=... of=... bs=1M` | 必学 | 灌满用;`of=` 是「往哪写数据」,不能写目录、不能写设备(会毁 fs) | 造满盘 |
| `mount` / `umount` | 必学 | mount 接线到目录;umount 断开(可给目录或设备);成功时都不打印 | 挂载 |
| `fallocate` / `losetup` / `fdisk` | 了解 | 查 `man 1 fallocate` / `man 8 losetup` / `man 8 fdisk` | 造盘、分区 |
流程附加段
旧知识回收
| 概念 | 一句话 | 出处 |
| ---- | ------ | ---- |
| 盘→分区→格式化→挂载 这条链 | 一条盘到能存文件的四步说不全 | 教学诊断 |
| ext2/ext3/ext4 家族 | 各自多了什么说不清;`resize2fs` 的 `2fs` 来历不明 | 补讲 |
| PV / VG / LV | 三层关系与「LV 才相当于分区」讲不清 | 补讲 |
| 命令名与动作的对应 | 「挂载」想不起是 `mount`;给动作报不出命令;`cron -l` 不是命令 | 反复出现 |
| `dd` 的 `if=` / `of=` | `if=/dev/null` 读 0 字节灌不满;`of=` 指目录报 `Is a directory`;`of=` 指设备盖坏文件系统 | 踩到 |
| 整机 CPU 百分比口径 | 20 核单核满 = 5%;8 核 2 核满 = 200/800 = 25%(分母 = 核数×100%) | 复习时踩到 |
| 用户级 vs root 权限 | `/mnt/d4` 属主 root,普通用户不能写;LVM 命令要 sudo | 踩到 |
| `lvextend` 没有 `-n` | `-n` 是 `lvcreate` 的参数,扩容只要「大小 + 卷设备」 | 踩到 |
| 判据数字 | 200M LV 可用 172M(不是 197M);400M 可用 359M(不是 380M) | 验证校正 |
今日产出验收
| 产出条 | 证据 | 结果 |
| ------ | ---- | ---- |
| 定时任务三层各自的证据 | 29/5/0 三个数;`beat.log` 增长;`sync.log` 的 `Permission denied`;`report.log` 不存在 | 过 |
| LVM 造卷挂载 | `pvcreate/vgcreate/lvcreate/mkfs/mount` 全流程输出 | 过 |
| 灌满 → 扩容 → `df` 证明 | 100% → `lvextend` 后仍 172M → `resize2fs` 后 359M | 过 |
| 独立做成(自己写全链) | 第 3 段两条命令由 AI 示范;2026-11-11 用户拍板改判全过 | 过(按用户拍板) |
| 复盘(为什么先 lvextend 再 resize2fs) | 补讲后重考答对 | 过 |
| 收尾面试题 | 两道题 + 答案要点见下节 | 已入笔记 |
面试题(两块内容各一道)
一、cron 任务不执行(Adaface 第 9 题)
> 出处:Adaface --- 98 Linux Interview Questions(https://www.adaface.com/blog/linux-troubleshooting-interview-questions/) 高级组第 9 题
**题面**
> A scheduled cron job is failing to execute as expected. What steps would you take to determine the cause of the failure and ensure the job runs successfully?
中文:一个定时任务没有按预期执行,你会怎么确定原因、并确保它跑起来?
说人话:cron 任务该跑没跑,你按什么顺序查、拿什么证据、修完怎么确认。
**答案要点(三层判断 + 修完验证)**
-
**第一层:cron 活着没**------`systemctl status cron` 看 `active (running)`;或心跳任务产物按分钟增长。服务没起时后面日志全空,会误判成「时间写错」。
-
**第二层:这条被触发没**------`journalctl -u cron | grep 关键字`,数 `CRONpid: (用户) CMD (命令)` 行:**>0 = 触发过;=0 = 从没触发**(时间写法不成立、`2 月 31 日` 这种、crontab 没装上,都归这层)。
-
**第三层:触发后成没成**------看重定向 log 原文、产物在不在、mtime 对不对;常见坑:相对路径 / cron 的 PATH 更短、脚本没 `x` 位、输出没重定向。
-
**修完怎么确保它跑起来**------手动跑一次 → 等一次真实触发 → 看产物 mtime 是否等于触发时刻;不能凭「我改完了」宣布解决。
> 最常见答错点:拿 `crontab -l` 当「跑过」的证据------它只证明写在表里。
二、自定义脚本执行失败(Adaface 第 28 题)
> 出处:同上,第 28 题
**题面**
> Explain how you would identify and resolve a situation where a custom script is failing to execute properly on a Linux server.
中文:一台 Linux 服务器上,一个自定义脚本无法正常执行,你会怎么定位并解决?
说人话:脚本该跑,但跑不起来或跑不对,你按什么顺序查、怎么确认修好了。
**答案要点(按顺序)**
-
**先分现象**:是「根本没跑」还是「跑了但结果不对」------手动执行一次,`echo $?` 看退出码;非 0 就读 stderr。
-
**看执行权限与解释器**:`ls -l 脚本` 看 `x` 位;`file 脚本` 或首行 shebang 看解释器;`bash -x 脚本`(或 `sh -x`)逐行看执行到哪。
-
**看「谁在跑」和「环境差」**:手动能跑、cron/服务里不跑 → PATH 更短、相对路径、cwd 不同、不读 `.bashrc`、没有终端;对应写绝对路径、`>> log 2>&1` 接住输出。
-
**看日志与产物**:重定向的 log、`journalctl -u <服务>`、产物在不在、mtime 对不对。
-
**修完验证**:手动跑一次 → 等一次真实触发 → 看产物 mtime / 日志不再新增报错。