当前位置:首页>鸿蒙APP>鸿蒙6文件系统的"物业管家"

鸿蒙6文件系统的"物业管家"

  • 2026-09-04 15:21:08
鸿蒙6文件系统的"物业管家"
在OpenHarmony6.1源码分析的init模块分析中,已经分析过 ueventd,在 base/startup/init/ueventd/ueventd_main.c 中,今天我们来拆解一下这个进程。
ueventd 是 OpenHarmony 启动子系统里的设备节点守护进程,主要职责是监听 Linux 内核发出的 uevent 事件,然后在用户空间创建、删除、修复 /dev 下的设备节点,并设置对应的权限、属主、SELinux 标签。

带你装杯带你废,带你霍霍OpenHarmony~ 大家好我是控哥,本期给大家带来的是:文件系统的"物业管家":ueventd。

欢迎大家点赞关注+转发,感谢您的支持!

一、角色定位

如果把系统启动比作装修:
内核= 施工队,把硬件设备识别出来,然后对外喊"我发现了 xx 设备,主从设备号是 yy"。
ueventd= 物业管家,听到喊声后去 /dev 目录下创建对应的门牌号(设备节点),并配好钥匙权限(mode/uid/gid/SELinux)。
init= 总包,第一阶段(init_early)会先把 ueventd 拉起来处理冷启动设备,后续继续完成挂载、服务启动等。
源码入口:
base/startup/init/ueventd/ueventd_main.c
base/startup/init/ueventd/ueventd.c
base/startup/init/ueventd/ueventd_device_handler.c

二、核心职责

从 base/startup/init/ueventd/ueventd_main.c 的 main 函数可以看到它有四种工作模式:
#defineUEVENTD_MODE_DEAMON     0#defineUEVENTD_MODE_EARLY_BOOT 1#defineUEVENTD_MODE_RETRIGGER  2#defineUEVENTD_MODE_LISTEN     3
对应职责:

模式

作用

daemon(默认)

长期守护,监听内核 uevent socket,处理热插拔

early boot

在启动极早期创建少量必须设备节点

retrigger

重新触发并处理所有已产生的 uevent,常用于冷启动补建节点

listen

只监听不 retrigger

base/startup/init/ueventd/ueventd.c 中的 HandleUevent 是事件分发的核心:
STATIC voidHandleUevent(conststruct Uevent *uevent){    if (uevent->action == ACTION_ADD || uevent->action == ACTION_CHANGE || uevent->action == ACTION_ONLINE) {        ChangeSysAttributePermissions(uevent->syspath);    }    SUBSYSTEMTYPE type = GetSubsystemType(uevent->subsystem);    switch (type) {        case SUBSYSTEM_BLOCK:            HandleBlockDeviceEvent(uevent);            break;        case SUBSYSTEM_FIRMWARE:            HandleFimwareDeviceEvent(uevent);            break;        case SUBSYSTEM_OTHERS:            HandleOtherDeviceEvent(uevent);            break;        default:            break;    }}
ueventd 支持的 action 包括:
static const char *actions[] = {    [ACTION_ADD] = "add",    [ACTION_REMOVE] = "remove",    [ACTION_CHANGE] = "change",    [ACTION_MOVE] = "move",    [ACTION_ONLINE] = "online",    [ACTION_OFFLINE] = "offline",    [ACTION_BIND] = "bind",    [ACTION_UNBIND] = "unbind",    [ACTION_UNKNOWN] = "unknown",};


三、举个例子:插入一个 U 盘 / 块设备

这是最常见的场景,也是 ueventd 存在的典型意义。
1. 内核发现设备并发送 uevent
当 USB 总线枚举到一个 U 盘时,内核会通过 netlink socket 发送一条 uevent 消息,内容类似:
ACTION=addDEVPATH=/devices/platform/soc/xxx/usb1/1-1/1-1:1.0/host0/target0:0:0/0:0:0:0/block/sdaSUBSYSTEM=blockDEVNAME=sdaMAJOR=8MINOR=0PARTN=0
2. ueventd 从 socket 读到这条消息
base/startup/init/ueventd/ueventd_main.c 的主循环:
staticvoidPollUeventdSocketTimeout(int ueventSockFd, bool ondemand){    struct pollfd pfd = {};    pfd.events = POLLIN;    pfd.fd = ueventSockFd;    // ...    while (1) {        ret = poll(&pfd, 1, timeout);        if (pfd.revents & (POLLIN | POLLERR)) {            ProcessUevent(ueventSockFd, NULL, 0, NULL);        }    }}
3. 解析 uevent 字段
base/startup/init/ueventd/ueventd.c 中的 AddUevent 把字符串解析成结构体:
staticvoidAddUevent(struct Uevent *uevent, constchar *event, size_t len){    if (STARTSWITH(event, "DEVPATH=")) {        uevent->syspath = event + strlen("DEVPATH=");    } else if (STARTSWITH(event, "SUBSYSTEM=")) {        uevent->subsystem = event + strlen("SUBSYSTEM=");    } else if (STARTSWITH(event, "ACTION=")) {        uevent->action = GetUeventAction(event + strlen("ACTION="));    } else if (STARTSWITH(event, "DEVNAME=")) {        uevent->deviceName = event + strlen("DEVNAME=");    } else if (STARTSWITH(event, "MAJOR=")) {        uevent->major = StringToInt(event + strlen("MAJOR="), -1);    } else if (STARTSWITH(event, "MINOR=")) {        uevent->minor = StringToInt(event + strlen("MINOR="), -1);    }    // ...}
4. 按子系统分发处理
HandleUevent 判断 SUBSYSTEM=block,调用 HandleBlockDeviceEvent。
base/startup/init/ueventd/ueventd_device_handler.c 里最终会调用 mknod 创建 /dev/block/sda,并参考 ueventd.config 设置权限、uid、gid、SELinux 标签。

四、ueventd 处理块设备的流程图


五、启动阶段:init_early 如何拉起 ueventd

在系统启动第一阶段,init_early 会调用 StartUeventd 来处理 ramdisk 阶段的冷启动设备事件。
源码位置:
base/startup/init/services/init/standard/init_firststage.c
voidSystemPrepare(longlong upTimeInMicroSecs){    // ...    CreateFsAndDeviceNode();    HookMgrExecute(GetBootStageHookMgr(), INIT_FIRST_STAGE, NULL, NULL);    if (InUpdaterMode() != 0) {        return;    }    MountRequiredPartitions();    StartSecondStageInit(upTimeInMicroSecs);}
CreateFsAndDeviceNode 内部会启动 ueventd 处理 coldplug,否则 ramdisk 阶段很多块设备节点(比如 /dev/block/by-name/system)还没创建,无法挂载 system 分区。
ueventd 在 daemon 模式下,启动后还会检查 UEVENTD_FLAG 文件:
if (!listen_only && access(UEVENTD_FLAG, F_OK)) {    INIT_LOGI("Ueventd started, trigger uevent");    RetriggerUevent(ueventSockFd, NULL, 0);    int fd = open(UEVENTD_FLAG, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);    // ...}
如果 flag 文件不存在,说明是冷启动,需要 RetriggerUevent 重新触发并处理所有已产生的 uevent。
如果 flag 文件已存在,说明已经处理过,只进入正常监听。

六、ueventd.config 长什么样

ueventd 通过配置文件知道每个设备节点应该用什么权限、属主、SELinux 标签。
配置文件路径:
base\startup\init\ueventd\etc\ueventd.config
base/startup/init/ueventd/ueventd_read_cfg.c 负责解析。
典型配置格式类似这样:
/dev/block/sda      0660   system     system/dev/ttyUSB*        0660   root       root/dev/input/event*   0660   system     input
<devicename> <mode> <uid> <gid> <parameter>  | 字段        | 说明             |  | ----------- | ---------------- |  | device name | 设备节点名称     |  | mode        | 设备节点所给予的权限,遵循UGO模型|  | uid         | 设备节点的uid |  | gid         | 设备节点的gid |  | parameter   | 设备节点参数信息,作用类似于通知,如果设备创建了,会设置一个对应parameter,根据参数信息便于其他进程对该节点进行对应的操作
举个栗子
   /dev/null 0666 0 0  device_null   如在dev/null节点下操作:   当设备被创建:系统会设置startup.uevent.device_null为"added"   当设备被卸载:系统会设置startup.uevet.device_null为"removed"
ueventd 在创建节点后,会按匹配规则调用 chown、chmod、setfilecon 等设置属性。

七、总结

问题

回答

ueventd 是什么

OpenHarmony 启动子系统中的设备节点守护进程

扮演什么角色

连接内核热插拔事件与 /dev 文件系统的"物业管家"

主要职责

监听 uevent、创建/删除/修复设备节点、设置权限和 SELinux 标签

何时启动

ramdisk 第一阶段由 init_early 拉起,处理冷启动设备;之后作为守护进程监听热插拔

典型例子

插入 U 盘后,内核发 ACTION=add SUBSYSTEM=block,ueventd 创建 /dev/block/sda 并配置权限

如果没有 ueventd,内核驱动虽然识别了设备,但用户空间没有 /dev 节点,framework 和应用就无法访问硬件。

通俗易懂的ueventd官方文档摘抄:

ueventd服务是按需启动的服务,其行为是当有uevent事件上报时,init监听到socket消息,会将ueventd服务拉起使其处理相应的socket消息,ueventd服务处理完现有的socket消息后,会自己再轮询对应socket句柄30秒,若30秒内又有新消息上报,则继续处理,待处理结束后再次计时轮询30秒;若超过30秒都没有新消息上报,ueventd服务将会退出,并将socket交还给init轮询,此时就会出现日志:poll ueventd socket timeout, ueventd exit


安卓小众宝藏 APP!灵动岛 + 流动文字表白墙,每天零钱可直接提现

灵动岛美化 安卓专属灵动常驻窗口,听歌、电量、网速等自定义样式,手机瞬间变精致,操作便利不卡顿。

全屏流动文字表白墙:三连击灵动岛可进入设置全屏滚动流动文字,不单用于告白,留联系电话、展示标语、心愿短句都适配,字体配色滚动速度随心调,摆摊、告白都好用。

无门槛赚钱提现:日常使用就能攒红包,无最低提现门槛,无提现上限,每日均可微信提现,亲测不到一个月累计微信到账 118.86 元。零碎时间买点零食,yyds

实测零碎时间看广告,每小时红包预计可以拿到手5块,真实靠谱。

填写邀请码 CEAF62A3,双方解锁红包福利,我升级 VIP,你额外多拿收益。 安装包很小,下载无捆绑,感兴趣直接点链接体验:

下载入口:

https://qcnvld5ujs4p.feishu.cn/file/PwlwbwRfSoIPYlxSAfFcaAqMnmd

最新文章

随机文章