Android ueventd浅析
来源:互联网 发布:c语言标识符由什么组成 编辑:程序博客网 时间:2024/05/22 13:01
platform
mstar828
android 5.0.1
在linux2.6之后,udev取代了devfs,但是在android中却没有udev或者mdev1,而是由ueventd进程实现了类似功能(管理设备节点权限、创建设备节点)。
ueventd通过两种方式创建设备节点:
- 静态,ueventd启动时,根据在sysfs中预定义的uevent信息创建设备节点;
- 动态,系统运行过程中,当接收到内核uevent事件时(如插入u盘),动态创建设备节点。
1. ueventd启动过程
在init.rc中,当触发条件为“early-init”时ueventd被启动:
system/core/rootdir/init.rc
on early-init # Set init and its forked children's oom_adj. write /proc/1/oom_score_adj -1000 # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls. write /sys/fs/selinux/checkreqprot 0 # Set the security context for the init process. # This should occur before anything else (e.g. ueventd) is started. setcon u:r:init:s0 # Set the security context of /adb_keys if present. restorecon /adb_keys start ueventd
在运行环境中查看命令“/sbin/ueventd”,其实它是”/init”的软链接:
shell@wwt:/ # ls sbin -l -rwxr-x--- root root 499152 1970-01-01 08:00 adbd-rwxr-x--- root root 3325472 1970-01-01 08:00 healthdlrwxrwxrwx root root 1970-01-01 08:00 ueventd -> ../initlrwxrwxrwx root root 1970-01-01 08:00 watchdogd -> ../init
通过分析Android.mk可知,ueventd.c、watchdog.c与init.c被编译成了同一个可执行文件“/init”,并创建了软链接“/sbin/ueventd”、“/sbin/watchdog”指向“/init”:
system/core/init/Android.mk
LOCAL_SRC_FILES:= \ builtins.c \ init.c \ devices.c \ property_service.c \ util.c \ parser.c \ keychords.c \ signal_handler.c \ init_parser.c \ ueventd.c \ ueventd_parser.c \ watchdogd.c......LOCAL_MODULE:= init......# Make a symlink from /sbin/ueventd and /sbin/watchdogd to /initSYMLINKS := \ $(TARGET_ROOT_OUT)/sbin/ueventd \ $(TARGET_ROOT_OUT)/sbin/watchdogd
原来在文件init.c的main()函数中有一个巧妙的处理:可以通过判断第一个运行参数来启动不同的进程:
- 如果执行“./ueventd”,进入第一个条件分支,执行uevent_main()函数;
- 如果执行“./watchdog”,进入第二个条件分支,执行watchdogd_main()函数;
- 如果执行”./init”,跳过所有分支条件,继续执行main()函数。
system/core/init/init.c
int main(int argc, char **argv){ ...... if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); if (!strcmp(basename(argv[0]), "watchdogd")) return watchdogd_main(argc, argv); ......}
因此,脚本init.rc中的命令“start ueventd”最终执行的是ueventd_main()函数。
2. ueventd代码分析
2.1 main
ueventd_main()函数就是ueventd进程的主体,实现了以下几个功能:
- 解析ueventd.rc文件,管理设备节点权限;
- 递归扫描/sys目录,根据uevent文件,静态创建设备节点;
- 通过netlink获取内核uevent事件,动态创建设备节点。
system/core/init/ueventd.c
int ueventd_main(int argc, char **argv){ struct pollfd ufd; int nr; char tmp[32]; INFO("starting ueventd\n"); ...... // 解析ueventd.rc文件 ueventd_parse_config_file("/ueventd.rc"); // 解析厂商相关的ueventd.$(TARGET_BOARD_PLATFORM).rc文件 snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware); ueventd_parse_config_file(tmp); // 创建netlink sockfd(全局变量device_fd),用于监听uevent事件 // 执行coldboot,递归扫描/sys目录下uevent文件,创建相应设备节点 device_init(); ufd.events = POLLIN; // 获取device_init()创建的sockfd ufd.fd = get_device_fd(); while(1) { ufd.revents = 0; // 通过sockfd监听内核uevent事件 nr = poll(&ufd, 1, -1); if (nr <= 0) continue; if (ufd.revents & POLLIN) // 当接收到内核uevent事件时,创建相应设备节点 handle_device_fd(); }}
2.2 device_init
device_init()函数做了两件事:
uevent_open_socket(),创建netlink套接字,并赋值给全局变量device_fd,用于后续的uevent事件监听,uevent_open_socket()函数涉及到netlink机制与socket编程,具体分析请参考:uevent_open_socket()浅析
coldboot(),递归扫描/sys目录下的uevent节点,然后写入字符串“add”,强制触发内核uevent事件。
这里我们对coldboot()函数代码进行重点分析,调用关系如下:
main() -> device_init()-> coldboot() -> do_coldboot()
system/core/init/devices.c
void device_init(void){ ...... // 创建netlink sockfd(全局变量device_fd),用于监听uevent事件 device_fd = uevent_open_socket(256*1024, true); if(device_fd < 0) return; fcntl(device_fd, F_SETFD, FD_CLOEXEC); fcntl(device_fd, F_SETFL, O_NONBLOCK); // 递归扫描/sys目录下uevent文件,创建相应设备节点 if (stat(coldboot_done, &info) < 0) { ...... coldboot("/sys/class"); coldboot("/sys/block"); coldboot("/sys/devices"); ...... } ......}
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); // 打开目录中的uevent节点,写入“add\n”触发内核uevent事件并处理 fd = openat(dfd, "uevent", O_WRONLY); if(fd >= 0) { write(fd, "add\n", 4); close(fd); handle_device_fd(); } // 递归调用do_coldboot(),扫描uevent节点 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); } }}
2.3 handle_device_id
在main()函数中通过poll监听到内核uevent事件后,由handler_device_id()函数进行处理:
- 解析uevent事件;
- 动态创建设备节点。
这一部分代码的调用关系如下:
main() -> handle_device_id() -> handle_device_event() -> handle_generic_device_event() -> handle_device() -> make_device() -> mknode()
system/core/init/devices.c
void handle_device_fd(){ char msg[UEVENT_MSG_LEN+2]; int n; // 通过sockfd调用recvmsg()获取内核uevent事件,以字符串形式存入msg while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) { ...... struct uevent uevent; // 将字符串msg解析成uevent parse_event(msg, &uevent); ...... // 处理设备相关uevent事件 handle_device_event(&uevent); // 处理固件相关uevent事件(暂不分析) handle_firmware_event(&uevent); }}
static void handle_device_event(struct uevent *uevent){ ...... handle_generic_device_event(uevent); ......}
static void handle_generic_device_event(struct uevent *uevent){ ...... // 根据uevent事件中子系统名称,创建/dev目录及其子目录 } else if(!strncmp(uevent->subsystem, "input", 5)) { base = "/dev/input/"; make_dir(base, 0755); } else if(!strncmp(uevent->subsystem, "mtd", 3)) { base = "/dev/mtd/"; make_dir(base, 0755); } else if(!strncmp(uevent->subsystem, "sound", 5)) { base = "/dev/snd/"; make_dir(base, 0755); } else if(!strncmp(uevent->subsystem, "misc", 4) && !strncmp(name, "log_", 4)) { kernel_logger(); base = "/dev/log/"; make_dir(base, 0755); name += 4; } else base = "/dev/"; links = get_character_device_symlinks(uevent); if (!devpath[0]) snprintf(devpath, sizeof(devpath), "%s%s", base, name); // 根据uevent事件中的信息创建/删除节点及链接 handle_device(uevent->action, devpath, uevent->path, 0, uevent->major, uevent->minor, links);}
static void handle_device(const char *action, const char *devpath, const char *path, int block, int major, int minor, char **links){ ...... // 当uevent事件中的atcion为“add”时,创建节点及链接 if(!strcmp(action, "add")) { make_device(devpath, path, block, major, minor, (const char **)links); if (links) { for (i = 0; links[i]; i++) make_link(devpath, links[i]); } } // 当uevent事件中的atcion为“remove”,删除链接 if(!strcmp(action, "remove")) { if (links) { for (i = 0; links[i]; i++) remove_link(devpath, links[i]); } unlink(devpath); } ......}
static void make_device(const char *path, const char *upath UNUSED, int block, int major, int minor, const char **links){ ...... // 合成设备号 dev = makedev(major, minor); ...... // 根据文件路径、权限、设备号创建节点 mknod(path, mode, dev); ......}
- 由busybox提供的简化版udev,适用于嵌入式应用场合 ↩
- Android ueventd浅析
- android ueventd
- Android ueventd解析
- Android中的ueventd
- android init进程分析 ueventd
- android ueventd 本地native部分源码分析
- android ueventd 本地native部分源码分析
- 往android的内核添加驱动及 ueventd.rc 修改
- 往android的内核添加驱动及 ueventd.rc 修改
- 往android的内核添加驱动及ueventd.rc 修改
- ueventd.rc 处理硬件设备权限和android init 对其解析
- ueventd.rc 处理硬件设备权限和android init 对其解析
- android init进程分析 ueventd — 设备节点的创建、固件更新过程
- Android修改ueventd.rc之后另一种快速打包ramdisk.img的方法
- ueventd.rc 处理硬件设备权限和android init 对其解析
- android6.0 ueventd
- Android6.0 ueventd
- [Android] Android 动画浅析
- GDB详解
- ngxin-rtmp-module 搭建及rtmp & hls 配置简单分享
- ImageViewDemo实现
- JNI的访问域
- 【Hibernate】主键生成策略图示
- Android ueventd浅析
- 安装第三方包的时候报错 IOError: [Errno 13] Permission denied: 'C:\\Program Files\\Python2.7\\Lib\\site-packages
- 程序员的能力拓展模型
- toolBar和默认的手机标题栏处理
- 干货|Nginx+Portus+Registry搭建私有镜像仓库
- windows下的日志查看工具
- myeclipse破解方法
- iOS渠道追踪和来源统计的几种原理
- FPGA问答