Linux 基础错误码(errno 1–34)技术参考

发布时间:2026/7/23 6:11:23

Linux 基础错误码(errno 1–34)技术参考 本文档说明include/uapi/asm-generic/errno-base.h中定义的 34 个基础错误码的用途、语义边界与常见使用场景。这些是所有架构共享的 POSIX 通用错误码构成内核态与用户态之间最基础的错误契约。约定内核函数通过返回负值如return -EINVAL;传递错误用户态系统调用失败时返回-1并把正值写入全局errno。二者是同一套编号只是符号相反。速查表编号宏含义一句话定位1EPERMOperation not permitted有权访问对象但操作本身被策略禁止≠ 无权限访问文件2ENOENTNo such file or directory路径 / 条目不存在3ESRCHNo such process目标 PID / 线程不存在4EINTRInterrupted system call阻塞调用被信号打断5EIOI/O error底层设备 I/O 失败6ENXIONo such device or address设备/地址不存在或未就绪7E2BIGArgument list too long参数/数据超出上限8ENOEXECExec format error可执行文件格式非法9EBADFBad file numberfd 无效或模式不匹配10ECHILDNo child processes无可等待的子进程11EAGAINTry again资源暂不可用稍后重试EWOULDBLOCK12ENOMEMOut of memory内存分配失败13EACCESPermission denied权限检查不通过访问许可14EFAULTBad address用户指针非法 / 拷贝越界15ENOTBLKBlock device required需要块设备但给了非块设备16EBUSYDevice or resource busy资源被占用 / 正在使用17EEXISTFile exists目标已存在18EXDEVCross-device link跨设备操作不允许19ENODEVNo such device设备不存在 / 操作不支持该设备20ENOTDIRNot a directory期望目录却是非目录21EISDIRIs a directory期望文件却是目录22EINVALInvalid argument参数非法最常用的“兜底”错误23ENFILEFile table overflow系统级打开文件数耗尽24EMFILEToo many open files进程级 fd 耗尽25ENOTTYNot a typewriterioctl 命令不适用于该 fd26ETXTBSYText file busy正在执行的镜像被写27EFBIGFile too large超过文件大小上限28ENOSPCNo space left on device设备空间/资源耗尽29ESPIPEIllegal seek对管道/socket 做 seek30EROFSRead-only file system只读文件系统上写操作31EMLINKToo many links硬链接数超限32EPIPEBroken pipe对端已关闭的管道/socket 写33EDOMMath argument out of domain数学函数定义域错误34ERANGEMath result not representable结果溢出/超出可表示范围1. 按主题分类详解1.1 权限与策略EPERMvsEACCES这是最容易混淆的一对EACCES13访问许可检查失败。你对这个对象没有资格——文件权限位不允许、SELinux 拒绝、目录不可搜索等。EPERM1操作本身在语义上被禁止通常与是否 root、是否持有 capability 有关而不是对象的访问位。例如非特权用户kill()不属于自己的进程、非CAP_SYS_ADMIN调用某些 ioctl。驱动 / DRM 场景if(!capable(CAP_SYS_ADMIN))return-EPERM;/* 特权操作被拒 */if(!drm_is_current_master(file))/* 非 master 不能改模式 */return-EACCES;1.2 参数校验EINVAL、EFAULT、E2BIG、ERANGE内核入口ioctl / syscall的第一道防线EINVAL22参数组合非法、标志位含未知位、枚举越界、对齐不满足。最常用的兜底错误。EFAULT14用户态指针非法。copy_from_user/copy_to_user返回非零时几乎总是转成-EFAULT。E2BIG7请求的数量/大小超过接口约定上限如批量提交条目数过多。ERANGE34/EDOM33多用于数值/数学语义ERANGE也常表示“缓冲区放不下结果”。if(args-flags~VALID_FLAGS)return-EINVAL;if(copy_from_user(k,uptr,sizeof(k)))return-EFAULT;if(args-countMAX_ENTRIES)return-E2BIG;1.3 设备与资源状态ENODEV、ENXIO、EBUSY、ENOMEM驱动开发amdgpu / DRM核心错误族ENODEV19设备不存在或该操作在此设备上不受支持。常用于 feature 未实现 / 硬件不具备能力。ENXIO6设备或地址不存在 / 未就绪。probe 阶段拿不到资源、MMIO 地址无效常用它。EBUSY16资源正被占用。BO 被 pin、显存区间被其他上下文持有、设备正在复位。ENOMEM12kmalloc/ 页分配 / dma 分配失败显存或系统内存耗尽。SVM / 迁移场景中的典型用法pagemigrate_pfn_to_page(migrate.src[i]);if(!page)continue;/* 空洞跳过 */if(!amdgpu_vram_mgr_new(...))return-ENOMEM;/* VRAM 分配失败 */if(kfd_process_device_busy(pdd))return-EBUSY;/* 设备忙稍后重试或回退 */1.4 可重试语义EAGAIN与EINTR对内核并发/迁移路径极其重要务必与“真失败”区分EAGAIN11等于EWOULDBLOCK暂时性失败调用方应重试。非阻塞 I/O 无数据、锁抢占失败、迁移过程中页状态发生变化需要重走。EINTR4阻塞调用在完成前被信号打断通常需要重启系统调用或向上传播让用户态重试。DRM/SVM 中EAGAIN常被用作“流程需要重来”的控制流信号而非真正错误if(!migrate_vma_setup(migrate))...;if(migrate.cpages!npages)return-EAGAIN;/* 部分页未能隔离让上层重试整个迁移 */if(dma_fence_wait_interruptible(fence))return-EINTR;注意-ERESTARTSYS属于内核内部区间512在返回用户态前会被转换为EINTR用于自动重启系统调用。二者概念相关但不同层。1.5 文件描述符与 ioctlEBADF、ENOTTY、EMFILE/ENFILEEBADF9fd 无效或用错误模式访问如对只读 fd 写。ENOTTY25该 fd 不支持这个 ioctl 命令。DRM 驱动对未识别的DRM_IOCTL_*常返回它。EMFILE24/ENFILE23分别是进程级RLIMIT_NOFILE和系统级的 fd 耗尽。1.6 文件系统语义ENOENT、EEXIST、ENOTDIR、EISDIR、ENOSPC等ENOENT2路径不存在——也广泛用于“查找某个键/条目失败”。EEXIST17O_CREAT|O_EXCL时目标已存在创建重复对象。ENOTDIR20/EISDIR21类型不匹配的一对。ENOSPC28空间耗尽也用于“某种表 / 槽位满了”。EROFS30/ETXTBSY26/EFBIG27/EMLINK31文件系统特定约束。1.7 管道与进程EPIPE、ESRCH、ECHILD、ESPIPEEPIPE32向已关闭读端的管道/socket 写同时触发SIGPIPE。ESRCH3目标进程/线程不存在kill、ptrace、sched_setaffinity。ECHILD10wait()时没有可等待的子进程。ESPIPE29对不可 seek 的对象管道/FIFO/socket调用lseek。2. 内核编码惯例返回负值return -EINVAL;成功返回0或正的有效值如已处理字节数。错误指针用ERR_PTR(-ENOMEM)编码IS_ERR()判定PTR_ERR()取出。boamdgpu_bo_create(...);if(IS_ERR(bo))returnPTR_ERR(bo);错误传播优先保留下层返回的 errno不要无脑改成-EINVAL以免丢失语义尤其-EAGAIN/-EINTR/-ENOMEM必须原样传播。goto清理链分配失败时按逆序释放返回对应 errno。不要用 errno 数值判断底层原因EAGAIN EWOULDBLOCKEDEADLK EDEADLOCK在部分架构相等用宏名而非数字。3. 用户态排查if(ioctl(fd,DRM_IOCTL_XXX,arg)0)fprintf(stderr,ioctl failed: %s\n,strerror(errno));命令行errno22# EINVAL Invalid argument moreutils 提供的 errno 工具errno-l# 列出全部perror...# 或用 strace 观察系统调用返回的 errno具体实战场景下面每个场景给出触发条件 → 现象 → 定位方法 → 处理方式均取自内核 / DRM / amdgpu SVM 常见问题。场景 1ioctl 返回-EINVAL但参数“看起来没问题”触发条件用户态填了一个内核尚未识别的 flag 位或结构体reserved字段非零。现象ioctl(...)返回-1strerror(errno)显示Invalid argument。定位strace-etraceioctl-f./my_test21|grep-iEINVAL内核侧常见校验if(args-flags~AMDGPU_VM_VALID_FLAGS)/* 有未知位 */return-EINVAL;if(args-_pad)/* 保留字段必须清零 */return-EINVAL;处理用户态memset(arg, 0, sizeof(arg))后再填字段确认内核版本支持该 flag。这是新旧 UAPI 不匹配最典型的表现。场景 2迁移路径反复返回-EAGAIN迁移“卡住不前进”触发条件migrate_vma_setup()后cpages ! npages——部分页正被其他 CPU/GPU 访问无法一次性隔离。现象SVM range 迁移函数被上层不断重调dmesg里没有真错误但吞吐上不去。定位migrate_vma_setup(migrate);if(migrate.cpages!migrate.npages){pr_debug(only isolated %lu/%lu pages\n,migrate.cpages,migrate.npages);ret-EAGAIN;/* 让上层重试整个 range */}处理这是正常的控制流不是 bug。但若无限重试需检查是否有页被长期 pin例如用户态持有 DMA-BUF、O_DIRECT I/O 在途否则会活锁。区分“可重试”与“死循环”的关键是加重试次数上限 backoff。场景 3分配显存得到-ENOMEM而free显示还有余量触发条件VRAM碎片化或被 pin 的 BO 占据关键区间连续大页分配失败或达到了 per-process 显存配额。现象amdgpu_bo_create()/amdgpu_vram_mgr_new()返回-ENOMEM但rocm-smi --showmeminfo vram显示总量未满。定位cat/sys/kernel/debug/dri/0/amdgpu_vram_mm# 查看 VRAM 分配器空洞分布dmesg|grep-iamdgpu.*out of.*memory处理改用可回退到 GTT 的分配标志触发 TTM 驱逐让出连续空间或缩小单次分配粒度。SVM 场景下可回退到系统内存后再异步迁移。场景 4设备复位期间所有提交返回-EBUSY/-ENODEV触发条件GPU 挂起后进入 recoveryGPU reset驱动临时把设备标记为不可用。现象先是-EBUSY资源忙reset 过程中或失败后变为-ENODEV设备消失。定位dmesg|grep-iEGPU reset|ring.*timeout|amdgpu.*hangcat/sys/kernel/debug/dri/0/amdgpu_gpu_recover处理用户态应捕获-ENODEV视为“设备需重新初始化”而非立即退出-EBUSY通常可短暂 backoff 后重试。驱动侧用amdgpu_in_reset()提前拦截提交并返回-EAGAIN让上层重排队。场景 5copy_from_user失败 →-EFAULT触发条件用户传入的地址范围部分不可读/不可写或结构体大小与内核期望不一致导致越界拷贝。现象ioctl 返回-EFAULT严重时伴随用户态 SIGSEGV。定位if(copy_from_user(kdata,u64_to_user_ptr(args-ptr),args-size)){pr_debug(bad user ptr %llx size %u\n,args-ptr,args-size);return-EFAULT;}配合strace看传入指针或用dmesg中的pr_debug需echo -n file amdgpu_xxx.c p /sys/kernel/debug/dynamic_debug/control。处理确认用户态缓冲区已分配且大小正确SVM 场景注意 range 边界对齐到页避免尾页越界。场景 6对已识别 fd 发 ioctl 却得-ENOTTY触发条件ioctl 命令号不属于该驱动或打开的是 render node 却发了只有 primary node 支持的命令反之亦然。现象Inappropriate ioctl for device。定位核对命令号是否用对了DRM_IOCTL_*宏确认打开的是/dev/dri/card0还是/dev/dri/renderD128。处理DRM 对未知 ioctl 默认返回-ENOTTY部分路径是-EINVAL。选对 node或确认驱动是否注册了该 ioctl。场景 7等待 fence 时被信号打断 →-EINTR/-ERESTARTSYS触发条件dma_fence_wait_interruptible()或 TTM 可中断驱逐等待期间进程收到信号如 Ctrl-C、SIGTERM。现象内核内部返回-ERESTARTSYS进入用户态被转为-EINTR。定位retdma_fence_wait_interruptible(fence);if(ret)returnret;/* 常为 -ERESTARTSYS勿改写成 -EINVAL */处理必须原样向上传播让系统调用自动重启或用户态重试。若被错误地转成其他 errno会导致信号无法及时响应或误报失败。场景 8文件系统类操作在容器/只读根下失败触发条件在只读挂载点写文件-EROFS、O_CREAT|O_EXCL目标已存在-EEXIST、路径中间是文件而非目录-ENOTDIR。现象常见于测试脚本在容器里创建 debugfs/tmp 文件失败。定位mount | grep ro、ls -ld检查路径每一段类型。处理换可写路径/tmp、tmpfs创建前stat判存在性确保父目录确为目录。场景 9目标进程/线程已消失 →-ESRCH语义锚点ESRCH的本义是“没有这个进程”因此它天然绑在task 查找失败上而不是 mm 引用计数归零。区分两条来源很重要task 查找为空 →-ESRCH标准用法find_get_task_by_vpid()/pid_task()返回 NULL。mm/migrate.c的find_mm_struct()task 查不到即return ERR_PTR(-ESRCH);move_pages系统调用。mm/process_vm_access.cfind_get_task_by_vpid()为空 →rc -ESRCH;process_vm_readv/writev。task 在、但没有 mm →-ESRCHkernel/fork.c的mm_access()mmget_task_mm(task);if(!mm)mmERR_PTR(-ESRCH);/* 进程已退出或是内核线程无地址空间 */elseif(!may_access_mm(mm,task,mode))mmput(mm),mmERR_PTR(-EACCES);触发条件对一个已退出或从未存在的 PID/TID 操作 —— 用户态kill(pid, sig)、sched_setaffinity(pid, ...)、ptrace(..., pid, ...)、读/proc/pid/...时进程刚好退出或内核异步路径用保存的pid回查进程restore worker、fault handler而进程已被销毁。现象系统调用返回-1且errno ESRCHNo such process内核路径返回-ESRCH异步 worker 直接放弃本次处理。定位taskfind_get_task_by_vpid(pid);if(!task){pr_debug(process %d already gone\n,pid);return-ESRCH;/* 进程已退出放弃本次处理 */}用户态复现与观察strace-etracekill,ptrace,sched_setaffinity ./tool21|grepESRCH竞态确认在kill与目标退出之间存在 TOCTOU 窗口——kill(pid,0)探活成功后真正操作时进程已死。处理用户态把-ESRCH当作“进程正常结束”的非致命情况处理而不是报错退出尤其监控/清理类工具。内核异步路径拿到-ESRCH应静默放弃并释放已占资源不要重试、不要打印 error 级日志进程退出是常态。用get_task_struct()持有 task 引用避免中途释放查不到 task 才返回-ESRCH。场景快速索引症状最可能的 errno首选排查动作ioctl 参数报错但看着正常EINVAL清零结构体、核对 flag 与内核版本迁移不前进但无报错EAGAIN检查页是否被 pin / 加重试上限分配失败但显存有余ENOMEM看 VRAM 碎片 / 配额 / 回退 GTT提交全部失败EBUSY→ENODEV查 GPU reset 日志ioctl 返回 Bad addressEFAULTstrace 看用户指针、核对 sizeInappropriate ioctlENOTTY选对 DRM node / 命令号等待被打断EINTR/ERESTARTSYS原样传播勿改写写文件失败EROFS/EEXIST/ENOTDIR检查挂载与路径类型

相关新闻