内核包含的文件系统非常丰富,无法全部列举,本文主要讨论的是网络文件系统,具体是 NFS 、CIFS 、9P 和 CEPH 这 4 个文件系统。
第1,关于 NFS 的调试开关。
原理:NFS 的调试打印沿用了 sunrpc 实现的 dprintk() 接口,该接口调用 printk() ,借助一些标志位进行细粒度的调试控制,这些标志位保存在一个静态变量里,该变量通过 procfs 接口暴露给用户态进行修改。NFS 就是在此之上,新增了 nfs_debug 变量作为调试开关,该变量支持保存的标志参见 include/uapi/linux/nfs_fs.h ,宏定义都是 NFSDBG_XXX 这种格式,呈现给用户态的 proc 文件是 /proc/sys/sunrpc/nfs_debug。
使用: 首先看调用 dprintk() 的 C 源文件如何定义 NFSDBG_FACILITY ,例如 fs/nfs/blocklayout/blocklayout.c 对 NFSDBG_FACILITY 的定义是 NFSDBG_PNFS_LD ,然后看 NFSDBG_PNFS_LD 的定义,可见是 0x2000 ,那么就使用以下命令开启调试打印,即
echo 0x2000 > /proc/sys/sunrpc/nfs_debug
第2,关于 CIFS 的调试开关。
原理:CIFS 封装了自己的调试打印接口,即 cifs_dbg() 接口。它基于 pr_debug() 接口实现,并由 cifsFYI 变量作为总开关。这个变量呈现给用户态的 proc 文件是 /proc/fs/cifs/cifsFYI 。
使用:
使用前需确认内核是否支持 pr_debug() ,执行以下命令确认,即
grep DYNAMIC_DEBUG /boot/config-`uname -r`
如果看到 CONFIG_DYNAMIC_DEBUG=y ,则可以继续执行以下操作。
然后确认系统是否挂载 debugfs 以及挂载路径,执行以下命令确认,即
mount | grep debugfs
通常会看到以下输出:
debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime)
说明系统已挂载 debugfs 到 /sys/kernel/debug 目录。
然后打开 CIFS 的调试总开关,执行
echo 1 > /proc/fs/cifs/cifsFYI
接下来就看 cifs_dbg() 接口所在的 C 源文件和行号,假设在 fs/cifs/transport.c 第 68 行调用了 cifs_dbg() 接口,那么执行以下命令,即
echo "file fs/cifs/transport.c line 68 +p" > /sys/kernel/debug/dynamic_debug/control
最后为了看到输出内容,还要调整内核日志级别(原理参考本专栏的 控制内核日志打印 ),执行以下命令:
echo 8 4 1 7 > /proc/sys/kernel/printk
或者
dmesg -n 8
至此就开启 CIFS 的调试开关了。
第3,关于 CEPH 的调试开关。
原理:CEPH 封装的调试打印接口是 dout() ,与 CIFS 类似,也是调用 pr_debug() 接口。
使用:
使用前也需要确认前置条件,参见上述 CIFS 的说明。
接下来看 dout() 接口所在的 C 源文件和行号,假设在 fs/ceph/inode.c 第 768 行调用了 dout() 接口,那么执行以下命令,即
echo "file fs/ceph/inode.c line 768 +p" > /sys/kernel/debug/dynamic_debug/control
最后调整内核日志级别,执行 dmesg -n 8 即可。
第4,关于 9P 的调试开关。
原理:9P 封装的调试打印接口是 p9_debug() ,底层调用的是 pr_notice() 。p9_debug() 用 p9_debug_level 变量作为调试开关,其中通过不同标志位进行细粒度的打印控制。 p9_debug_level 变量由挂载选项 debug 来控制,它支持保存的标志位参见 include/net/9p/9p.h 定义的 p9_debug_flags 枚举,其中的枚举都是 P9_DEBUG_XXX 这种格式。
使用:在虚拟机里挂载 9P 时指定 debug 参数,假设要开启 P9_DEBUG_SLABS 标志,枚举值是 (1<<7) = 128 ,那么执行
mount -t 9p -o trans=virtio,debug=128,nfcp host9ptest /mnt/9ptest -oversion=9p2000.L
总结:
上述文件系统的调试打印接口都是基于内核已有的日志接口进行封装,封装的目的是方便新增一些标志位进行细粒度的控制,这些标志位保存在某个变量里。修改这个变量的方式可以是通过 proc 文件修改,也可以是通过挂载选项指定。