惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
美团技术团队
腾讯CDC
V
V2EX
G
Google Developers Blog
博客园 - Franky
博客园 - 司徒正美
Stack Overflow Blog
Stack Overflow Blog
阮一峰的网络日志
阮一峰的网络日志
Microsoft Azure Blog
Microsoft Azure Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
The Blog of Author Tim Ferriss
Recent Announcements
Recent Announcements
Google DeepMind News
Google DeepMind News
罗磊的独立博客
C
Check Point Blog
MyScale Blog
MyScale Blog
aimingoo的专栏
aimingoo的专栏
F
Fortinet All Blogs
酷 壳 – CoolShell
酷 壳 – CoolShell
V2EX - 技术
V2EX - 技术
The Last Watchdog
The Last Watchdog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
有赞技术团队
有赞技术团队
Security Archives - TechRepublic
Security Archives - TechRepublic
S
Security @ Cisco Blogs
W
WeLiveSecurity
D
DataBreaches.Net
Forbes - Security
Forbes - Security
V
Visual Studio Blog
P
Proofpoint News Feed
S
Secure Thoughts
H
Help Net Security
Cloudbric
Cloudbric
云风的 BLOG
云风的 BLOG
Microsoft Security Blog
Microsoft Security Blog
N
News and Events Feed by Topic
Schneier on Security
Schneier on Security
Engineering at Meta
Engineering at Meta
Attack and Defense Labs
Attack and Defense Labs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
S
Security Affairs
L
LangChain Blog
Last Week in AI
Last Week in AI
Martin Fowler
Martin Fowler
Hacker News: Ask HN
Hacker News: Ask HN
H
Heimdal Security Blog
M
MIT News - Artificial intelligence
The Register - Security
The Register - Security

est の 输入 输出和出入

iosevka字体让中文 ASCII diagram 图表对齐 为什么 Github OAuth 故意拦截 CORS gitweets改版,复刻微信「朋友圈」 MiMoCode 干完活儿发通知 写作能力和 locate cost grep vs sqlite 谁更适合微信聊天记录? 或许「数羊」真是个有效的入睡法 唯物主义「天命」论 我的 Vibe Coding 最佳实践——ADR文档 MacOS 快速插入当前时间 locate cost 基于 git 的零拷贝静态web服务器 AI和柜台费 Sutton 论 discovery Elon Musk 五步工作法 Playlet:DLNA听歌神器 免安装app播放NAS里的歌 不修改nginx接收websocket AI 硬伤 理解LLM的范式——它就是个差分机? 路径依赖 AI 流式接口的pattern FSRS核心字段 SVG 时钟 AI的自我觉醒是必然 唐宋之变 Louis Alexander 谈英语学习 Predict Next Word Instruct 的意义 Sentry 替代品 Bugsink 安装配置 浏览器通过WebGPU上做AI推理 Indent Is All You Need 语文学习和考试 失落的学习兴趣 如何跟孩子讲学习的意义 物权 Bonsai 在 M2 安装 The Porsche Diffusion 首页和404更新 无用之用——或许LLM真的还不是AGI 发明后训练的人真是天才 看好 Taalas 码奸 claw会代替员工? 如果拿 token 抵工资会发生什么? tmux enables AIs to operate servers safely chat.est.im launches at 3.14 2026 股灾? AI 弑父 Configurable Intelligence 巴别塔,Vibe Coding和Lisp之咒 Citrini的2028智力危机 中文不是唯一的意译语言 观星有感 体细胞阶级论——当繁衍权被剥夺
[AI] curl -NT. 导致100% CPU原因
est · 2026-07-08 · via est の 输入 输出和出入

有AI就是好使,搁以前自己得盯半天也看不明白。。。

问题确认

curl -NT. 在连接一个持续推送数据的 streaming 服务器时,会产生一个 紧密的 pause/unpause 循环,导致 100% CPU。

根因分析

-T.(注意是点号,不是减号)将 stdin 设为非阻塞模式,然后依赖 EAGAIN + CURL_READFUNC_PAUSE 机制来避免阻塞。核心循环发生在三个回调之间:

第一步 — 上传尝试失败src/tool_cb_rea.c):

  {
    rc = read(per->infd, buffer, sz * nmemb);
    if(rc < 0) {
      if(errno == EAGAIN) {
        errno = 0;
        config->readbusy = TRUE;
        return CURL_READFUNC_PAUSE;
      }
      /* since size_t is unsigned we cannot return negative values fine */
      rc = 0;
    }
  }

stdin 没数据 → read() 返回 EAGAIN → 设 readbusy=TRUE → 暂停上传。

第二步 — 下载数据到达立刻解除暂停src/tool_cb_wrt.c):

  if(config->readbusy) {
    config->readbusy = FALSE;
    curl_easy_pause(per->curl, CURLPAUSE_CONT);
  }

streaming 服务器持续发送数据 → tool_write_cb 被不断调用 → 每次都无等待地立刻解除上传暂停

第三步 — 进度回调只有 1ms 退避src/tool_cb_rea.c):

  if(config->readbusy) {
    if(ulprev == ulnow) {
#ifndef _WIN32
      waitfd(1, per->infd);
#else
      /* sleep */
      curlx_wait_ms(1);
#endif
    }

    config->readbusy = FALSE;
    curl_easy_pause(per->curl, CURLPAUSE_CONT);
  }

tool_readbusy_cb 有 1ms 的 waitfd 退避,但被 tool_write_cb 的无等待解除抢先了。

完整的死循环(每次 curl_multi_perform 内)

lib/transfer.cCurl_sendrecv 中,处理顺序是先下载后上传

  if(k->keepon & KEEP_RECV) {
    result = sendrecv_dl(data, k);
    if(result || data->req.done)
      goto out;
  }

  /* If we still have writing to do, we check if we have a writable socket. */
  if(Curl_req_want_send(data)) {
    result = sendrecv_ul(data);
    if(result)
      goto out;
  }

所以单次迭代内:
1. sendrecv_dl → 读到 streaming 数据 → tool_write_cb → 看到 readbusy=TRUE立刻解除暂停(零等待)
2. sendrecv_ultool_read_cb → stdin EAGAIN又暂停readbusy=TRUE
3. 回到 curl_multi_poll → 下载 socket 有数据 → 立刻返回(不会 sleep)
4. 重复 1-3

lib/easy.c 的主循环:

  while(!done && !mresult) {
    int still_running = 0;

    mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL);

    if(!mresult)
      mresult = curl_multi_perform(multi, &still_running);

curl_multi_poll 的 1000ms 超时形同虚设——因为下载 socket 始终可读,poll() 立刻返回。

结果:每秒跑上千次这个循环,每次包含多个系统调用(pollreadwrite),CPU 直接打满。

问题本质

stdin 没有被加入 curl_multi_poll 的 pollset。只有网络 socket 参与了 poll。所以 curl 无法高效地"同时等待 stdin 和网络数据",只能靠 tool_write_cb 里的盲目 unpause 来弥补,在 streaming 场景下退化成忙等。

用户侧解决方案

如果你不需要从 stdin 上传数据(只是想看 streaming 响应),不要用 -T.

# 正确的方式:只看 streaming 输出
curl -N https://streaming-server/events

# 或者重定向 stdin 为空
curl -NT. https://streaming-server/events < /dev/null

< /dev/null 会让 read() 返回 0(EOF)而不是 EAGAIN,不会进入 pause/unpause 循环。

如果确实需要双向 streaming(边上传 stdin 边接收),目前没有完美的用户侧 workaround。可以考虑:

# 用管道加一层缓冲,减轻空转(效果有限)
cat | curl -NT. https://streaming-server/events

源码级修复思路

如果要在源码层面修复,最直接的方案是tool_write_cb 中去掉盲目 unpause,改为只在 tool_readbusy_cb 中带退避地 unpause,并增大退避时间:

// tool_cb_wrt.c — 去掉这段,或加条件限制
// 不再从 write callback 无脑解除上传暂停
if(config->readbusy) {
    config->readbusy = FALSE;
    curl_easy_pause(per->curl, CURLPAUSE_CONT);  // 删除或改为有条件触发
}
// tool_cb_rea.c — 增大退避时间
if(config->readbusy) {
    if(ulprev == ulnow) {
        waitfd(100, per->infd);  // 从 1ms 提高到 100ms
    }
    config->readbusy = FALSE;
    curl_easy_pause(per->curl, CURLPAUSE_CONT);
}

更彻底的方案是把 stdin fd 加入 curl_multi_poll 的 extra fds,但这需要改 curl_multi_poll 的调用方式,涉及面更大。