当前位置:首页>Linux>文件写入磁盘时,linux系统做了些什么?

文件写入磁盘时,linux系统做了些什么?

  • 2026-10-11 06:24:39
文件写入磁盘时,linux系统做了些什么?
“ 续接上一篇面试题库中的文件inode问题,很多人私信说磁盘理解起来很抽象,linux中遇到磁盘故障问题一头雾水,不知道如何下手操作了。本篇重点介绍关于磁盘在linux系统中的全链路解析说明,帮助大家认识数据存储的过程和故障恢复操作。”

刚接触Linux时,我以为磁盘管理就是"插上硬盘 →fdisk分个区 →mkfs格式化 →mount挂载"。直到某天线上报警:"磁盘写满",我登录一看——df -h显示还有10G空闲;又有一天,"文件删了,空间却不释放";还有一次,服务器突然卡死,dmesg 里全是 I/O error……

这些经历让我明白:磁盘不是一块沉默的铁片,而是一套精密的软硬协同系统。今天,我们就从最底层的物理结构开始,一路向上,讲清楚Linux中磁盘的完整工作链路,以及运维中那些“看似诡异实则有因”的问题。

1

硬件层:数据是如何被“记住”的

一切始于物理介质。在Linux系统运维与系统管理中,磁盘管理是基础且关键的一环。无论是新服务器部署、数据扩容,还是性能调优,都离不开对磁盘底层机制和上层工具的深入理解

机械硬盘(HDD):旋转的磁性记忆,数据存储在高速旋转的盘片上,靠磁头改变磁性颗粒方向记录 0/1。最小读写单位是扇区(Sector),传统为 512 字节,现代采用 4096 字节(4K)。性能瓶颈在于寻道时间 + 旋转延迟,随机 I/O 极慢。

固态硬盘(SSD):电子化的闪存存储。基于 NAND 闪存芯片,无机械部件。以“页”(Page,通常 4–16KB)为单位写入,以“块”(Block,含 256+ 页)为单位擦除。写前必须擦除,不能直接覆盖 → 依赖 FTL(闪存转换层) 映射逻辑地址到物理位置。有写入寿命限制(P/E Cycle),需靠磨损均衡和 TRIM 延长寿命。

HDD 适合大容量冷存储,SSD 适合高性能热数据;

2

接口层:如何与主机通信

磁盘通过接口连接主板,接口决定了带宽上限。

接口
带宽
特点

SATA

~600 MB/s

消费级主流,兼容好

SAS

12–24 Gb/s

企业级,支持双端口

NVMe over PCIe

PCIe 4.0 x4 ≈ 8 GB/s

专为 SSD 设计,低延迟、高并发

3

分区层:磁盘的逻辑空间

一块裸盘不能直接用,必须先分区——这是操作系统与磁盘协商的“空间划分协议”,当前有两种磁盘分区表格式。,新服务器一律使用 GPT。

MBR:传统磁盘分区表

  • 位于 LBA 0(第 0 扇区),512 字节

  • 仅支持 4 个主分区,最大识别 2TB

  • 分区表无校验,损坏即丢失

GPT:现代标准

  • 支持 >2TB(理论 8ZB)

  • 默认 128 个分区,头尾双备份 + CRC 校验

  • UEFI 启动必备

#查看分区表sudo fdisk -l /dev/sda      # 显示 MBR/GPT 类型sudo parted -l              # 更详细,支持 GPT#parted创建分区parted /dev/sdb(parted) mklabel gpt(parted) mkpart primary ext4 0% 100%(parted) quit

4

文件系统层:管理数据存储

分区后需格式化,即创建文件系统,定义如何存储文件、目录、元数据结构,使操作系统能高效组织和访问数据

常见文件系统

  • ext4:Linux 传统默认,支持最大 16TB 单文件,成熟稳定

  • xfs:高性能日志文件系统,CentOS/RHEL 默认,擅长大文件和高并发

  • swap:交换分区,用于虚拟内存

# 查看磁盘fdisk -l# 对 /dev/sdb1 创建 ext4 文件系统mkfs.ext4 /dev/sdb1# 或创建 XFSmkfs.xfs /dev/sdb1# 创建 swap 分区mkswap /dev/sdb2swapon /dev/sdb2# 挂载mount /dev/sdb1 /mnt/data # 永久挂载需编辑 /etc/fstab

核心概念

  • 超级块(superblock):描述文件系统整体状态信息(大小、状态、块大小等)

  • inode:每个文件/目录的唯一标识,记录权限、大小、时间戳、数据块指针

  • 数据块(Data Block):实际存储文件内容的单元(通常 4KB),标记哪些块被使用

  • 目录项(Directory Entry):文件名 ↔ inode 编号的映射

常见问题

场景1:inode耗尽,明明有空间,却写不进去数据。原因是Linux文件系统不仅限制空间,还限制 inode 数量(每个文件/目录都要一个 inode)。小文件特别容易吃光 inode。

$ df -hFilesystem      Size  Used Avail Use% Mounted on/dev/sda2        20G   10G   10G  50% /var$ touch testfiletouch: cannot create 'testfile': No space left on device

排查方法

df -i          # 查看 inode 使用率ncdu -i /var   # 按 inode 排序,定位罪魁祸首

场景2:文件删除了,但是空间没有释放。因为Linux 删除文件 = unlink(),即移除目录项与 inode 的链接。只要还有进程打开该文件(持有文件描述符),inode 就不会释放,数据仍在磁盘上。

# 终端 Atail -f /var/log/app.log# 终端 Brm /var/log/app.log

验证方法,查看是否有标记删除的文件,如果有,那就是还有进程在用,空间还在占用中,这个阶段数据完全可以恢复回来。

lsof | grep deleted# 输出示例:java    1234 user   12w   REG  8,2 1073741824 123456 /var/log/app.log (deleted)

恢复文件(如果进程还在)

# 找到进程 PID(如 1234)和 fd 编号(如 12)cp /proc/1234/fd/12 /var/log/app.log.recovered

如果确认数据不需要了,可以对当前看到占用该文件的进程kill掉,或者重启恢复。

5

linux的哲学:一切皆文件

理解 Linux 中与文件和磁盘相关的路径(尤其是 /proc、/dev、/sys 等虚拟文件系统)是深入掌握系统行为的关键,Linux 抽象了硬件、进程、内核状态等资源,统一用“文件”接口暴露给用户空间。这种设计让所有 I/O 操作都可用 open()、read()、write()、close() 完成,极大简化了编程模型。你操作的磁盘是/dev/sda这样的块设备文件;你敲击键盘是/dev/input/event0这样的字符设备文件;当前进程信息是/proc/self/下的一堆虚拟文件;网络连接可通过/proc/net/tcp查看。

dev目录

设备目录文件。/dev/sda块设备, /dev/null字符设备等。

proc目录

进程与内核运行时信息,这是一个内存中的虚拟文件系统,文件内容由内核动态生成,不占用磁盘空间,这里说几个与磁盘相关的核心子目录

(1)/proc/<PID>/fd/ —— 文件描述符的窗口。每个进程目录下都有 fd/ 子目录,列出该进程打开的所有文件描述符,每个 fd 是一个符号链接,指向实际文件或设备。

$ ls -l /proc/1234/fd/lr-x------ 1 user user 64 Dec 31 02:00 0 -> /dev/pts/0lrwx------ 1 user user 64 Dec 31 02:00 1 -> /dev/pts/0lrwx------ 1 user user 64 Dec 31 02:00 2 -> /dev/pts/0lr-x------ 1 user user 64 Dec 31 02:00 3 -> /var/log/app.log (deleted)

(2)/proc/mounts ——当前挂载点列表。等价于mount命令的输出,格式如下:

设备 挂载点 文件系统类型 挂载选项 dump/fsck标志

(3)/proc/partitions ——内核识别的分区表,显示所有被识别到的磁盘和分区(单位是:1kb块)

major minor  #blocks  name   8        0  500107608 sda   8        1    1048576 sda1   8        2  499057664 sda2

(4)/porc/swaps   ——交换分区文件信息。显示当前启用的swap空间

(5)/proc/diskstats —— 磁盘IO统计,每行对应一个块设备,包含读写次数、扇区数、耗时等,iostat的数据来源于此。

sys目录

设备与驱动的视图关系,比proc更加结构化,反映了设备、驱动、总线之间的拓扑关系,在SAN存储中常用到volume和lan的映射关系。

(1)/sys/block ——列出所有块设备(例如:sda,nvme0n1),每个设备目录包含扇区总大小(size),一个扇区512B;和队列(queue),涉及io调度,队列纵深,逻辑/物理块大小等。

cat /sys/block/sda/queue/scheduler   # 查看调度器echo 'none' > /sys/block/sda/queue/scheduler  # 切换(需 root)

(2)/sys/class/block —— 按块设备类创建的软连接,指向/sys/devices 下的实际设备

/etc/fstab ——静态配置文件,定义开机自动挂载的文件系统,和/proc/mounts一样。

文件功能性实战

场景1:恢复被删除的日志文件

# 1. 找到持有 deleted 文件的进程lsof | grep deleted# 2. 假设 PID=1234, fd=3cp /proc/1234/fd/3 ./recovered.log

场景2:查看哪些进程打开了哪些文件

ls -l /proc/$(pgrep nginx)/fd/ | grep -E '\.(log|dat)$'

场景3:查看磁盘物理/逻辑扇区的大小

cat /sys/block/sda/queue/physical_block_size   # 通常是 4096cat /sys/block/sda/queue/logical_block_size    # 通常是 512

场景4:强制内核重新扫描scsi总线,一般在磁盘热插拔后执行。

echo "- - -" > /sys/class/scsi_host/host0/scan

6

lvm逻辑卷组:物理磁盘虚拟化

传统分区一旦建好,扩容极其麻烦。而 LVM(逻辑卷管理) 提供了存储虚拟化,支持在线扩容、快照、跨盘聚合

物理磁盘 → PV(物理卷) → VG(卷组,存储池) → LV(逻辑卷,可挂载)

# 1. 创建 PVpvcreate /dev/sdb1 /dev/sdc1# 2. 创建 VG(名为 data_vg)vgcreate data_vg /dev/sdb1 /dev/sdc1# 3. 创建 LV(10GB,名为 web_data)lvcreate -L 10G -n web_data data_vg# 4. 格式化并挂载mkfs.xfs /dev/data_vg/web_datamount /dev/data_vg/web_data /var/www# 5. 扩容 LV(+5G)lvextend -L +5G /dev/data_vg/web_dataxfs_growfs /var/www        # XFS 自动扩展# 若是 ext4:resize2fs /dev/data_vg/web_data

在线扩容

# 1. 云平台扩容磁盘后,通知内核重读大小echo 1 > /sys/class/block/sdb/device/rescan# 2. 用 parted 扩展分区到新边界parted /dev/sdb resizepart 1 100%# 3. 若是 LVM PV,执行pvresize /dev/sdb1# 4. 扩展 LV 和文件系统lvextend -l +100%FREE /dev/vg/dataxfs_growfs /data

7

磁盘故障诊断与恢复

诊断工具和命令

(1)smart诊断工具确认磁盘是否故障

# 安装sudo apt install smartmontools# 查看健康状态sudo smartctl -H /dev/sda# 查看详细属性(重点关注)sudo smartctl -A /dev/sda

判断故障的几个关键参数

Reallocated_Sector_Ct:重映射扇区数(&gt;0 表示坏道)

Current_Pending_Sector:待重映射扇区(高危!)

UDMA_CRC_Error_Count:数据线问题(非磁盘本身)

(2)通过内核日志分析磁盘故障

dmesg | grep -i "error\|fail\|bad"journalctl -k | grep sd

典型错误,在之前维护ceph的时候,osd经常报I/O error,只能剔除这块osd,换新盘。

end_request: I/O error → 硬件通信失败

Buffer I/O error on dev sda1 → 文件系统层报错

(3)文件系统检查

# 必须在 unmounted 状态下运行!sudo fsck -f /dev/sdb1# ext4 日志回放(安全)sudo e2fsck -p /dev/sdb1

修复策略

场景1:分区表损坏,GPT格式,如果有备份,gdisk可以自动恢复。MBR引导代码损坏,重装grub

# gptsudo gdisk /dev/sda(gdisk) r → b → w   # 从备份恢复# mbrsudo grub-install /dev/sda

场景2:文件系统元数据损坏,使用fsck修复inode、位图、超级快

sudo dumpe2fs /dev/sda1 | grep -i superblocksudo e2fsck -b 32768 /dev/sda1   # 指定备用超级块

场景3:物理坏道

badblocks -v /dev/sda > badblocks.txt# 创建文件系统时跳过坏块mkfs.ext4 -l badblocks.txt /dev/sda1

常用检查和恢复方式

# 健康监控smartctl -H /dev/sda          # 查看健康状态smartctl -A /dev/sda          # 查看详细属性# 文件系统修复(必须 unmount!)fsck -f /dev/sdb1# 数据恢复extundelete /dev/sdb1 --restore-all   # ext3/4 误删photorec /dev/sdb                     # 按特征恢复(无视文件系统)ddrescue /dev/bad /dev/good log.txt   # 克隆故障盘# 查找高 inode 消耗for d in /var/*; do echo $d; find $d | wc -l; done

磁盘性能测试

# 顺序写dd if=/dev/zero of=/testfile bs=1G count=1 oflag=direct# 随机读写(需安装 fio)fio --name=randwrite --ioengine=libaio --rw=randwrite --bs=4k --size=1G --numjobs=4 --runtime=60 --group_reporting

8

linux存储栈:数据从应用到硬件

当程序调用 `write()`,数据走过一条多层路径:

  • Page Cache:默认写入内存缓存,异步刷盘(断电可能丢数据)

  • fsync():强制刷盘(数据库常用)

  • O_DIRECT:绕过缓存,用于 Redis/MySQL 等自管理缓存的应用

(1)Page Cache 与 Writeback

默认情况下,write() 仅将数据写入内存 Page Cache,立即返回内核后台线程(pdflush / writeback)在以下情况刷盘:

  • 页面脏时间超过 dirty_expire_centisecs(默认 30 秒)

  • 脏页占比超过 dirty_ratio(默认 20%)

  • 强制刷盘:fsync()(文件级)、sync()(系统级)

  • 风险:断电可能导致 Page Cache 中数据丢失!

(2)I/O 调度器(Scheduler)

  • CFQ(Completely Fair Queuing):公平分配带宽(HDD 时代主流)

  • Deadline:避免请求饿死,适合数据库

  • NOOP / none:SSD 无需寻道,直接传递请求(现代默认)

(3)Direct I/O 与 O_DIRECT

  • 绕过 Page Cache,直接从用户缓冲区 → 块设备

  • 用于数据库(如 MySQL InnoDB),避免双重缓存

往期推荐:

操作系统Centos和openEuler有什么不同点

linux安全 云主机常见的安全配置

局域网内部配置ubuntu apt本地软件源

linux系统中用一次忘一次的命令

面试linux系统运维工程师需要知道的知识点整理(1)

——关注订阅不迷路——

持续分享了 Linux 运维中的实用技巧和工具,希望能为你的日常工作带来帮助。


系统稳定,始于细节;运维之道,贵在积累。

🔹 欢迎点赞 + 在看 + 转发,让更多同行看到!

🔹 关注本号,持续更新 Linux、Shell、云计算等技术干货!

🔹 留言区开放:你在实践中遇到过哪些运维问题?欢迎交流!

最新文章

随机文章