百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Android 开机启动流程分析(03)init启动关键进程uevent&watchdog

gudong366 2025-07-08 00:33 10 浏览

本章关键点总结 & 说明:

说明:思维导图是基于之前文章不断迭代的,本章内容我们关注uevent & watchdog部分即可

1 Uevent入口

Uevent是接收uevent的守护进程,这里它的主要作用根据kernel接收到的uevent事件来创建或删除/dev/xxx(xxx设备名),主函数实现如下:

C++
int ueventd_main(int argc, char *argv)
{
struct pollfd ufd;
int nr;
char tmp[32];

umask(000);
signal(SIGCHLD, SIG_IGN);
open_devnull_stdio();//输入输出重定向
klog_init();
#if LOG_UEVENTS
/ Ensure we're at a logging level that will show the events */
if (klog_get_level() < KLOG_INFO_LEVEL) {
klog_set_level(KLOG_INFO_LEVEL);
}
#endif
//selinux相同,同init
union selinux_callback cb;
cb.func_log = log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
INFO("starting ueventd\n");

//----1 解析和处理ueventd的rc文件,start
import_kernel_cmdline(0, import_kernel_nv);
get_hardware_name(hardware, &revision);
ueventd_parse_config_file("/ueventd.rc");//<关键点1,解析配置文件>
snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);
ueventd_parse_config_file(tmp);
//----1 解析和处理ueventd的rc文件,end
//----2 polling uevent消息,对设备进行管理,start
device_init();//设备初始化
ufd.events = POLLIN;
ufd.fd = get_device_fd();//获取由device_init中uevent_open_socket打开的device_fd
while(1) {
ufd.revents = 0;
nr = poll(&ufd, 1, -1);//poll监听ufd
if (nr <= 0)
continue;
if (ufd.revents & POLLIN) // polling到消息,处理event消息
handle_device_fd();
}
//----2 polling uevent消息,对设备进行管理,end
}

1.1 解析和处理uevent的rc文件

解析流程参考后面的init.rc的解析流程,流程如下:

C++
/**
ueventd_parse_config_file
{根据rc文件,生成一个参考数据结构图}
->parse_config
-->loop循环
-->int token = next_token(&state);
-->根据token执行parse_line
--->lookup_keyword,查找关键字
--->分支kw_is(kw, SECTION)
---->parse_new_section(state, kw, nargs, args);
----->根据kw获取不同的parse_line,这里设置为parse_line_subsystem或no_op{空操作}
------>parse_line_subsystem中调用lookup_keyword();->根据kw 获取s->devname_src和s->dirname
--->分支kw_is(kw, OPTION)
---->state->parse_line(state, nargs, args);
--->分支,其他,即表示根据rc文件创建数据结构,为后期创建节点做数据参考图
---->parse_line_device(state, nargs, args);->set_device_permission()
----->获取(name, attr, perm, uid, gid, prefix, wildcard)参数
----->add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard);
*/

注意:ueventd_parse_config_file并不创建设备节点,它的作用是提供数据库,当有设备节点生成的时候,eventd会参考这个数据库设置设备节点的权限。处理和解析ueventd.rc这部分相比init.rc简单,主要是通过解析rc文件,实现控制目录节点的权限,如:

Bash
/dev/ttyUSB2 0666 radio radio
/dev/ts0710mux* 0640 radio radio
/dev/ppp 0666 radio vpn
# sysfs properties
/sys/devices/virtual/input/input* enable 0666 system system
/sys/devices/virtual/input/input* poll_delay 0666 system system

1.2 polling uevent消息,对设备进行管理

这里主要针对device_init(),get_device_fd()与handle_device_fd()进行分析。

1.2.1 device_init()实现如下:

C++
/**
主要是创建了uevent的socket handle,同时触发/sys/clas,/sys/block,/sys/devices
这三个目录及其子目录下的uevent,然后接受并创建设备节点
*/
void device_init(void)
{
suseconds_t t0, t1;
struct stat info;
int fd;
...//SELinux相关
/ is 256K enough? udev uses 16MB! /
//打开uevent的socket。这里的uevent是用到netlink中的内核事件向用户态通知(NETLINK_KOBJECT_UEVENT)功能
//是内核和用户态进行双向数据传输的非常好的方式,除了eventd外,netd和vold也是使用uevent的
device_fd = uevent_open_socket(2561024, true);
if(device_fd < 0)
return;
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
fcntl(device_fd, F_SETFL, O_NONBLOCK);
if (stat(coldboot_done, &info) < 0) {
t0 = get_usecs();
coldboot("/sys/class");
coldboot("/sys/block");
coldboot("/sys/devices");
t1 = get_usecs();
fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
close(fd);
log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
} else {
log_event_print("skipping coldboot, already done\n");
}
}

这里关键分析coldboot进而分析,代码实现如下:

C++
static void coldboot(const char path)
{
DIR d = opendir(path);
if(d) {
do_coldboot(d);
closedir(d);
}
}
static void do_coldboot(DIR d)
{
struct dirent de;
int dfd, fd;
dfd = dirfd(d);
fd = openat(dfd, "uevent", O_WRONLY);
if(fd >= 0) {
write(fd, "add\n", 4);//激活内核且重发add事件的uevent
close(fd);
handle_device_fd();//针对event消息做响应的处理
}
while((de = readdir(d))) {
DIR *d2;
if(de->d_type != DT_DIR || de->d_name[0] == '.')
continue;
fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
if(fd < 0)
continue;
d2 = fdopendir(fd);
if(d2 == 0)
close(fd);
else {
do_coldboot(d2);//递归调用函数
closedir(d2);
}
}
}

继续,关键分析handle_device_fd()的处理,实现如下:

C++
void handle_device_fd()
{
char msg[UEVENT_MSG_LEN+2];
int n;
//接收kernel通过netlink的socket方式发送的msg事件
while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
continue;
msg[n] = '\0';
msg[n+1] = '\0';
struct uevent uevent;
parse_event(msg, &uevent);//将msg解析成uevent
...//SELinux相关
handle_device_event(&uevent);
handle_firmware_event(&uevent);
}
}

1.2.2 handle_device_event 和handle_firmware_event的分析:

handle_device_event的处理流程如下所示(部分给出伪代码,主要分析流程):

C++
static void handle_device_event(struct uevent uevent)
{
if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") 
|| !strcmp(uevent->action, "online"))
fixup_sys_perms(uevent->path); //通过之前的数据库查表,重新修正节点权限
if (!strncmp(uevent->subsystem, "block", 5)) {
/*
handle_block_device_event(struct uevent uevent)的处理流程如下:
{根据数据库创建对应的节点}
->parse_device_name(),根据uevent解析出name
->make_dir(),根据解析出的name创建文件夹
->对links类型文件进行处理
->handle_device() 处理
-->make_device()
--->get_device_perm()获取设备的perm链表节点
--->mknod创建设备节点+setegid变更节点权限+chown设置节点权限
*/
handle_block_device_event(uevent);
} else if (!strncmp(uevent->subsystem, "platform", 8)) {
/**
handle_platform_device_event(struct uevent uevent)
->分支add_platform_device(path);
-->platform节点添加操作
->分支remove_platform_device(path);
-->platform节点删除操作
*/
handle_platform_device_event(uevent);
} else {
/**
handle_generic_device_event(struct uevent uevent)
{根据subsystem创建各种类型文件夹}
->分支 subsystem 不为空
-->parse_device_name(),根据uevent解析出name
-->ueventd_subsystem_find_by_name
-->mkdir_recursive_for_devpath{call mkdir_recursive->call make_dir}
->分支 uevent->subsystem为usb
-->mkdir_recursive_for_devpath{call mkdir_recursive->call make_dir}
-->make_dir
->分支 uevent->subsystem为drm,input,adsp等等
-->make_dir
->get_character_device_symlinks()做链接处理
->handle_device() 处理
-->make_device()
--->get_device_perm()获取设备的perm链表节点
--->mknod创建设备节点+setegid变更节点权限+chown设置节点权限
*/
handle_generic_device_event(uevent);
}
}

如果有协处理器, 还要下载协处理器的firmware,这里是处理协处理器要下载firmware的指令,fork一个子进程处理,代码实现如下:

C++
static void handle_firmware_event(struct uevent uevent)
{
pid_t pid;
int ret;
if(strcmp(uevent->subsystem, "firmware"))
return;
if(strcmp(uevent->action, "add"))
return;
/ we fork, to avoid making large memory allocations in init proper /
pid = fork();
if (!pid) {
/*
process_firmware_event(struct uevent uevent)
->确保isbooting
->通过uevent->firmware和uevent->path获取文件路径path
->open path,firmware,loading,data
->load_firmware(从firmware中读取数据到data中,且向loading_fd中写入状态)
->close path,firmware,loading,data
*/
process_firmware_event(uevent);
exit(EXIT_SUCCESS);
}
}

1.3 总结{uevent功能}

Plain Text
@1 uevent本质上是解析对应的uevent.rc以及硬件相关rc,生成一张数据库表
@2 监听来自kernel的socket信息,将其转换成uevent,调用事件处理方法来处理
@3 处理的事件

处理的事件如下图所示:

2 watchdog分支分析

2.1 watchdog逻辑说明:

@1 init进程中会解析/init.rc,init.rc文件中有启用watchdog的服务,service watchdogd /sbin/watchdogd 10 20进行喂狗,即当系统出现崩溃或死机达到30s时会进行重启;watchdogd <argv1,argv2> 参数1为间隔的喂狗时间,argv1+argv2为超时时间,当argv2设置为0则到达argv1时间后系统会进行重启

2.2 watchdog进程的主函数实现如下:

C++
#define DEV_NAME "/dev/watchdog" //设备节点,实际上上层仅仅是通过设备节点对硬件进行控制
int watchdogd_main(int argc, char **argv)
{
int fd;
int ret;
int interval = 10;
int margin = 10;
int timeout;
open_devnull_stdio();
klog_init();
INFO("Starting watchdogd\n");
if (argc >= 2)
interval = atoi(argv[1]);//喂狗时间,即每间隔internal喂狗一次
if (argc >= 3)
margin = atoi(argv[2]);//internal+margin,过了超时时间还没有喂狗,则重新启动系统
timeout = interval + margin;
fd = open(DEV_NAME, O_RDWR);//打开看门狗设备
if (fd < 0) {
ERROR("watchdogd: Failed to open %s: %s\n", DEV_NAME, strerror(errno));
return 1;
}
ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);//通过ioctl设置超时时间
if (ret) {//如果超时设置失败,则获取超时
ERROR("watchdogd: Failed to set timeout to %d: %s\n", timeout, strerror(errno));
ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
if (ret) {//设置和获取都失败就不管了,直接报错
ERROR("watchdogd: Failed to get timeout: %s\n", strerror(errno));
} else {//如果第一次设置失败但获取成功,根据timeout和margin重新设置interval,保证看门狗有效
if (timeout > margin)
interval = timeout - margin;
else
interval = 1;
ERROR("watchdogd: Adjusted interval to timeout returned by driver: timeout %d, interval %d, margin %d\n",
timeout, interval, margin);
}
}
while(1) {//无限循环,不断喂狗,除非整个系统挂掉,否则会每隔interval,喂狗一次
write(fd, "", 1);
sleep(interval);
}
}

至此,除了init,另外两个和init相关的进程 uevent和watchdog也分析完毕;这里说明下,实际上通过system/core/init/ 目录下的android.mk文件可以知道,三者就是同一个程序,仅仅是名字不同而已;而通过名字的不同而在开始时执行不同的函数调用。

相关推荐

U盘文件被删怎么简单恢复(u盘里的文件被误删了怎么找回)

现在这个社会不是靠关系靠路子,主要还是靠实力。刘强在机关工作,人长得帅气,工作能力又强。唯独一样不好,脾气太大,动不动就发火,因为小事常和同事发生口角。一次他火大的差点把办公桌给掀翻了,领导见他野蛮的...

不小心删除了一些文件?9 个最佳免费硬盘恢复软件

恢复您曾经无意或意外删除的所有文件和数据。您是否曾经错误地删除了一个对您的工作至关重要并导致您丢失所有进度的文件?我们为您提供了一些最好的免费硬盘恢复软件,以帮助您恢复意外删除的文件,以解决您的文件删...

Studio 中文版:数据救援神器,误删 / 分区损坏 / RAID 恢复一键找回

Studio中文版:数据救援神器,误删/分区损坏/RAID恢复一键找回当文件意外删除、分区损坏,或RAID阵列崩溃时,一款可靠的数据恢复工具往往能挽回关键损失。R-Studio中文版...

你值得拥有的11款Linux数据恢复工具

如果你使用的是Linux操作系统,那么你一定想知道一旦硬盘崩溃的话又该如何保存和恢复数据。其实,现在有很多Linux数据恢复工具可以让我们摆脱数据安全的困扰。小编已经为各位准备好了一些最好的Linux...

误删文件内容怎么恢复(误删文件内容怎么恢复回来)

  在日常使用电脑的过程中,误删文件的情况时有发生。无论是由于操作失误还是病毒攻击,误删文件都会给我们带来不小的困扰。幸运的是,随着技术的发展,误删文件恢复已不再是难题。本文将介绍几款国内外知名的误删...

u盘如何恢复删除的文件?推荐5款u盘数据恢复软件!

在日常生活与工作中,U盘作为便捷的数据存储载体,频繁用于传输和保存各类重要文件。然而,误删文件的情况却时有发生,无论是珍贵的照片、重要的工作文档,还是精心制作的视频,一旦删除,都可能带来不小的麻烦。...

怎么恢复删除的数据?5种有效的数据恢复方法汇总!

在数字化办公与生活的时代,电脑里的每一份数据都承载着重要信息。然而,一个误操作就可能导致数据被删除,无论是尚未保存的重要文档,还是珍藏多年的照片,都可能瞬间“消失”。但其实,数据删除并不意味着永久丢...

u盘删除文件怎么找回?5个数据恢复工具汇总,助你巧妙恢复数据!

在日常使用U盘的过程中,误删文件的情况时有发生,重要的工作文档、珍贵的照片视频一旦消失,难免让人焦急万分。别担心,只要选对数据恢复工具,被删除的数据仍有找回的可能。下面就为你汇总5款实用的数据...

Linux下恢复误删文件:思路+实践(linux删除如何恢复)

周五篮球群里有人问误删文件了怎么恢复,得知是ext4文件系统之后我推荐了ext4magic这个工具,然后又有人提到了xfs的话怎么办,正好前几天看到DaveChinner在邮件列表里提到了这个问题,...

苹果放大招!不用虚拟机了,Mac直接跑Linux容器,开发者效率翻倍

苹果这次真给开发者送福利了!今天凌晨(6月10日),苹果在官宣的Containerization框架直接炸了技术圈——Mac现在能原生运行Linux容器镜像了!这可不是虚拟机那种“套娃”方案,而是基...

7 款老牌经典软件,值得收藏(经典老歌软件)

Calibrehttps://calibre-ebook.com/Calibre是一个电脑电子书管理软件。肯定有人说了,电子书还要管理?那当然了。它的功能更强大的让你想象不到,首先它可以导入PDF,...

神仙级的免费开源电子书阅读器,还支持听书功能

神仙级的免费开源电子书阅读器,还支持听书功能,极空间部署『KoodoReader』哈喽小伙伴们好,我是Stark-C~前段时间不是给大家分享的电子书管理工具『TaleBook』嘛~,然后就有粉丝私信...

如何在Ubuntu系统中重置root密码(ubuntu忘记密码重置root密码命令)

很多人有个问题,就是喜欢把密码设置得很长很复杂,结果谁也没防住,却成功防住了自己ヽ(.ˇдˇ;)ノ对于现代人,特别是年轻人,都有过忘记密码的经历吧。在这篇文章中,我们来了解如何在Ubuntu1...

5款功能强大的PDF阅读器,让PDF阅读更轻松

分享5款功能强大的PDF阅读器,拥有丰富的PDF阅读工具,支持PDF文档划线、笔记、标记等操作,让PDF阅读更轻松!1.嗨动PDF编辑器一款实用的PDF处理软件,不仅可以阅读PDF文档,还能直接编辑、...

上班摸鱼利器! 免费好用的电子书阅读器,NAS轻松部署Koodo Reader

哈喽,大家好我是生活爱好者。笔者也是一名小说爱好者,平时用手机用某信读书,会员也开了,在家看体验也不错,但是上班的时候,在工作快速完成之后,想摸个鱼用手机就不太方便啦,作为爱折腾的人,必须要工作认真,...