news 2026/9/10 1:46:52

SerenityOS uname(2) 系统调用指南:从 utsname 结构到内核实现的完整解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SerenityOS uname(2) 系统调用指南:从 utsname 结构到内核实现的完整解析

SerenityOS uname(2) 系统调用指南:从 utsname 结构到内核实现的完整解析

【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity

本文以 SerenityOS 的手册页uname(2)为核心,讲解uname系统调用如何检索当前内核信息:utsname结构体的五个字段含义、接口原型、返回值与错误语义,并结合内核源码 uname.cpp 与 C 库封装 utsname.cpp 说明每个字段的实际填充来源。读完后可掌握如何在 SerenityOS 上获取系统名、主机名、发行版本、修订号与机器架构,并理解各字段背后的内核实现机制。

接口原型与头文件

uname系统调用用于检索当前内核的信息,并将其写入调用者提供的utsname结构体。手册页给出的原型如下(见 uname(2) 手册页):

#include <sys/utsname.h> int uname(struct utsname* buf);

调用时需将utsname结构体的用户态指针传入buf;该结构体由内核负责填充。

utsname 结构体:字段定义与长度上限

手册页中给出的结构体简写为五个变长成员:

struct utsname { char sysname[]; char nodename[]; char release[]; char version[]; char machine[]; };

实际实现中各字段为定长数组,长度上限由UTSNAME_ENTRY_LEN宏指定。查阅 内核 POSIX 头文件 可以看到完整定义:

#define UTSNAME_ENTRY_LEN 65 struct utsname { char sysname[UTSNAME_ENTRY_LEN]; char nodename[UTSNAME_ENTRY_LEN]; char release[UTSNAME_ENTRY_LEN]; char version[UTSNAME_ENTRY_LEN]; char machine[UTSNAME_ENTRY_LEN]; };

即每个字段最多容纳 65 字节(含结尾'\0',实际内容上限 64 字符)。该头文件通过 LibC 的头文件 sys/utsname.h 暴露给用户态程序,其中声明了int uname(struct utsname*);函数。

五个字段的语义为:

字段含义
sysname操作系统名称(SerenityOS 固定为SerenityOS
nodename节点名,即当前主机名(hostname)
release发行版本号(如1.0-dev形式的主/次版本)
version构建修订信息(源码版本标识)
machine机器架构类型(按编译目标架构而定)

返回值与错误语义

  • 成功时返回0
  • 失败时返回-1,并通过errno描述错误原因。

手册页列出的错误条件为:

  • EFAULTbuf不是一个可写入的用户态地址(即内核无法将结果拷贝回用户内存)。

由于utsname的填充全部由内核内部完成,正常情况下调用几乎不会失败;EFAULT是唯一的典型错误路径,对应参数指针非法或不可写的场景。

内核侧实现:各字段是如何填充的

uname系统调用的内核实现位于 Kernel/Syscalls/uname.cpp,入口函数为Process::sys$uname。其执行流程与字段填充逻辑如下:

前置条件:Pledge 检查

TRY(require_promise(Pledge::stdio));

从源码可以看到,SerenityOS 对uname系统调用施加了 Pledge 安全机制 约束:调用进程必须持有stdio权限承诺,否则系统调用会被拒绝。这是 SerenityOS 区别于传统 Unix 的一个特点——即便是查询类系统调用也需要显式承诺。

machine:由编译目标架构静态决定

machine字段在编译期通过预处理器确定,支持三种目标架构:

#if ARCH(X86_64) # define UNAME_MACHINE "x86_64" #elif ARCH(AARCH64) # define UNAME_MACHINE "aarch64" #elif ARCH(RISCV64) # define UNAME_MACHINE "riscv64" #else # error Unknown architecture #endif

uname -m的结果直接对应内核构建时选择的架构,未知架构会在编译期报错。

release 与 version:版本字符串的组装

releaseversion字段分别来自两处:

  1. release:来自全局变量g_version_string。它在 Process.cpp 的启动初始化阶段被创建:

    g_version_string = MUST(KString::formatted("{}.{}-dev", SERENITY_MAJOR_REVISION, SERENITY_MINOR_REVISION)).leak_ptr();

    从源码结构看,release 字段呈现为<主版本>.<次版本>-dev的形式(如1.0-dev),其中版本号由构建系统注入的宏提供。

  2. version:来自SERENITY_VERSION常量(对应 generate-version-file.sh 生成的版本信息),通常携带 git 提交哈希等构建修订信息。

内核在填充时对两者都做了长度截断保护:

AK::TypedTransfer<u8>::copy(reinterpret_cast<u8*>(buf.release), g_version_string->bytes().data(), min(g_version_string->length(), UTSNAME_ENTRY_LEN - 1));

即无论版本字符串多长,最多只写入UTSNAME_ENTRY_LEN - 1个字节,保证不会溢出定长数组。

nodename:来自进程附着的主机名上下文

nodename(主机名)不是硬编码值,而是在调用时从当前进程附着的主机名上下文动态读取:

m_attached_hostname_context.with(& { hostname_context_ptr->buffer().with(& { auto name_length = name_buffer.representable_view().length(); VERIFY(name_length <= (UTSNAME_ENTRY_LEN - 1)); AK::TypedTransfer<char>::copy(reinterpret_cast<char*>(buf.nodename), name_buffer.representable_view().characters_without_null_termination(), name_length); buf.nodename[name_length] = '\0'; }); });

从源码结构看,主机名以内核侧的共享上下文对象维护,uname调用时实时读取其当前值并显式补上结尾'\0';主机名在用户态被修改后,后续uname的结果会随之变化(修改主机名的命令行工具见 hostname(8) 手册页)。

结果写回用户空间

填充完成后通过一次用户态拷贝返回:

TRY(copy_to_user(user_buf, &buf)); return 0;

copy_to_user失败(例如buf指向非法地址)正是手册页中EFAULT错误的来源。系统调用在 Process.h 中的声明为:

ErrorOr<FlatPtr> sys$uname(Userspace<utsname*>);

C 库封装:用户态如何发起调用

用户态的uname()函数由 LibC 提供,实现见 Userland/Libraries/LibC/utsname.cpp:

int uname(struct utsname* buf) { int rc = syscall(SC_uname, buf); __RETURN_WITH_ERRNO(rc, rc, -1); }

逻辑很直接:通过syscall(SC_uname, buf)发起系统调用,__RETURN_WITH_ERRNO宏负责在返回值为负(错误码)时设置errno并返回-1,成功时原样返回0。因此应用开发者只需#include <sys/utsname.h>并调用uname(),无需关心底层系统调用号。

配套命令:uname(1) 的选项与示例

系统调用之上的命令行工具uname用于打印内核信息,其手册页见 Base/usr/share/man/man1/uname.md。用法为:

$ uname [-s] [-n] [-r] [-v] [-m] [-a]

各选项对应utsname的字段:

选项作用对应字段
-s打印系统名称sysname
-n打印节点名(主机名)nodename
-r打印系统发行版本release
-v打印发行版本的具体修订版本version
-m打印机器类型machine
-a打印以上全部信息全部

手册页给出的示例输出:

$ uname -sm Serenity x86_64

可见-s打印系统名、-m打印架构,与内核侧UNAME_MACHINE的定义一一对应。

小结与验证方式

综合手册页与源码,SerenityOS 的uname(2)调用链路为:应用调用 LibCuname()SC_uname系统调用 →Process::sys$uname(Kernel/Syscalls/uname.cpp)→ Pledge 检查、组装utsname(sysname 固定、machine 按架构、release/version 来自构建版本、nodename 实时读自主机名上下文)→copy_to_user写回并返回 0

在 SerenityOS 终端中,可通过uname -a验证全部字段的实际值;若修改主机名后再次调用uname -n,输出应同步变化,这正是内核侧动态读取主机名上下文的直接体现。

【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 1:46:24

PowerFactory风储联合系统蓄电池建模全流程实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 1:46:19

STM32+OV5642二维码识别实战:从硬件驱动到Quirc解码

简介&#xff1a;STM32单片机结合OV5642摄像头实现二维码识别的完整Keil工程包&#xff0c;面向嵌入式图像识别学习者和开发者&#xff0c;解决在MCU平台上完成图像采集、二维码解码与显示的实际需求。包内共316个文件&#xff0c;压缩后约12.05MB&#xff0c;以80个头文件、67…

作者头像 李华
网站建设 2026/9/10 1:45:34

LabVIEW数据XML序列化实战指南:从原理到工程落地

在LabVIEW项目里折腾数据交换的时候&#xff0c;很多人迟早会撞上“XML序列化”这个词。早年间我一度觉得&#xff0c;LabVIEW天生是给测控系统用的&#xff0c;跟XML这种文本标记语言八竿子打不着。直到有一天&#xff0c;一个项目要求把采集到的设备参数、标定数据导出给外部…

作者头像 李华
网站建设 2026/9/10 1:43:53

基于Python的热门游戏推荐系统设计与实现:从算法到部署全解析

1. 项目概述&#xff1a;这个系统到底解决什么问题如果你打开过任一家游戏平台的首页&#xff0c;比如Steam、Epic或者WeGame&#xff0c;会发现它们都有一个模块叫“为你推荐”或者“猜你喜欢”。这个模块背后跑的就是一套推荐系统。而“基于Python的热门游戏推荐系统的设计与…

作者头像 李华
网站建设 2026/9/10 1:43:42

MATLAB中的FFT滤波:从频谱分析到频域滤波实战指南

先说个实际场景。我以前做传感器数据采集的时候&#xff0c;被50Hz工频干扰搞得焦头烂额&#xff0c;时域波形上那个毛刺怎么滤都滤不干净&#xff0c;FIR滤波器阶数加高了几十倍&#xff0c;延迟大得像慢动作&#xff0c;结果还不好。后来换了个思路&#xff0c;先把信号做FFT…

作者头像 李华