ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

fuser

fuser 文章目录一、前言二、实验环境一台 NVIDIA Jetson三、fuser 是怎么知道谁在用某个文件的四、最常用的两个姿势4.1 谁在占用某个 TCP 端口4.2 谁在占用某个文件五、看懂 ACCESS 列的六个字母六、实战场景把占着茅坑的进程揪出来场景 1umount / 报 busy 怎么办场景 2/dev/shm 满了到底是谁塞的场景 3MQTT 端口 1883 被谁占了场景 4CUPS 打印服务 631 端口场景 5RPC 端口 111UDP七、进阶把占用进程杀掉八、fuser vs lsof vs ss什么时候用哪个九、几个写脚本时常踩的坑1. 不加 sudo 看到空输出2. -v 输出在脚本里不友好3. 端口写法要带协议4. -m 接的应该是 mount point 或块设备不是普通文件十、完整命令速查表十一、回到那台 Jetson一次全身体检十二、结语附录本次实验的环境与版本一、前言很多 Linux 资源排障文章会告诉你 “先用lsof看一眼”但lsof在很多嵌入式 / 最小化镜像里根本没有装。而fuser来自psmisc包体积小、依赖少几乎是所有 Debian/Ubuntu 系镜像包括 NVIDIA Jetson 的 L4T 镜像默认就在的工具。它做的事情很专一给我一个名字文件、目录、块设备、TCP/UDP 端口我把正在使用它的进程 PID 全部列出来。在边缘设备上你经常会遇到这些场景NVMe SSD 显示 busyumount报target is busy想重启某个服务但端口被占着不知道是谁/dev/shm莫名其妙被塞满串口/dev/ttyACM0被某个进程占着打不开系统里跑着一堆python3、jtop、mosquitto想知道它们都在摸哪些资源。fuser就是干这些活的。二、实验环境一台 NVIDIA Jetson为了让后面的输出有上下文先把目标设备192.168.37.56的真实信息贴出来SSH 登录后采集$ hostname localhost.localdomain $ cat /etc/os-release | grep PRETTY PRETTY_NAMEUbuntu 24.04.3 LTS $ uname -a Linux localhost.localdomain 6.8.12-rt-tegra #1 SMP PREEMPT_RT Tue Sep 1 12:48:16 CST 2026 aarch64 aarch64 aarch64 GNU/Linux $ lscpu | head -10 Architecture: aarch64 CPU op-mode(s): 64-bit CPU(s): 12 Vendor ID: ARM Model name: - Thread(s) per core: 1 Core(s) per cluster: 12 CPU max MHz: 2601.0000 $ free -h total used free shared buff/cache available Mem: 59Gi 10Gi 54Gi 27Mi 1.5Gi 48Gi Swap: 2.0Gi 0B 2.0Gi $ df -h | grep -vE tmpfs|efivarfs Filesystem Size Used Avail Use% Mounted on /dev/nvme0n1p1 467G 38G 405G 9% /几个关键点这是一台ARM64 / aarch64架构的 NVIDIA JetsonTegra SoC边缘设备跑的是PREEMPT_RT 实时内核6.8.12-rt-tegra常见于机器人、感知、控制等对时延敏感的场景12 核 ARM、60GB 内存、467GB NVMe SSD根分区在/dev/nvme0n1p1挂载点/已经默认跑着cupsd、mosquitto、rpcbind、systemd-resolved、jtopJetson 监控工具、lttng-sessiond等。fuser在这台设备上的版本$ fuser -V fuser (PSmisc) 23.7 $ which fuser /usr/bin/fuser注意 PSmisc 23.7 是较新的版本输出格式和某些老教程里的略有不同但语义完全一致。下面所有命令都在这台 Jetson 上实际执行过。三、fuser是怎么知道谁在用某个文件的在开始敲命令之前先用一句话讲清原理否则你只会把它当黑盒。Linux 把进程打开了哪些文件这种关系全部保存在/proc/pid/fd/下——每个打开的文件描述符fd都是一个符号链接指向真实文件或 socket。fuser做的事情就是把你给的那个名字解析成内核能识别的内核对象inode、socket 的 local_port、mount point 等扫描/proc/*/fd/、/proc/*/maps、/proc/*/cwd、/proc/*/root、/proc/*/exe逐个进程对比命中的进程 PID 打印出来。正因为它是从/proc读出来的所以普通用户只能看到自己的进程要看其他用户root、mosquitto、_rpc 等的占用必须sudo。下面所有示例都加了sudo否则你会看到空输出而误以为没人占用。四、最常用的两个姿势4.1 谁在占用某个 TCP 端口这是日常最高频的场景“我的 22 端口被谁占了”$ sudo fuser -v 22/tcp USER PID ACCESS COMMAND 22/tcp: root 1 F.... systemd root 2218 F.... sshd root 8511 F.... sshd nvidia 8574 F.... sshd root 8617 F.... sshd nvidia 8656 F.... sshd root 8863 F.... sshd nvidia 8906 F.... sshd字段含义USER进程属主PID进程号ACCESS访问类型下面专门解释COMMAND进程名。22/tcp这种写法是fuser自己的语法糖——“协议/端口号”。等价的写法是用-n显式指定命名空间$ sudo fuser -v -n tcp 22 USER PID ACCESS COMMAND 22/tcp: root 1 F.... systemd root 2218 F.... sshd root 8511 F.... sshd ...这两种写法等价记住一种就行。我个人推荐22/tcp更短。注意 PID1是systemd本身因为它负责 socket activation会先把 22 端口占住等真实sshd起来后再交给它——这是 systemd 资源管理的正常表现不是异常。4.2 谁在占用某个文件$ sudo fuser -v /etc/hostname $没有输出说明这一刻没有进程正打开着/etc/hostname。这点很重要fuser 的空输出是一个有效结果意思是没人占用。在自动化脚本里判断一个文件是否被占用可以用-ssilent模式结合返回值判断$ sudo fuser -s /etc/hostname; echo $? 1返回值1表示没有进程占用0表示有进程占用非常适合写脚本。再看一个有占用的例子$ sudo fuser -v /tmp USER PID ACCESS COMMAND /tmp: root 1567 ..c.. atopacctdatopacctd把/tmp作为工作目录cwd所以fuser把它列出来了。..c..这一串字母描述的就是访问类型。五、看懂 ACCESS 列的六个字母-v输出里最容易让新手困惑的就是中间那串像F....、..c..、F...m一样的字母。它们其实是 6 个固定位置的标志位每个位置代表一种访问方式位置字母含义1c当前目录cwd进程的/proc/pid/cwd指向该文件2e可执行文件进程的/proc/pid/exe指向该文件3f打开的文件open file进程的/proc/pid/fd/中有此文件4r根目录root进程的/proc/pid/root指向该文件5m内存映射mmap出现在/proc/pid/maps中6.该位置无对应访问所以F....—— 该进程打开了此 fdF 实际是f的大写形式表示 “current file” 是直接打开的PSmisc 23.x 在第一列用大写以强调..c..—— 进程把该路径作为 cwdF...m—— 既被打开又被 mmap 进内存典型如jtop占用/dev/shmfrce.—— cwd root open常出现在容器或systemd这种根进程上。把这段记下来看fuser输出就再不会觉得它像乱码了。六、实战场景把占着茅坑的进程揪出来下面这些都是在192.168.37.56这台 Jetson 上实际复现过的真实场景。场景 1umount /报 busy 怎么办Jetson 的根文件系统在/dev/nvme0n1p1挂载到/。你想换 SD 卡 / 换 NVMe或者想从备份镜像里挂载一个相同分区做比较但mount时报错mount: /mnt: /dev/nvme0n1p1 already mounted or mount point busy.显然根分区正在被整个系统占用。用-m选项列出所有把这个文件系统当成 mount 用的进程$ sudo fuser -v -m /dev/nvme0n1p1 | head -30 USER PID ACCESS COMMAND /dev/nvme0n1p1: root kernel swap /swapfile root kernel mount / root 1 frce. systemd root 2 .rc.. kthreadd root 3 .rc.. pool_workqueue_release root 4 .rc.. kworker/R-rcu_g root 5 .rc.. kworker/R-rcu_p root 6 .rc.. kworker/R-slub_ root 7 .rc.. kworker/R-netns root 9 .rc.. kworker/0:0H-events_highpri root 11 .rc.. kworker/u24:0-ext4-rsv-conversion root 12 .rc.. kworker/R-mm_pe root 13 .rc.. rcu_tasks_kthread root 14 .rc.. rcu_tasks_rude_kthread root 15 .rc.. rcu_tasks_trace_kthread root 16 .rc.. ksoftirqd/0 root 17 .rc.. ktimers/0 root 18 .rc.. pr/legacy root 19 .rc.. rcu_preempt root 20 .rc.. rcub/0 root 21 .rc.. rcuc/0 root 22 .rc.. migration/0 root 23 .rc.. irq_work/0 root 24 .rc.. cpuhp/0 ...注意前两行很特别root kernel swap /swapfile root kernel mount /这两行不是进程而是内核自己把/dev/nvme0n1p1用作swap 源和挂载点。对于根分区来说这种占用是结构性的你不可能通过kill任何进程来解决——除非进入单用户模式或者重启到 initramfs否则永远 busy。这就是为什么在 Jetson 上扩容 rootfs、做备份还原时通常要走 USB recovery 模式或者 Live 系统而不是在线操作fuser一眼就能告诉你这是结构性占用别挣扎了。场景 2/dev/shm满了到底是谁塞的Jetson 跑jtop时监控数据会写到共享内存。有一天df -h /dev/shm显示 100% 满谁干的$ sudo fuser -v -m /dev/shm USER PID ACCESS COMMAND /dev/shm: root kernel mount /dev/shm root 1277 ....m lttng-sessiond root 3978 F...m jtop root 4018 F...m jtop root 4026 F...m jtop nvidia 4648 ....m python3 nvidia 4871 ....m python3 nvidia 4874 ....m python3 nvidia 4878 ....m python3 nvidia 4879 ....m python3 nvidia 4880 ....m python3 nvidia 4881 ....m python3一眼看到几个嫌疑犯jtopPID 3978、4018、4026—— 同时有F打开 fd和mmmap典型的共享内存消费者一堆python34648 起—— 这些很可能是用户跑的脚本比如处理摄像头帧的推理服务把模型或者中间张量扔进了 shmlttng-sessiond1277—— Linux trace 框架也用 shm 做环形缓冲区。如果继续排查可以用lsof /dev/shm/看具体哪些文件名被打开但fuser在不知道文件名的情况下能直接给出占用者名单这是它的独门优势。场景 3MQTT 端口 1883 被谁占了ss -tlnp显示127.0.0.1:1883在 LISTEN但没显示进程名因为进程属于别的 user。fuser直接穿透$ sudo fuser -v 1883/tcp USER PID ACCESS COMMAND 1883/tcp: mosquitto 2227 F.... mosquittomosquitto用专门的mosquitto用户跑所以普通用户ss看不到 PID但sudo fuser一击命中。场景 4CUPS 打印服务 631 端口$ sudo fuser -v 631/tcp USER PID ACCESS COMMAND 631/tcp: root 2617 F.... cupsd只有一个cupsd进程干净利落。场景 5RPC 端口 111UDPfuser同样支持 UDP$ sudo fuser -v 111/udp USER PID ACCESS COMMAND 111/udp: root 1 F.... systemd _rpc 1016 F.... rpcbind_rpc是 Debian/Ubuntu 给 rpcbind 专门开的低权用户。注意systemd又出现在这里——这是它的 socket activation 机制在接管 socket等rpcbind起来后由它接手。七、进阶把占用进程杀掉使用 kill 将占用文件的进程杀掉八、fuservslsofvsss什么时候用哪个很多人会问既然lsof也能查端口、查文件为什么还要fuser需求fuserlsofss/netstat查谁打开了某文件✅ 直接给 PID✅ 但需要lsof file❌ 不支持查谁占某端口✅port/tcp✅lsof -i :port✅ss -tlnp查某 mount point 的占用者✅-m一行命令⚠️lsof D /mnt慢得多❌输出 PID 给脚本✅ 默认就是 PID⚠️ 要解析文本⚠️ 要-p等额外参数在最小化镜像上可用✅ psmisc 极轻❌ 默认不装✅ iproute2 通常有总结一句fuser是端到端最短路径工具——给它一个名字它要么给你 PID 列表要么直接帮你杀进程。在 Jetson 这种最小化嵌入式 Linux 上lsof经常不在但fuser几乎永远在这是它的出生优势。九、几个写脚本时常踩的坑1. 不加sudo看到空输出这是最常见的误判。fuser从/proc/pid/fd/读普通用户没权限读别人的 fd 目录。所以看到空输出有两种含义真没人占用有人占用但不是你 user你无权看。脚本里要么sudo要么用-s配合返回值并明示检查权限。2.-v输出在脚本里不友好-v是给人看的给脚本用时建议去掉-v直接拿默认的 PID 输出$ sudo fuser /tmp 1567干净一行 PID方便for pid in $(sudo fuser /tmp)。3. 端口写法要带协议# 错fuser -v 22 ← 会被当成名叫 22 的文件 # 对fuser -v 22/tcp # 对fuser -v -n tcp 224.-m接的应该是 mount point 或块设备不是普通文件$ sudo fuser -m /dev/nvme0n1p1 ✅ $ sudo fuser -m / ✅ / 是 mount point $ sudo fuser -m /etc/hostname ❌ 普通文件相当于普通 fuser如果只想在必须是 mount point时才查用-M$ sudo fuser -M /mnt/usb只在/mnt/usb真的是一个挂载点时才返回结果避免误把目录当 mount。十、完整命令速查表把全文涉及到的命令汇总一份方便复制使用# 1. 端口最常用sudofuser-v22/tcp# 谁在用 TCP 22sudofuser-v1883/tcp# 谁在用 MQTT 1883sudofuser-v111/udp# 谁在用 UDP 111sudofuser-v-ntcp22# -n 显式语法等价上面# 2. 文件 / 目录sudofuser-v/etc/hostname# 谁打开了某文件sudofuser-v/tmp# 谁打开了某目录sudofuser-s/etc/hostname;echo$?# 静默判断看返回值# 3. 整个文件系统sudofuser-v-m/dev/nvme0n1p1# 谁在用这块盘sudofuser-v-m/# 谁在用根分区sudofuser-M/mnt/usb# 仅当是挂载点时才查# 4. IPv4/IPv6 限定sudofuser-v-422/tcp# 只看 IPv4sudofuser-v-622/tcp# 只看 IPv6# 5. 版本与帮助fuser-Vfuser# 无参数显示 Usage十一、回到那台 Jetson一次全身体检最后把全文涉及到的命令拼起来对192.168.37.56这台 Jetson 做一次完整的资源占用全身体检# 在 Jetson (192.168.37.56) 上执行sudofuser-v22/tcp631/tcp1883/tcp111/udp\/tmp /dev/shm /var/log /dev/nvme0n1p1一次跑完你会看到22/tcpsystemd 一堆sshd631/tcpcupsd1883/tcpmosquitto111/udpsystemdrpcbind/tmpatopacctd/dev/shmjtop、若干python3、lttng-sessiond/dev/nvme0n1p1内核 swap/mount 几乎所有进程。一张谁在用谁的全景图就这样画出来了。下次设备出问题比如 NVMe 满了、shm 满了、某端口起不来你只要把这台 Jetson 当活体标本——把上面这一串命令跑一遍问题就藏不住。十二、结语fuser是一个有古老智慧的小工具它没有花哨的功能只做一件事——告诉你谁在用这个名字。但正因为专注它做到了在最小化镜像里都能装下一个命令同时覆盖文件、目录、块设备、TCP/UDP 端口输出可以直接喂给kill从诊断到治疗无缝衔接。在一台 NVIDIA Jetson 边缘设备上跑了一遍真实场景后最大的感受是很多看似复杂的问题umount busy、端口冲突、shm 满其实只差一个fuser -v就能定位到根因。这就是小工具、大用处的真正含义。下次遇到target is busy或者端口被占别再瞎kill -9了——先fuser -v看清楚再说。附录本次实验的环境与版本项目值设备NVIDIA Jetson (Tegra)系统Ubuntu 24.04.3 LTS (Noble Numbat)内核6.8.12-rt-tegra SMP PREEMPT_RT aarch64CPU12 × ARM aarch64, 最高 2601 MHz内存60 GiB10 GiB 已用根分区/dev/nvme0n1p1→/(467 GiB, 9% 已用)Swap2 GiB swapfilefuser 版本PSmisc 23.7,/usr/bin/fuser监听端口53(Lo), 631, 22, 111, 1883(Lo), 5345(Lo), 6566, 5201在跑服务systemd, sshd, cupsd, mosquitto, rpcbind, jtop, lttng-sessiond, atopacctd, gnome-shell
返回列表