linux 常见问题

ps

查看进程

1
ps aux

kill pkill

1
kill -9 pid

pkill 后面可以直接写进程的名字

如果是一个服务使用 systemctl stop

w who

看谁正在连接系统

lsof 查看端口占用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#列出所有打开的文件:
lsof
备注: 如果不加任何参数,就会打开所有被打开的文件,建议加上一下参数来具体定位

# 查看谁正在使用某个文件
lsof /filepath/file

#递归查看某个目录的文件信息
lsof +D /filepath/filepath2/
备注: 使用了+D,对应目录下的所有子目录和文件都会被列出

# 比使用+D选项,遍历查看某个目录的所有文件信息 的方法
lsof | grep ‘/filepath/filepath2/’

# 列出某个用户打开的文件信息
lsof -u username
备注: -u 选项,u其实是user的缩写

# 列出某个程序所打开的文件信息
lsof -c mysql
备注: -c 选项将会列出所有以mysql开头的程序的文件,其实你也可以写成lsof | grep mysql,但是第一种方法明显比第二种方法要少打几个字符了

# 列出多个程序多打开的文件信息
lsof -c mysql -c apache

# 列出某个用户以及某个程序所打开的文件信息
lsof -u test -c mysql

# 列出除了某个用户外的被打开的文件信息
lsof -u ^root
备注:^这个符号在用户名之前,将会把是root用户打开的进程不让显示

# 通过某个进程号显示该进行打开的文件
lsof -p 1

# 列出多个进程号对应的文件信息
lsof -p 123,456,789

# 列出除了某个进程号,其他进程号所打开的文件信息
lsof -p ^1

# 列出所有的网络连接
lsof -i

# 列出所有tcp 网络连接信息
lsof -i tcp

# 列出所有udp网络连接信息
lsof -i udp

# 列出谁在使用某个端口
lsof -i :3306

# 列出谁在使用某个特定的udp端口
lsof -i udp:55

# 特定的tcp端口
lsof -i tcp:80

# 列出某个用户的所有活跃的网络端口
lsof -a -u test -i

# 列出所有网络文件系统
lsof -N

#域名socket文件
lsof -u

#某个用户组所打开的文件信息
lsof -g 5555

# 根据文件描述列出对应的文件信息
lsof -d description(like 2)

# 根据文件描述范围列出文件信息
lsof -d 2-3

SSH免密登录

  • 生成密钥对

    -t 指定密钥类型,默认是 rsa ,可以省略。
    -C 设置注释文字,比如邮箱。
    -f 指定密钥文件存储文件名。

    如果想免密登录,密钥不要填写密码,否则必须验证密钥密码才能登录

    1
    2
    3
    4
    ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

    # 或者创建使用 ed25519 加密算法的密钥
    ssh-keygen -t ed25519 -C "your_email@example.com"
  • 上传共钥到服务器对应账号的 home 目录下.ssh 文件夹下面,公钥的权限为 600

    linux 执行以下命令

    1
    ssh-copy-id -i mykey_rsa.pub you_user_name@xxx.xxx.xxx.xxx

    window 需要手动在服务器上创建文件,

    1
    2
    3
    4
    5
    6
    7
    touch  ~/.ssh/authorized_keys

    # 或直接写入文件
    echo "your_public_key_here" >> ~/.ssh/authorized_keys

    # 修改权限
    chmod 600 authorized_keys
  • 指定私钥登陆,私钥的权限为 600

    1
    ssh -i 私钥 user@xxx.xxx.xxx.xxx
  • 通过配置文件免密登陆,在本地服务器把私钥复制到 home 下的.ssh 文件夹下面

    创建名称为 config 的文件,配置单一服务器免密登陆

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    Host tencent
    HostName 124.222.139.87
    User ubuntu
    IdentityFile ./.ssh/id_ed25519
    IdentitiesOnly yes


    # Host 别名
    #  HostName IP
    #  Port 端口
    #  User 用户名
    #  IdentitiesOnly yes
    # IdentityFile ~/.ssh/user_rsa (私钥路径)
    # Protocal 2 (协议版本号)
    # Compression yes
    # ServerAliveInterval 60 (防止被踢配置,长时间没有操作会被踢掉,每隔60秒发一个信号)
    # ServerAliveCountMax 20 (最大连接数)
    # LogLevel INFO

    通配符配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Host app-produce
    HostName 192.168.1.10
    Port 22
    User appuser
    IdentityFile ~/.ssh/id_ed25519

    Host \*\_produce
    User commonuser
    IdentityFile ~/.ssh/id_ed25519
    Port 22

wget

wget 是一个从网络上自动下载文件的自由工具,支持通过 HTTP、HTTPS、FTP 三个最常见的 TCP/IP 协议 下载,并可以使用 HTTP 代理。”wget” 这个名称来源于 “World Wide Web” 与 “get” 的结合。

wget 可以在用户退出系统的之后在后台执行。这意味这你可以登录系统,启动一个 wget 下载任务,然后退出系统,wget 将在后台执行直到任务完成,相对于其它大部分浏览器在下载大量数据时需要用户一直的参与.

1
wget (选项) (参数)

其中选项如下:

-a<日志文件>:在指定的日志文件中记录资料的执行过程;

-A<后缀名>:指定要下载文件的后缀名,多个后缀名之间使用逗号进行分隔;

-b:进行后台的方式运行 wget;

-B<连接地址>:设置参考的连接地址的基地地址;

-c:继续执行上次终端的任务;

-C<标志>:设置服务器数据块功能标志 on 为激活,off 为关闭,默认值为 on;

-d:调试模式运行指令;

-D<域名列表>:设置顺着的域名列表,域名之间用“,”分隔;

-e<指令>:作为文件“.wgetrc”中的一部分执行指定的指令;

-h:显示指令帮助信息;

-i<文件>:从指定文件获取要下载的 URL 地址;

-l<目录列表>:设置顺着的目录列表,多个目录用“,”分隔;

-L:仅顺着关联的连接;

-r:递归下载方式;

-nc:文件存在时,下载文件不覆盖原有文件;

-nv:下载时只显示更新和出错信息,不显示指令的详细执行过程;

-q:不显示指令执行过程;

-nh:不查询主机名称;

-v:显示详细执行过程;

-V:显示版本信息;

–passive-ftp:使用被动模式 PASV 连接 FTP 服务器;

–follow-ftp:从 HTML 文件中下载 FTP 连接文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
wget http://test.com/testfile.zip ->下载指定文件到当前文件夹
wget -O wordpress.zip http://test.com/download ->指定保存名字
wget --limit-rate=300k http://www.linuxde.net/testfile.zip ->限制下载速度
wget -c http://www.linuxde.net/testfile.zip ->断点续传
wget -b http://www.linuxde.net/testfile.zip ->后台下载

# 设置使用指定浏览器下载(伪装下载)
wget --user-agent="Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16" http://www.linuxde.net/testfile.zip

wget --spider url ->测试下载
wget --tries=40 URL ->设置重试次数为40
wget -i filelist.txt ->从filelist.txt获取下载地址

# 镜像网站
# --miror开户镜像下载。
# -p下载所有为了html页面显示正常的文件。
# --convert-links下载后,转换成本地的链接。
# -P ./LOCAL保存所有文件和目录到本地指定目录
wget --mirror -p --convert-links -P ./LOCAL URL

wget --reject=gif ur ->下载一个网站,但你不希望下载图片,可以使用这条命令
wget -o download.log URL ->把下载信息存入日志文件
wget -Q5m -i filelist.txt ->限制总下载文件大小
wget -r -A.pdf url ->下载指定格式文件

# FTP下载
wget ftp-url
wget --ftp-user=USERNAME --ftp-password=PASSWORD url

修改系统时间时区

最标准且永久生效的方法是使用系统自带的 timedatectl 工具。

1
2
3
4
5
6
7
sudo timedatectl set-timezone Asia/Shanghai

#强制开启网络时间同步 (NTP)
sudo timedatectl set-ntp true

#检查时间状态
timedatectl status

lvm-cache

为HDD添加SSD作为缓存盘,提高读写速度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 擦除所有分区签名(极其危险,请确保 /dev/nvme0n1 就是目标盘)
wipefs -a /dev/nvme0n1
wipefs -a /dev/sdc

# 彻底销毁 GPT 分区表
sgdisk --zap-all /dev/nvme0n1
sgdisk --zap-all /dev/sdc

# 创建物理卷 (PV),将这两块干净的硬盘注册为 LVM 的底层物理存储节点。
pvcreate /dev/sdc /dev/nvme0n1

# 创建基础卷组 (VG) 和逻辑卷 (LV):关键:此时只用机械硬盘.为了确保你的基础数据 100% 落在机械硬盘上,我们先只用机械硬盘创建一个卷组,并把它的全部空间划给一个逻辑卷。
vgcreate vg_home_data /dev/sdc

# 将 vg_home_data 的所有空间分配给逻辑卷 lv_home_data
lvcreate -n lv_home_data -l 100%FREE vg_home_data

# 如果提示 WARNING: zfs_member signature detected on /dev/vg_home_data/lv_home_data at offset 401408. Wipe it? [y/n]:
# 可以通过管道符批量处理
yes | lvcreate -n lv_home_data -l 100%FREE vg_home_data

#将 NVMe 固态扩容进卷组:房子建好了,现在把你的固态硬盘加入到 vg_home_data 这个卷组里,作为后续的缓存备用。
vgextend vg_home_data /dev/nvme0n1

# 在固态硬盘上创建缓存池,并绑定到机械硬盘的 lv_home_data 上
lvcreate -y --type cache --cachemode writeback -l 95%FREE -n home_data_cache vg_home_data/lv_home_data /dev/nvme0n1

# 查看命中率与"脏数据"
# WriteHits (写命中): 写入时成功被 SSD 接管的次数。多线程下载时,这个数值会疯狂暴涨,代表固态硬盘正在疯狂抗压。
# DirtyBlocks (脏数据块): 这是 Writeback 模式特有的概念!意思是“目前还在 SSD 里,还没有来得及搬到机械硬盘里的数据量”。下载时它会堆积,系统闲置时,LVM 会在后台默默把它们顺序写入机械盘,这个数值就会慢慢清零。

watch -n 1 "lvs -o name,data_percent,cache_write_hits,cache_dirty_blocks vg_home_data/lv_home_data"

LXC 容器使用LVM存储

如何配置给 LXC 容器使用LVM存储, 首先为什么不能直接直通给 LXC?是因为 LXC 容器的底层机制决定的。即便你现在手里是一个纯纯的物理机械硬盘 /dev/sda,我依然不建议你直接把它直通给 LXC 容器。

如果是 虚拟机 (VM): 虚拟机有自己完整的“虚拟主板”。你可以直接把 /dev/sda 或者 /dev/vg_home_data/lv_home_data 作为一块“虚拟硬盘”挂载给它。虚拟机开机后,会认为自己插了一块新硬盘,并在虚拟机内部自己进行格式化。

如果是 LXC 容器: LXC 没有“主板”,它就是一个隔离的进程,直接和 PVE 宿主机共用同一个 Linux 内核。如果你强行把裸设备(/dev/sda 或逻辑卷)映射进容器(需要改复杂的 cgroup 权限,特别是无特权容器),很容易遇到权限报错。

对 LXC 容器而言,不管是物理的 /dev/sda,还是带缓存魔法的 lv_home_data,最规范、最不容易出 Bug 的用法,都是让 PVE 宿主机去格式化它,把它变成一个文件夹,然后把文件夹映射给容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 在 PVE 宿主机上格式化并挂载
# 首先,我们要给这个裸设备穿上文件系统(推荐 ext4),并让 PVE 把它挂载出来。

# 1. 格式化为 ext4 文件系统(只需执行一次)
mkfs.ext4 /dev/vg_home_data/lv_home_data

# 2. 在 PVE 宿主机上创建一个挂载点(比如叫 gamedata)
mkdir -p /mnt/gamedata

# 3. 挂载上去
mount /dev/vg_home_data/lv_home_data /mnt/gamedata


#为了让 PVE 每次重启后都能自动挂载,你需要把它写进开机启动文件里, 编辑 fstab 文件
nano /etc/fstab
# 在末尾添加
/dev/vg_home_data/lv_home_data /mnt/gamedata ext4 defaults 0 2

# 将目录映射进 LXC 容器
# 在 pve UI 界面通过 Mount Point 添加挂在点,或通过以下命令,假设你的 LXC 容器的 ID 是 101(请替换为你的实际 ID)

pct set 101 -mp0 /mnt/gamedata,mp=/downloads

VM虚拟机使用LVM存储,需要用到“块设备直通(Block Device Passthrough)

配置开启 iSCSI 服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 在的终端执行以下命令,安装并进入 targetcli 交互式命令行:

apt update
apt install targetcli-fb -y
targetcli

# 把你的 LVM Cache 逻辑卷注册为一个名为 home_data 的块设备
cd /backstores/block
create name=home_data dev=/dev/vg_home_data/lv_home_data

# 创建 iSCSI 目标 (Target):创建一个对外的 iSCSI 节点名称(IQN)。这里以当前的年份月份命名:
cd /iscsi
create iqn.2026-07.local.pve:homedata

# 映射 LUN (逻辑单元):将刚才创建的底层存储块 gamedisk 绑定到这个 iSCSI 目标上:
cd iqn.2026-07.local.pve:homedata/tpg1/luns
create /backstores/block/home_data

# 为了数据安全,我们要告诉 PVE 只允许你的 Windows 电脑连接,拦截网络里其他乱七八糟的请求。这里要用到你刚才在 Windows 上复制的 IQN 名字
# 在windows 打开 iscsi initiator 在 configuration 标签里面 initiator name
cd ../acls
create 你的Windows_IQN

# 所有的配置现在都在内存中,我们需要将其保存到配置文件并退出工具:

cd /
saveconfig
exit

Q: 如果 targetcli-fb 用 block Block 模式,数据也能先进 pve 的内存么,还是说只有fileio 才能进pve的内存?
A: 如果使用 Block 模式,数据将【基本跳过】PVE 的系统内存(Page Cache),直接砸向底层的块设备。而 FileIO 默认会大量吃掉 PVE 的内存。

这也是为什么我们在前一步骤中,必须配合 dm-writecache (SSD) 来使用 Block 模式的原因。下面为你拆解这两种模式在内存利用上的根本差异:

FileIO:严重依赖内存的伪装者
当你在 PVE 中使用 fileio 创建 iSCSI 目标时,Linux 内核会把这个虚拟磁盘当成一个普通的本地文件(.raw 或 .img)来处理。

数据流向:Windows 发送数据 ➡️ PVE 内存(Page Cache 页缓存) ➡️ PVE 机械硬盘。

现象:你会发现就算没有 SSD,一开始拷文件也能跑到 280MB/s。但这是虚假的繁荣,因为数据其实全堆在 PVE 的内存里。等内存快塞满时,内核开始手忙脚乱地把内存数据往机械硬盘里倒腾,速度会瞬间断崖式下跌到 0,甚至卡死。

致命风险:如果此时 PVE 突然死机或停电,堆在内存里(还没写进机械盘)的脏数据瞬间灰飞烟灭。这对于 iSCSI 来说是毁灭性的,会导致 Windows 端的 NTFS 索引损坏,整个盘变成 RAW。

Block 模式:直达底层的直通车 (Direct I/O)
当你在 LIO (targetcli) 中使用 block (iblock) 模式时,它绕过了 Linux 的虚拟文件系统(VFS)和页缓存(Page Cache)。

数据流向:Windows 发送数据 ➡️ PVE 网卡缓冲(微秒级停留) ➡️ 直接下发 SCSI 指令到底层块设备。

现象:系统不再用大量的内存去给写入做缓冲。这意味着,如果底层是机械硬盘,你的写入速度会直接被机械硬盘的真实速度(比如 100MB/s)卡死,甚至因为 iSCSI 的小块随机写入,速度可能掉到只有 30MB/s。

这就是为什么我们需要 dm-writecache! 因为 Block 模式没有了内存做缓冲,我们就用物理的 SSD (dm-writecache) 来代替内存的作用。

Q:为什么windows写入pve是直接写入内存,但是拷贝一个大电影的时候,复制速度会卡到0
A: PVE 的 LVM Cache 底层使用的是 dm-cache 技术,默认采用了极其聪明的 smq (Stochastic Multiqueue) 缓存策略。

这个策略有一个核心逻辑:保护 SSD 寿命,防止“缓存污染(Cache Thrashing)”。

当 Windows 刚开始传数据时,LVM 以为是普通的小文件写入,于是把数据丢进 SSD(这就是你看到缓存增长的原因)。

但是,当 LVM 发现这是一个连续的、巨大的数据流(比如你的 8GB 电影)时,它会立刻触发 sequential_threshold(顺序 I/O 阈值)保护机制。

LVM 的逻辑是:“这种连续大文件写进去就是占地方,以后大概率也是连续读,直接交给机械硬盘处理最合适,没必要磨损 SSD 的颗粒。”

结果:LVM 会在中途主动将后续的数据流从 SSD 切换,直接穿透到后端的机械硬盘上。因为你的单块机械硬盘真实写入速度就是 100MB/s 左右,所以最终速度被死死卡在这个数值。

Q: 开启 Turn off Windows write-cache buffer flushing on the device 有什么效果。
A: 无论你是否勾选这个选项,数据都会先进入 Windows 的内存(RAM)。

这里需要区分两个极其关键的概念:“进内存(Write Cache)”和“清空内存(Buffer Flushing)”。

你看到的属性面板里,其实有两个复选框:

启用设备上的写入缓存(默认开启):这个选项决定了数据是否先进入 Windows 内存。

关闭设备上的 Windows 写入高速缓存缓冲区刷新(你提到的这个):这个选项决定了 Windows 是否向服务器发送“立刻把缓存写进物理硬盘”的强制命令(Flush/FUA)。

下面为你还原勾选与不勾选时,到底发生了什么导致了“卡顿”:

状态一:不勾选(默认状态,会卡顿掉速)
当你不勾选这个选项时,Windows 会极其谨慎:

狂飙:数据先以 2.5G 的极速塞满 Windows 的内存缓冲区。

强制对账(发 Flush 指令):Windows 觉得内存里攒得够多了,于是通过网络给 PVE 发送一个 Flush 指令:“停下!现在立刻把你接收到的数据彻底写进底层的机械硬盘里,没写完之前不许接新数据!”

卡顿掉到 0:因为 PVE 底层的机械硬盘只有 100MB/s 的速度,要消化这几个 GB 的数据需要好几十秒。在这几十秒里,Windows 在干等 PVE 的“写完确认”信号,复制进度条随之彻底卡死,速度掉到 0。

状态二:勾选(关闭刷新,速度平滑但危险)
当你勾选这个选项时,你实际上剥夺了 Windows 发送强制对账指令(Flush)的权利:

狂飙并持续:数据依然先进入 Windows 内存,然后源源不断地通过 2.5G 网络发给 PVE。

不管底层死活:Windows 不再发送强制落盘的指令,它采取“发后不理”的态度。只要 PVE 的系统内存(Page Cache)还没满,它就继续发。

视觉不卡顿:因为没有了那个“必须等机械盘写完”的强制等待期,整个复制过程在 Windows 看来非常顺畅,也就不会出现断崖式掉到 0 的卡顿。

但是代价极其高昂:
因为 Windows 放弃了“对账”,如果在复制过程中,或者复制刚完成的几秒钟内,网线松了、PVE 意外重启、或者交换机闪断,那些存在 Windows 内存里以及 PVE 内存里、还没来得及真正写到机械硬盘上的数据,会瞬间灰飞烟灭,并大概率导致你的 iSCSI 分区文件系统直接损坏(变成 RAW 格式)。

iSCSI策略设计

服务端主外,客户端主内。

写入加速:交给 PVE 服务端 (dm-writecache) 在 PVE 端,配置一块 SSD 作为 dm-writecache。

作用:它作为唯一的数据接收大闸。Windows 传来的 50GB 大文件,全部由它安全、全速地吃下。就算此时 Windows 蓝屏、网线断了,数据只要进了 PVE 的 SSD 就绝对不会丢。

从 lvm-cache(即 dm-cache)切换到 dm-writecache,绝不能直接强行删除 SSD 逻辑卷。

因为 lvm-cache 默认可能是“回写(Writeback)”模式,SSD 里可能还存留着尚未刷入机械硬盘的“脏数据(Dirty Data)”。如果直接暴力破坏,你的整个 iSCSI 磁盘就会永久损坏。

正确的做法是让内核“平滑退役”旧缓存,然后再挂载新缓存。请按照以下安全步骤操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# 1.断开 Windows iSCSI 连接:防止新数据涌入.为了让旧缓存能尽快把数据排空,必须先切断数据源。去 Windows 端的 iSCSI 发起程序 中,将目标 断开连接 (Disconnect)。

# 2.安全解除并销毁旧的 lvm-cache:耗时取决于脏数据量,切勿强行中断.在 PVE 的 SSH 命令行中,输入以下命令解除缓存:
# 执行完毕后,旧的 SSD 缓存池会被自动销毁并释放空间,而你的原始数据卷会完好无损地保留下来。
lvconvert --uncache vg_home_data/lv_home_data
# Flushing 0 blocks for cache vg_home_data/lv_home_data.
# Logical volume "home_data_cache_cpool" successfully removed.
# Logical volume vg_home_data/lv_home_data is not cached.

# 暴力拆除旧逻辑卷 (LV):数据将在此步被销毁.强行删除损坏的缓存和主数据卷:
lvremove -f vg_home_data/home_writecache
lvremove -f vg_home_data/lv_home_data

# 创建主存储
yes | lvcreate -l 100%FREE -n lv_home_data vg_home_data /dev/sdc
# 2.在 SSD 上切出新的蓄水池:必须在命令末尾指定 /dev/nvme0n1.既然空间释放了,我们要在这个大 VG 里切出一块名为 nvme_writecache 的新逻辑卷。极其重要: 因为你的 VG 里同时有机械盘和固态盘,为了防止系统把这个蓄水池建在机械盘上,必须在命令最后指名道姓要求用 /dev/nvme0n1
lvcreate -n home_writecache -l 95%FREE vg_home_data /dev/nvme1n1

# 3.当你刚用 lvcreate 创建完 home_writecache 时,系统会自动把它“激活”(挂载在内核中备用)。
# 但是 dm-writecache 架构有一个硬性要求:它必须在底层彻底接管这个卷的格式。所以在此之前,我们需要手动把这个新建的缓存卷“停用”(从内核中剥离出休眠状态),才能把它当做外挂缓存装定到主数据卷上。
# 停用新建的缓存卷:让内核释放锁定.执行这条命令,把刚才新建的 SSD 卷变成 inactive(非激活)状态:
lvchange -a n vg_home_data/home_writecache

# 4.激活 dm-writecache 模式:将蓄水池盖在数据卷上.最后,将刚才创建的高速蓄水池,以 writecache 模式正式绑定到你的 2.73T 主数据卷上:
lvconvert --type writecache --cachevol vg_home_data/home_writecache vg_home_data/lv_home_data
# Erase all existing data on vg_home_data/home_writecache? [y/n]: y
# Using writecache block size 4096 for unknown file system block size, logical block size 512, physical block size 512.
# WARNING: unable to detect a file system block size on vg_home_data/lv_home_data
# WARNING: using a writecache block size larger than the file system block size may corrupt the file system.
# Use writecache block size 4096? [y/n]: y
# 第二个 y(Use writecache block size 4096?):
# 为什么会弹这个? 因为你的 lv_home_data 是通过 iSCSI 挂载给 Windows 使用的,底层格式化(如 NTFS)是在 Windows 端完成的。PVE 宿主机无法直接“透视”到里面的文件系统块大小,所以它报了个“无法检测”的警告。
# 为什么选 4096? Windows 的 NTFS 文件系统默认簇大小(Block Size)几乎全都是 4096 字节 (4KB)。dm-writecache 使用 4096 字节不仅性能最好,而且能与 Windows 的文件系统完美对齐。

# 1. 永久修改并发数为 4
lvchange --cachesettings 'writeback_jobs=4' vg_home_data/lv_home_data
# 2. 永久修改高低水位
lvchange --cachesettings 'high_watermark=30 low_watermark=10' vg_home_data/lv_home_data

# 3. 永久修改自动提交时间(可选,增加到 5秒/5000毫秒)
# dm-writecache 的元数据全部保存在“缓存盘(SSD)”的头部专属区域内,它压根不需要写进机械硬盘。这 5 秒发生了什么?
# 系统只是在 SSD 上极速记录一笔:“刚才收到的这几批数据块已经有效,索引已建好”。对于 SSD 而言,写几个 4KB 的元数据块耗时在 微秒(μs)级别,就像在便签纸上打了个勾,对 SSD 自身寿命和性能都几乎毫无察觉。
# 机械硬盘在这个瞬间需要做什么?什么都不需要做。 此时根本没有任何指令发给机械硬盘,机械硬盘完全不受这 5 秒定时器的干扰。
lvchange --cachesettings 'autocommit_time=5000' vg_home_data/lv_home_data

# 验证
dmsetup table vg_home_data-lv_home_data
# 如果没有生效必须热更新
dmsetup resume vg_home_data-lv_home_data

# 5.targetcli 找到旧的 LUN 和 fileio 名称
targetcli
ls

# 6.删除旧的 LUN 映射:必须先删 LUN,才能删底层存储.先解除 iSCSI 目标对旧 fileio 的占用。把下面命令中的 iqn... 替换为你自己的名称(多用 Tab 补全):
/iscsi/iqn.2026-07.local.pve:homedata/tpg1/luns/ delete lun0

# 7.删除旧的 fileio/block 设备:释放对 LVM 的 fileio/block 占用.删除旧的 fileio/block 后端存储(把 旧的名字 换成你刚才看到的名称):
/backstores/fileio delete fileio_home_data

# 8.创建全新的 Block 设备:指定我们刚才搞好的 LVM 路径.现在,创建一个纯粹的块级设备,命名为 block_home_data ,并指向你的主数据卷
# 在底层已经是 4K 的情况下,必须显式告诉 iSCSI 协议对外广播自己是 4K 盘。进入 targetcli,在创建块设备后,必须立即设置 block_size:
/backstores/block create name=block_home_data dev=/dev/vg_home_data/lv_home_data

/backstores/block/block_home_data set attribute block_size=4096

# 9.将新 Block 映射为 LUN:重新挂载给 iSCSI 目标.把刚刚建好的 block_home 挂载到你的 iSCSI 目标上
/iscsi create iqn.2026-07.local.pve:homedata
/iscsi/iqn.2026-07.local.pve:home-data/tpg1/acls create 你的Windows_IQN
/iscsi/iqn.2026-07.local.pve:homedata/tpg1/luns create /backstores/block/block_home_data

# 保存退出
saveconfig
exit

# 监控 lvm-cache dm-cache状态
watch -n 1 "lvs -o name,data_percent,cache_write_hits,cache_dirty_blocks vg_home_data/lv_home_data"

# 监控 lvm-cache dm-writecache 状态
# | 序号 | 示例值 | 字段名称 (Kernel 源码) | 详细功能与解读 |
# | :---: | :--- | :--- | :--- |
# | **1** | `0` | **Starting Sector** | 逻辑卷起始扇区号(通常为 0)。 |
# | **2** | `5860524032` | **Length (Sectors)** | 设备总扇区数(换算后约为 2.72 TB)。 |
# | **3** | `writecache` | **Target Type** | Device-mapper 驱动模块类型名称。 |
# | **4** | `0` | **Error / Status** | 错误码(`0` 为正常,非 0 表示处于错误/只读模式)。 |
# | **5** | `118312625` | **Total Blocks** | **缓存盘总块数**(按 4KB/块 计算,约 451.3 GB)。 |
# | **6** | `62277359` | **Free Blocks** | **当前空闲可用块数**(尚未被脏数据或读缓存占用的块)。 |
# | **7** | `0` | **Writeback in progress** | **当前正在进行回写锁定的块数**。 |
# | **8** | `842069694` | **Read Hits** | **读命中次数**(直接由 SSD 返回数据的累计 I/O 次数)。 |
# | **9** | `512795821` | **Read Misses** | **读未命中次数**(SSD 无缓存,穿透到底层机械盘读取的次数)。 |
# | **10** | `592773768` | **Write Hits** | **写命中次数**(目标块已在 SSD 中,直接覆盖更新的次数)。 |
# | **11** | `279529` | **Write Misses** | **写未命中次数**(写入全新位置,在 SSD 中新分配块的次数)。 |
# | **12** | `29003956` | **Dirty Blocks** | **【核心指标】当前积压的脏块数**(已入 SSD 但**未同步到底层机械盘**的数据,当前约 110.6 GB)。 |
# | **13** | `0` | **Clearing in progress** | 当前正在执行擦除/元数据清理的块数。 |
# | **14** | `563490289` | **Total Flushed Blocks** | **累计已刷盘块数**(历史成功写回到底层机械盘的总块数)。 |
# | **15** | `0` | **Discard Blocks** | 累计处理的 TRIM/Discard 丢弃块统计。 |
# | **16** | `1815036` | **Allocated Structures** | **当前分配的元数据结构体/内存节点数**(反映驱动占用的内存开销)。 |
# | **17** | `0` | **Corrupt / Bad Blocks** | 损坏/校验失败的坏块数(非 0 代表硬件故障或数据损坏)。 |
# 0 5860524032 writecache 0 118312625 62277359 0 842070036 512795837 592774496 279529 29004304 0 563490669 0 1815077 0
watch -n 1 'dmsetup status vg_home_data-lv_home_data'

watch -n 1 -t 'dmsetup status vg_home_data-lv_home_data | awk "{
total=\$5; free=\$6; in_flight=\$7;

used = total - free;

total_gb = total * 4 / 1024 / 1024;
free_gb = free * 4 / 1024 / 1024;
dirty_gb = used * 4 / 1024 / 1024;
inflight_mb = in_flight * 4 / 1024;

dirty_ratio = (total > 0) ? (used / total) * 100 : 0;

read_hit=\$8; read_miss=\$9; write_hit=\$10; write_miss=\$11;
total_reads = read_hit + read_miss;
read_hit_ratio = (total_reads > 0) ? (read_hit / total_reads) * 100 : 0;

printf \"%-22s: %.2f GB\n\", \"SSD 缓存总容量\", total_gb;
printf \"%-22s: %.2f GB (这就是脏数据池)\n\", \"当前占用 (未落盘)\", dirty_gb;
printf \"%-22s: %.1f%%\n\", \"当前脏数据水位\", dirty_ratio;
printf \"%-22s: %.2f MB\n\", \"正在写入机械盘的实时量\", inflight_mb;
printf \"%-22s: %.2f%% (命中: %d, 未命中: %d)\n\", \"读缓存命中率\", read_hit_ratio, read_hit, read_miss;
printf \"%-22s: %d 次 / %d 次\n\", \"写入命中/未命中\", write_hit, write_miss;
}"'

# 检查底层是否抛出内核错误
dmesg -T | grep -E "blocked|hung|writecache|I/O error|timeout"

# 查看硬盘状读写状态
iostat -x -z 1

#监控系统内存状态
watch -n 1 "free -h; echo '===================='; grep -E 'Dirty|Writeback' /proc/meminfo"

#如果达到水位之后,还是不落盘执行以下命令
dmsetup message vg_home_data-lv_home_data 0 flush

Q: 为什么挂在后会变成容量变大且不能访问?
A: 记得刚才我们配置 dm-writecache 时,按下的那两个 y 吗?当时系统提示:Use writecache block size 4096? [y/n]: y。你之前的旧架构(fileio)默认向 Windows 汇报的磁盘扇区大小是 512 字节。我们刚才建立的 dm-writecache 底层使用的是 4096 字节 (4K) 扇区。$4096 \div 512 = 8$。Windows 的分区表原本是用 512 字节一把尺子量出来的,现在突然尺子的刻度放大了 8 倍,Windows 傻乎乎地直接把你的磁盘容量也乘以了 8!(如果你原本的磁盘分区是 2T 左右,$2 \text{T} \times 8 = 16\text{T}$,完全对上了!)

读取加速:交给 Windows 客户端 (PrimoCache L2 读缓存)

在你日常使用的 Windows 电脑里,划出一块 SSD 给 PrimoCache 当作二级缓存(L2 Cache)。

关键设置:进入 PrimoCache,关闭“延迟写入”,将这块 L2 缓存配置为 100% 纯读取(Read-Data Only)。

作用:当你第一次从 iSCSI 看电影或读数据时,数据从 PVE 的机械硬盘流向 Windows,PrimoCache 会在后台悄悄把这些数据在 Windows 本地的 SSD 里存一份。明天你再去读这个文件,它直接从你电脑本地的 SSD 秒开,连 2.5G 的网络带宽都不占!

彻底静默运行,丢弃该命令输出的所有正常信息和报错信息,不向屏幕打印任何内容,也不发送通知

1
2
3
命令  >  /dev/null  2>&1
│ │ │
① ② ③

① >(重定向):

把前面的输出内容,转存/流向后面的目标(默认省略了数字 1,即 1>)。

② /dev/null(Linux 的黑洞 / 垃圾桶):

Linux 系统内置的一个特殊虚拟设备文件,写入它的任何数据都会被系统瞬间丢弃。

所以 > /dev/null 的意思就是:把所有的“标准正常输出(stdout)”全部扔进垃圾桶。

③ 2>&1(合并错误输出):

2 代表 stderr(错误输出);

> 代表 重定向;

&1 代表 引用 1 号通道(stdout)当前指向的地方。

整句的意思是:把“错误输出(2)”也丢到跟“正常输出(1)”一样的地方去(即扔进 /dev/null 黑洞)。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2015-2026 SunZhiqi

此时无声胜有声!

支付宝
微信