接手过几十台内存告警的Linux服务器,我几乎都会先看一眼free -h里的Swap是不是0B。大部分性能问题的表象是进程被杀、服务抖动,底层原因往往就一句话:压根没建交换空间。这篇就是来聊磁盘维护里那个经常被一带而过、但关键时刻能救命的mkswap命令。
mkswap的全称是make swap,作用是在磁盘分区或文件上初始化交换空间。它和mkfs的关系有点像“格式化”这个词的两种走向:mkfs建立文件系统,mkswap建立内核可直接读取的swap superblock。这篇实操篇会从交换空间原理讲起,带你把一个裸盘从分区、格式化、激活、开机自启完整走一遍,再聊聊swap文件方案、生产环境踩坑和调优。无论你是刚接触Linux的运维新人,还是正在给服务器做磁盘规划的资深工程师,都能在里面找到直接把命令抄走就能用的内容。
1. 在敲mkswap之前,先把交换空间的底细摸清楚
1.1 交换空间的本质:内存与磁盘之间的“应急缓存区”
先说个反直觉的事:很多人以为swap是内存不够时的“扩容”,其实更准确的说法是临时中转站。操作系统把物理内存中暂时不活跃的内存页搬到磁盘上的交换空间,腾出物理内存给正在运行的活跃进程。这个过程叫换出(swap out),等进程再次需要这些数据时再换入(swap in)。
如果一台服务器物理内存8GB,MySQL占6GB,redis占3GB,内存已经超卖。此时内核的内存回收机制会优先把文件页(比如磁盘缓存的读缓存)回收掉,但如果还不够,就必须把匿名页换出。如果没有swap,内核唯一的选择就是调用OOM Killer杀进程——你可以想象一下凌晨三点MySQL被内核杀掉是什么体验。
所以mkswap真正解决的是“物理内存不够时,系统能优雅地降级而不是直接崩溃”的问题。很多云厂商默认镜像不开swap,因为云盘IOPS有限,交换频繁会拖垮整个系统,但这不等于swap没用。关键看场景:内存充足、业务延迟敏感的环境可以不要swap;内存紧张、跑批任务、Java应用堆内存偏大的环境,swap就是保命绳。
1.2 建swap靠mkswap,启swap靠swapon,两兄弟分工不同
我刚学Linux时老搞混一件事:mkswap之后是不是就自动生效了?不是。mkswap只是“写入交换空间格式”,相当于在一张白纸上先画好格子;真正让内核使用它,还需要swapon激活。
这俩命令的分工决定了排查故障的基本思路:
mkswap报错,通常和磁盘本身、分区表、文件大小有关,比如设备不存在、分区被占用、文件系统已有数据需要强制。swapon报错,说明设备/文件格式没问题,但内核拒绝激活,常见原因包括权限不对、swap header损坏、设备忙。
实战里我也见过不少人直接mkswap完就以为万事大吉,结果重启之后swap还是0B——因为没写/etc/fstab。先记住这条主线,后面所有操作都围绕“建、启、持久化”三件事展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从一块裸盘开始的完整实操:fdisk分区、mkswap格式化、swapon激活
2.1 用fdisk规划磁盘并标记82类型
假设现在给服务器加了一块20GB的云盘/dev/sdb,打算拿8GB做swap,剩下12GB给数据盘。第一步是分区,注意swap分区有个传统习惯:把分区类型标记为82(Linux swap / Solaris),这是给fdisk、lsblk等工具看的标识,虽然不标记也不影响mkswap运行,但规范的分区表对后续维护很重要。
bash复制[root@localhost ~]# fdisk /dev/sdb
命令(输入 m 获取帮助):n
分区类型
p 主分区 (0个主分区,0个扩展分区,4空闲)
e 扩展分区
选择 (默认 p):p
分区号 (1-4,默认 1):1
起始 扇区 (2048-41943039,默认为 2048):
Last 扇区, +/-扇区 or +sizeK/M/G (2048-41943039,默认为 41943039):+8G
命令(输入 m 获取帮助):t
已选择分区 1
Hex 代码(输入 L 列出所有代码):82
已将分区“Linux swap / Solaris”的类型更改为“Linux swap / Solaris”
命令(输入 m 获取帮助):w
分区表已调整!
这里有个很多人踩过的细节:fdisk的w只是把分区表写入磁盘,内核不一定立刻感知。尤其是云主机、虚拟机的热添加磁盘场景,写完分区表后最好执行一次partprobe /dev/sdb或者partx -u /dev/sdb,让内核重读分区表,否则后面mkswap可能还看到旧的分区信息,直接报No such file or directory。
分区完成后用lsblk确认一下:/dev/sdb1 8G,TYPE列应该显示part。如果没显示,就执行partprobe再查。
2.2 mkswap格式化:核心参数与执行验证
分区就绪后,终于到主角登场。最基本的用法是直接指定设备:
bash复制[root@localhost ~]# mkswap /dev/sdb1
Setting up swapspace version 1, size = 8 GiB (8587833344 bytes)
no label, UUID=3f2e0b1a-9d5e-4b7a-8c90-1e2f3a4b5c6d
看到Setting up swapspace version 1就是成功了,同时会打印UUID,这个UUID后面写/etc/fstab要用,务必记下来。
mkswap常用的几个参数,我按使用频率排个序:
| 参数 | 作用 | 实际使用建议 |
|---|---|---|
-L label |
给交换空间设置卷标 | 便于多块swap时用名字识别,比如-L SWAP-DATA |
-U UUID |
手动指定UUID | 一般留给脚本自动化用,日常让系统自动生成即可 |
-c |
检查坏块后再格式化 | 老机械盘建议用,SSD和新盘一般不必要,耗时很长 |
-f |
强制创建 | 当设备上检测到已有文件系统或数据时会要求强制,使用前必须确认没放错盘 |
-p PAGE_SIZE |
指定页面大小 | 默认4096,创建swap文件时偶尔需要处理页大小不对齐的问题 |
-v version |
指定swap版本 | 现代内核只用version 1,这个参数已经很少需要手动指定 |
-f参数要特别讲一讲。如果你对一个已经mkfs.ext4过的分区执行mkswap,默认会拒绝:
bash复制[root@localhost ~]# mkswap /dev/sdb1
mkswap: /dev/sdb1: 现有文件系统,使用 -f 强制交换。
系统提示已经很明显了,但就是有人不看提示直接mkswap -f把放有数据的分区给冲了。我的习惯是执行强制操作前先blkid看一遍目标设备:
bash复制blkid /dev/sdb1
如果输出显示LABEL="/data"或者TYPE="ext4",立刻停手,检查是不是盘符搞错了。生产环境的教训告诉我:-f是最后一个选项,不是第一个选项。
2.3 swapon激活与free验证
格式化成功不等于启用。接下来执行:
bash复制[root@localhost ~]# swapon /dev/sdb1
[root@localhost ~]# swapon --show
名称 类型 大小 已用 优先级
/dev/sdb1 partition 8G 0B -2
swapon --show是新版util-linux推荐的方式,比老的cat /proc/swaps输出更友好。然后用free -h验证:
bash复制[root@localhost ~]# free -h
total used free shared buff/cache available
Mem: 7.6Gi 2.3Gi 3.1Gi 12Mi 2.2Gi 5.1Gi
Swap: 8.0Gi 0B 8.0Gi
Swap那一行从0B变成8.0Gi,说明已经生效。如果你希望这个swap在系统重启后依然存在,还得做第三步:写/etc/fstab。
2.4 写入/etc/fstab实现开机自动启用
fstab里swap专用的那一行,网上能搜到很多版本,最稳的是用UUID,因为设备名(/dev/sdb1)在插拔磁盘或换驱动后可能漂移,UUID是稳定标识:
bash复制UUID=3f2e0b1a-9d5e-4b7a-8c90-1e2f3a4b5c6d none swap sw 0 0
解释一下这行各字段的含义:第一列是设备标识,第二列挂载点写none(swap不是普通文件系统),第三列文件系统类型写swap,第四列挂载参数写sw,最后两列dump和fsck都写0,表示不做备份、不检查。
写完fstab后,重要的一步是验证配置是否正确:
bash复制[root@localhost ~]# swapon -a
swapon -a会按照fstab条目把所有swap都启用。如果这里没报错,同时swapon --show能看到设备,那说明重启后大概率没问题。还可以用systemctl daemon-reload再systemctl status swap.target确认,但实际运维中最常见的是忘掉“验证”这步,结果重启后才发现swap没挂上,到时候排查起来更麻烦。
3. 不想动分区?用swap文件方案,少踩很多磁盘规划的坑
3.1 用dd还是fallocate创建文件,结论很明确
在某些磁盘被LVM、云盘分区策略搞得比较死板的环境里,临时加一个swap分区要动分区表,风险不小。这时swap文件是个非常灵活的替代方案。创建一个2GB的swap文件:
bash复制[root@localhost ~]# dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress
2048+0 records in
2048+0 records out
2147483648 bytes (2.1 GB, 2.0 GiB) copied, 1.23456 s, 1.7 GB/s
有人会问:fallocate -l 2G /swapfile更快,为什么不用?我实测过很多次,fallocate在某些文件系统上会创建带有“洞”的稀疏文件,mkswap虽然能跑通,但swapon时可能报swapfile has holes,或者虽然激活成功但后续使用中文件系统行为怪异。man手册里也明确建议dd而不是fallocate。既然是为了稳定性,就不差那几秒钟,用dd最稳妥。
3.2 swap文件的权限问题和fstab写法
文件创建完,第一个动作是收紧权限:
bash复制[root@localhost ~]# chmod 600 /swapfile
这一步不做的话,swapon /swapfile会直接拒绝,提示insecure permissions 0644, 0600 suggested。为什么内核这么敏感?因为swap文件里放的是内存页的副本,可能包含密码、密钥等敏感数据,如果其他用户可读,等于把物理内存里的秘密暴露给所有人。生产环境我见过因为swap文件权限是644导致的安全扫描报警,属于低级但真实的错误。
接着格式化并激活:
bash复制[root@localhost ~]# mkswap /swapfile
[root@localhost ~]# swapon /swapfile
fstab里对应的一行和分区方式略有区别,不需要UUID,直接用文件路径:
bash复制/swapfile none swap sw 0 0
验证方式和前面一样,swapon -a + swapon --show。
3.3 swap文件与swap分区的选型边界
很多教程会笼统说“swap文件性能不如swap分区”,这句话在机械硬盘时代基本成立,但到了SSD和NVMe时代,两者的实际性能差异越来越小。真正影响性能的是底层物理介质和内核的换页策略,而不是“文件”和“分区”这个壳。
我的选型经验:
- 云主机:没有裸设备控制权的时候,直接用swap文件,省去分区表风险。
- 物理机:如果磁盘是独立的SSD,我会用swap分区,因为分区连续空间分配更简单,不会受文件系统碎片影响。
- 容器或K8s节点:尽量不依赖swap文件放在容器目录里,而是放在宿主机的独立分区,避免容器删除时误删。
- 临时救急:某个Java进程内存不够又不想重启加内存,直接swap文件顶上,用完swapoff删掉即可,不动分区表。
4. mkswap实操中的高频报错与完整排查链路
4.1 分区类型是83而不是82:mkswap不报错,但隐患在后面
很多人fdisk建完分区,忘了把类型改成82。这种情况下mkswap通常会正常完成,内核也不管分区类型,照样能swapon。那为什么还要改82?
因为很多自动化脚本、可视化面板、以及lsblk -f这类工具是靠分区类型判断设备用途的。如果分区表里明明是个swap设备,类型却是83(Linux filesystem),排查故障时会造成严重误导。你想想,一个运维同事看lsblk发现这块盘显示ext4,他会下意识跳过它去查别的地方。
规范操作应该是建成82。不过要注意,云控制台创建的裸盘如果不支持fdisk交互改类型,也可以靠wipefs和parted set来补救:
bash复制parted /dev/sdb set 1 type linux-swap
实操下来效果一样。
4.2 swapon失败:read swap header failed的根因定位
我接到过不少“swap建完但启用报错”的问题,最典型的错误是:
bash复制[root@localhost ~]# swapon /swapfile
swapon: /swapfile: read swap header failed: Invalid argument
这类报错我通常按下面的链路排查:
-
先确认文件/设备大小是否是4KiB的整数倍。swap header要写在页边界上,而内存页大小默认4096字节,文件大小不对齐就会报Invalid argument。解决办法是删掉重建,
dd时按块对齐。 -
确认文件系统是否支持swap文件挂载。某些网络文件系统(NFS、CIFS)和部分堆叠文件系统不支持mmap和写回语义,mkswap虽然能跑,但swapon时内核不认。本地ext4、xfs都没问题。
-
确认是不是稀疏文件。
fallocate创建的文件如果用ls -ls看到占用的block数远小于文件大小,就是带洞文件,内核拒绝加载。解决方法是dd重新创建。 -
确认SELinux上下文。RHEL系系统开启SELinux后,swap文件的类型必须是
swapfile_t。用ls -Z查看,如果不是就执行:
bash复制semanage fcontext -a -t swapfile_t /swapfile
restorecon -v /swapfile
一条条走下来,绝大部分swapon失败都能找到原因。关键是别跳过第一步就去重装系统,很多问题只是文件大小不对齐。
4.3 不要对正在使用的swap执行mkswap,以及误操作后的处理方法
这条我放在“踩坑”里说,是因为它真的危险。mkswap会重写设备或文件头部的superblock,而内核正在使用这个交换空间时,它会假设superblock一直有效。一旦superblock被破坏,内核可能产生随机内存错误、进程崩溃、甚至整个系统死锁。别问我怎么知道的——我在测试环境手滑过一次,之后那台机器随机panic了一个星期才定位到是swap被重刷了。
如果不小心对正在使用的swap执行了mkswap,最接近正确的处理方式是:
bash复制sync
swapoff /dev/sdb1 && swapon /dev/sdb1
但说实话,如果系统已经开始报内存错误,swapoff本身也可能卡住或失败,最终只能重启。所以我的经验是两条铁律:执行mkswap前必看cat /proc/swaps和swapon --show;不在任何业务高峰期动swap相关操作。
5. 生产环境里的swap规划:大小、位置与调优参数
5.1 swap分区大小到底该给多少
网上流传的“物理内存2倍”是早年内存只有几十MB时代的经验,拿到现在并不适用。现在8GB起步、64GB常见的服务器,如果还按2倍给swap,不仅浪费磁盘,还会让内核把大量不必要的内存页换出,拖慢性能。
我一般参考这个区间,再结合业务负载做判断:
| 物理内存 | 推荐swap大小 | 说明 |
|---|---|---|
| 小于2GB | 内存的2倍 | 小内存机器确实需要swap兜底 |
| 2GB - 8GB | 等于内存大小 | 兼顾性能和容错 |
| 8GB - 64GB | 至少4GB,最多16GB | 具体看应用负载,跑批类取大值 |
| 大于64GB | 按需分配,甚至0 | 一般靠物理内存,特殊情况开启 |
还有一个容易忽略的点:如果你需要休眠(suspend-to-disk)功能,swap必须大于或等于物理内存,否则休眠镜像写不下。Linux服务器一般不开休眠,但如果你在个人笔记本上折腾,这个约束要注意。
5.2 swappiness参数:控制内核“多积极”地使用swap
/proc/sys/vm/swappiness的取值范围是0到100,默认60。值越大,内核越倾向于把不常用的内存页换出到swap;值越小,越倾向于保留在物理内存。
我见过两类极端配置:一类是无脑设成0,另一类是内存不够还设成100。都不对。
- 如果内存充足,业务延迟敏感,比如Redis、MySQL这类追求低延迟的服务,建议设成10左右,让内核只在万不得已时才使用swap。
- 如果内存经常不够,比如一些跑批任务、数据分析节点,设成60到80反而能提高吞吐,因为内核可以主动把冷数据换出,给热数据腾地方。
临时调整:
bash复制sysctl vm.swappiness=10
永久调整,写入sysctl配置目录:
bash复制echo 'vm.swappiness=10' > /etc/sysctl.d/99-swap.conf
sysctl --system
调整后不用重启即可生效,验收就一条:cat /proc/sys/vm/swappiness。
5.3 多个swap设备与优先级:让内核“按顺序”使用交换空间
生产环境一张盘扛所有读写确实有瓶颈。如果你有多块磁盘,可以把swap分散放在不同物理盘上,再用优先级控制内核使用顺序。
bash复制mkswap /dev/sda2 && mkswap /dev/sdb2
swapon -p 100 /dev/sda2
swapon -p 10 /dev/sdb2
优先级数字大的先被使用,当高优先级swap空间用满,内核才会流到低优先级设备。这样可以把更快的SSD作为首选,慢的HDD作为兜底。
在fstab里对应写法是:
bash复制UUID=xxxx-xxxx none swap sw,pri=100 0 0
UUID=yyyy-yyyy none swap sw,pri=10 0 0
注意pri参数要写在第四列挂载选项里,用逗号分隔。这个设计在混合存储环境中非常实用:NVMe盘负责热交换,机械盘负责偶尔的大量换出。
6. 更进阶的场景:LVM逻辑卷、GPT分区表与mkswap边界
6.1 在LVM逻辑卷上创建swap
如果你所在的公司用LVM管理磁盘,那swap完全可以做成逻辑卷,好处是以后扩容缩容不用重新分区,直接lvextend或lvreduce就能调整。
bash复制pvcreate /dev/sdc1
vgcreate vg_swap /dev/sdc1
lvcreate -L 4G -n swap vg_swap
mkswap /dev/vg_swap/swap
swapon /dev/vg_swap/swap
fstab里的写法不变,用逻辑卷路径或者UUID都行。后续如果发现4G不够,扩到8G:
bash复制lvextend -L 8G /dev/vg_swap/swap
swapoff /dev/vg_swap/swap
mkswap /dev/vg_swap/swap
swapon /dev/vg_swap/swap
注意扩容后需要重新mkswap,因为大小变化会重建swap header,重新mkswap后原来的数据会清空。如果是临时swap,数据本来就无所谓,重来就好。
6.2 GPT分区表下如何标识swap分区
老式MBR分区表里swap类型代码是82,但在GPT分区表里,Linux swap的GUID是0657FD6D-A4AB-43C4-84E5-0933C84B4F4F。好在现代fdisk已经把这些类型封装成了易读的别名,你不需要记GUID。
在fdisk操作GPT磁盘时,输入t后如果记不住类型编号,直接输入L,会列出当前分区类型列表,找到名字带Linux swap的那一项选中即可。parted下则可以使用set 1 type linux-swap,效果一样。
我见过有人在GPT盘上输入82,结果fdisk把它当成别的类型,甚至直接报错。所以关键点还是那句话——不要背编号,用交互菜单里的别名。
6.3 旧内核与mkswap版本兼容性
mkswap命令来自util-linux包,绝大多数现代发行版都内置了较新版本。但一些老系统(比如CentOS 6、Ubuntu 14.04)上,mkswap对参数的支持范围和现代版本不太一样。
最典型的是老版本mkswap需要显式指定设备大小(默认单位是1024字节块),用法形如mkswap -c /dev/hdb3 1024。新版本工具已经能自动识别设备大小,不再需要手动传size参数。如果你写脚本同时要兼容新旧系统,最简单的方式是不要在mkswap命令后面加size参数,让工具自己探测。
另外,老版本还可能使用mkswap -v0创建旧格式swap,新内核虽然兼容,但很多新工具解析时可能报警告。现代系统里永远不要手动指定-v0,除非你明确知道目标内核版本老到不支持v1。
写在最后的一个小建议
mkswap本身不复杂,但它是磁盘维护里“见效极快、出事也极快”的命令。我每次在生产环境执行mkswap前,都会强制自己过一遍这个检查单:确认目标设备不是系统盘、确认分区类型正确、确认fstab写完后用swapon -a验证过、确认没有对正在使用的swap执行操作。这套流程一共也就一分钟,但能免掉很多半夜重启机器的麻烦。
如果你按这篇的操作走完了一遍自己的swap方案,建议顺手做两件事:一是把swapon --show的输出截图或记在服务器资产信息里,后续巡检能快速对比;二是给fstab改动加个注释,说明哪一行是后来加的,避免以后同事或者半年后的自己看到配置一脸茫然。好习惯比好命令更值钱。
