当前位置:首页 > 文章列表 > 文章 > linux > Linux getrandom 设置 GRND_NONBLOCK 后为什么返回 EAGAIN

Linux getrandom 设置 GRND_NONBLOCK 后为什么返回 EAGAIN

来源:17golang原创 2026-11-15 00:50:55 0浏览 收藏

我在 Linux 服务启动阶段遇到 getrandom() 返回 EAGAIN 时,第一反应也曾是“随机数接口是不是坏了”。但如果调用参数里有 GRND_NONBLOCK,这个结果通常正是参数承诺的行为:随机源还没准备好时,不等待,立即返回。

官方文档:https://man7.org/linux/man-pages/man2/getrandom.2.html

默认从 urandom 源读取时,如果随机池尚未初始化,getrandom 会等待;加入 GRND_NONBLOCK 后则返回 -1,并把 errno 设为 EAGAIN。处理重点不是把 EAGAIN 当成参数错误,而是决定启动流程要延后、有限重试,还是允许系统调用阻塞。

GRND_NONBLOCK 为什么会把等待变成 EAGAIN

getrandom() 默认从 urandom 源获取随机字节。这里有一个启动时序:内核随机池尚未初始化时,普通调用会阻塞;如果 flags 包含 GRND_NONBLOCK,调用者明确告诉内核“不要在这里等”,于是内核用 EAGAIN 表示暂时没有可立即返回的数据。

getrandom 默认 urandom 源、随机池状态与 GRND_NONBLOCK 返回 EAGAIN 的静态关系图
图1:getrandom 随机源状态与 GRND_NONBLOCK 返回分支图;这是静态说明图,不是终端截图或运行证据。

因此下面两种写法的取舍不同:

/* 允许内核等待随机池就绪;适合不要求启动路径非阻塞的初始化。 */
ssize_t n = getrandom(buf, sizeof buf, 0);

/* 明确不等待;随机池未就绪时要检查 errno == EAGAIN。 */
ssize_t n = getrandom(buf, sizeof buf, GRND_NONBLOCK);

第二种写法并不保证“永远更快”。它只是把等待责任交还给应用:应用可以延后初始化、安排下一次事件,或者做有上限的重试。若把它写成无间隔死循环,反而会在启动早期持续占用 CPU。

先判断返回值,再判断 errno

调用约定很简单,但顺序不能反:返回值为 -1 时才读取 errno;返回非负值时,返回值本身表示已经复制到缓冲区的字节数。对于小于或等于 256 字节、且使用默认 urandom 源的读取,文档给出了“初始化完成后通常一次返回请求字节数”的保证,但代码仍应检查返回值。

#include 
#include 
#include 

int read_seed(unsigned char *buf, size_t size) {
    /* 非阻塞只改变“未就绪时怎么返回”,不会改变错误检查规则。 */
    ssize_t n = getrandom(buf, size, GRND_NONBLOCK);
    if (n == (ssize_t)size) {
        /* 读取完整,调用者可以继续初始化。 */
        return 0;
    }
    if (n == -1 && errno == EAGAIN) {
        /* 随机池暂未就绪:这是可延后的状态,不是参数错误。 */
        return 1;
    }
    if (n == -1 && errno == EINTR) {
        /* 被信号打断,是否重试要由上层策略决定。 */
        return 2;
    }
    /* 其他错误或短读都不能假装成完整种子。 */
    return 3;
}

这里返回 1、2、3 是给示例调用者区分状态的本地约定,不是 Linux 的 errno 编号。尤其不要把 n >= 0 直接等价于“成功”:如果请求较大,或者使用了 GRND_RANDOM,返回字节数可能小于请求值。

启动阶段怎样设计有限重试

如果服务必须尽快完成启动,又需要随机种子,可以只对 EAGAIN 做短暂、有限的等待。下面示例使用 256 字节缓冲区,并把重试次数写成显式上限;它是策略示例,不代表某个发行版的最佳时间参数。

启动阶段 getrandom 小缓冲区、有限重试与 EAGAIN 终止边界的静态关系图
图2:启动阶段的 getrandom 返回处理边界图;这是静态说明图,不代表真实执行结果。
#include 
#include 
#include 

int fill_seed(unsigned char seed[32]) {
    const int max_attempts = 5;  /* 上限防止启动路径无限忙等。 */
    for (int attempt = 0; attempt 

这个策略有三个关键点:第一,只有明确识别出的暂态情况才重试;第二,重试有总次数上限;第三,耗尽后必须把失败交给上层,而不是用时间戳、固定字符串等不等价的数据替代。对于对启动延迟不敏感的程序,直接不加 GRND_NONBLOCK 可能更简单。

256 字节、GRND_RANDOM 和短读边界

不要把“getrandom 返回 EAGAIN”与所有随机源情况混为一谈:

场景需要关注的结果处理重点
默认 urandom,随机池未初始化,带 GRND_NONBLOCK-1,errno=EAGAIN延后或有限重试
默认 urandom,读取不超过 256 字节且已初始化通常返回完整请求仍检查返回值
指定 GRND_RANDOM,随机源暂时没有足够字节可能短读或 EAGAIN不能假设一次填满
大于 256 字节的请求被信号打断可能短读或 EINTR按实际 n 和 errno 处理

在普通服务初始化中,我更倾向于请求较小的缓冲区,并让初始化函数明确返回“完成、暂时不可用、永久失败”三类状态。这样调用方能决定是稍后重试,还是让服务管理器重新安排启动,而不是把内核的时序信号吞掉。

排查 EAGAIN 时的一套判断顺序

  1. 确认 flags 是否真的包含 GRND_NONBLOCK,不要先去怀疑随机设备文件。
  2. 只在返回值为 -1 时读取 errno,确认是否为 EAGAIN。
  3. 确认请求使用的是默认 urandom 源还是 GRND_RANDOM,两者的短读和等待语义不同。
  4. 检查请求大小,优先把启动阶段的种子读取控制在 256 字节以内。
  5. 为 EAGAIN 设计有限重试或异步延后,为其他错误保留明确失败路径。

几个容易混淆的问题

EAGAIN 是不是说明 getrandom 参数非法?

不是。非法 flags 通常对应 EINVAL;EAGAIN 表示如果不使用非阻塞标志,调用本来会等待,但当前选择了立即返回。

把 GRND_NONBLOCK 去掉就一定更好么?

不一定。它会让调用可能阻塞,启动路径是否允许等待取决于服务的时延目标、依赖关系和故障处理方式。关键是明确选择,而不是盲目替换参数。

收到 EAGAIN 能不能马上 while 重试?

可以实现,但不建议无间隔无限重试。应使用短暂等待、次数或截止时间上限,并在耗尽后交给上层决定延后启动还是失败退出。

GRND_NONBLOCK 的真正含义,是把“随机源何时可用”从隐式阻塞变成了应用可观察的状态。理解这一点后,EAGAIN 就不再是神秘错误,而是启动流程需要认真处理的一条分支。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Linux io_uring SQE 怎样组织异步操作Linux io_uring SQE 怎样组织异步操作
上一篇
Linux io_uring SQE 怎样组织异步操作
Web ReadableStream 怎样取消未读响应
下一篇
Web ReadableStream 怎样取消未读响应
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    1029次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    1030次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1173次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1134次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    974次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码