linux无法直接限制单个进程下载带宽,因内核无硬限速接口;ulimit和ionice不作用于网络栈,trickle仅对特定动态链接程序有效;唯一精准方案是tc+iptables按pid打标或cgroups v2+net_cls标记限速。

不能直接限制“某个进程的下载带宽”,因为 Linux 内核没有为单个进程提供网络出口带宽的硬限速接口;ulimit 不管用,ionice 和 trickle 有严重适用边界,真正可行的方案只有两个:按进程打标 + tc 过滤,或用 trickle 仅限特定用户态程序。
为什么 ulimit、ionice 都不能限制下载带宽
ulimit 只控制文件描述符、内存、CPU 时间等资源,完全不触碰网络栈;ionice 仅影响块设备 I/O 调度优先级(比如磁盘读写),对 socket 发送/接收无任何约束。试图用它们限速下载,结果必然是无效的。
-
ulimit -n限制打开文件数,和带宽无关 -
ionice -c 3 -p $PID只让进程在磁盘空闲时才能读写,不影响网卡发包速率 - 所有基于 syscalls 直接调用
send()/recv()的程序(如curl、wget、rsync)都不受ionice影响
trickle 只对部分程序有效,且必须提前启动
trickle 是用户态 LD_PRELOAD 注入工具,它劫持 socket API 调用并人为插入 delay。但它只对动态链接、非 setuid、且未绕过 libc 的程序生效。
- ✅ 有效:
trickle -d 200 wget http://...(wget使用 libc socket) - ❌ 无效:
trickle -d 200 dd if=/dev/zero | nc host port(dd不走 socket,nc可能静态链接) - ❌ 无效:
trickle -d 200 python3 -c "import requests; requests.get(...)"(某些 Python 发行版自带静态 OpenSSL) - ⚠️ 注意:
trickle无法限制已运行进程,必须在启动前加 wrapper
用 tc + iptables 按进程 PID 打标限速(推荐)
这是唯一能精准控制“某个正在运行的进程”的出口带宽的方法,原理是:利用 /proc/$PID/fd/ 下的 socket 文件获取其 inode → 通过 ss -tunp 或 lsof -nP -p $PID 提取源端口 → 用 iptables 标记该端口流量 → tc 对标记流限速。
- 步骤 1:查出进程所有 TCP/UDP 源端口
ss -tunp | awk '$7 ~ /'$PID'/ {print $5}' | cut -d: -f2 | sort -u - 步骤 2:用 iptables 标记这些端口的出向包
sudo iptables -t mangle -A OUTPUT -m owner --pid-owner $PID -j MARK --set-mark 1
(注意:需内核启用CONFIG_NETFILTER_XT_MATCH_OWNER,主流发行版默认开启) - 步骤 3:用 tc 绑定标记限速
sudo tc qdisc add dev eth0 root handle 1: htb default 30sudo tc class add dev eth0 parent 1: classid 1:1 htb rate 10mbitsudo tc class add dev eth0 parent 1:1 classid 1:10 htb rate 1mbit ceil 1mbitsudo tc filter add dev eth0 protocol ip parent 1:0 prio 1 handle 1 fw flowid 1:10 - ⚠️ 坑点:
--pid-owner在容器内或 systemd --scope 启动的进程里可能失效,此时需改用 cgroup v2 +net_cls(但需额外配置)
更可靠但需前置准备:用 cgroups v2 + net_cls 标记流量
如果进程可由你启动(如脚本调度),比 --pid-owner 更稳定的方案是把它放进专用 cgroup,并用 net_cls 控制器打标:
- 创建 cgroup:
sudo mkdir -p /sys/fs/cgroup/netlimit - 设置 classid:
echo 0x00100001 | sudo tee /sys/fs/cgroup/netlimit/net_cls.classid(classid 格式为 0x00XX0000) - 将 PID 加入:
echo $PID | sudo tee /sys/fs/cgroup/netlimit/cgroup.procs - iptables 标记:
sudo iptables -t mangle -A OUTPUT -m cgroup --cgroup 0x00100001 -j MARK --set-mark 1 - 后续 tc 规则同上
- ✅ 优势:不依赖进程是否存活、支持容器、systemd scope 兼容性好
- ❌ 劣势:无法 retroactively 限制已运行且未进 cgroup 的进程
真正难的不是写几条命令,而是判断目标进程是否走标准 socket 路径、是否在容器里、是否用了自定义网络命名空间——这些细节决定了 --pid-owner 能不能用,也决定了你得选 trickle 还是 cgroup + net_cls。没验证过进程实际网络行为就开干,大概率限了个寂寞。











