须知少年凌云志,曾许人间第一流。这篇文章主要讲述android init进程分析 ueventd相关的知识,希望能为你提供帮助。
转自:http://blog.csdn.net/freshui/article/details/2132299
(懒人最近想起我还有csdn好久没打理了,这个Android
init躺在我的草稿箱中快5年了,稍微改改发出来吧)
ueventd主要是负责设备节点的创建、权限设定等一些列工作。服务通过使用uevent,监控驱动发送的消息,做进一步处理。
ueventd实际和init是同一个binary,只是走了不同分支,可参看前一部分。
ueventd的整体代码比较简单,主要是三部分:
- 解析ueventd.rc
- 初始化设备信息
- 循环polling uevent消息
[cpp] view plain copy
- int ueventd_main(int argc, char **argv)
- {
- // 和init一样,没有std输入输出
- open_devnull_stdio();
- // 初始化kernel log,让ueventd的log,通过kernel printk的log输出到串口中
- klog_init();
- //解析和处理ueventd的rc文件
- import_kernel_cmdline(0, import_kernel_nv);
- get_hardware_name(hardware, & revision);
- ueventd_parse_config_file("/ueventd.rc");
- snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);
- ueventd_parse_config_file(tmp);
- // 设备初始化
- device_init();
- // polling uevent消息,对设备进行管理
- ufd.events = POLLIN;
- ufd.fd = get_device_fd();
- while(1) {
- ufd.revents = 0;
- nr = poll(& ufd, 1, -1);
- if (nr < = 0)
- continue;
- if (ufd.revents == POLLIN)
- handle_device_fd(); // polling到消息,处理event消息
- }
- }
这部分相比init.rc来说,巨简单,没什么特别的。主要是通过rc文件,来控制目录节点的权限。如:
[plain] view plain copy
- /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
设备初始化
kernel在加载设备时,会通过socket发送uevent事件给userspace, 在init里,通过接受这些uevent事件,来创建设备的节点。主要函数是device_init()
初始化函数为device_init,如下
[cpp] view plain copy
- void device_init(void)
- {
- suseconds_t t0, t1;
- struct stat info;
- int fd;
- sehandle = NULL;
- if (is_selinux_enabled() > 0) {
- sehandle = selinux_android_file_context_handle();
- }
- /* is 256K enough? udev uses 16MB! */
- 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);
- 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");
- }
- }
open_uevent_socket();
这是打开uevent的socket。这里的uevent是用到netlink中的内核事件向用户态通知(NETLINK_KOBJECT_UEVENT)功能,是内核和用户态进行双向数据传输的非常好的方式,除了eventd外,netd和vold也是使用uevent的。
初始化函数中,比较要注意的coldboot这个函数,字面意思是冷启动,它的作用是,对那些在uventd启动前,已经add上的驱动,再重新操作一下,让他们再发一次event消息,上层号针对性处理。
这里对 /sys/class,/sys/block和/sys/devices下的设备遍历一遍:
[cpp] view plain copy
- 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);
- close(fd);
- handle_device_fd();
- }
- 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);
- }
- }
- }
uevent消息处理
初始化好了之后,daemon程序只要polling新的event事件即可,polling到了,就调用handle_device_fd(); 来处理,可以看看这个函数:
[cpp] view plain copy
- void handle_device_fd()
- {
- char msg[UEVENT_MSG_LEN+2];
- int n;
- 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);
- handle_device_event(& uevent);
- handle_firmware_event(& uevent);
- }
- }
这里:
[cpp] view plain copy
- static void handle_device_event(struct uevent *uevent)
- {
- if (!strcmp(uevent-> action,"add") || !strcmp(uevent-> action, "change"))
- fixup_sys_perms(uevent-> path);
- if (!strncmp(uevent-> subsystem, "block", 5)) {
- handle_block_device_event(uevent);
- } else if (!strncmp(uevent-> subsystem, "platform", 8)) {
- handle_platform_device_event(uevent);
- } else {
- handle_generic_device_event(uevent);
- }
- }
[cpp] view plain copy
- 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(uevent);
- exit(EXIT_SUCCESS);
- }
- }
推荐阅读
- Android端 APP GPU过度绘制GPU过度绘制及优化
- Android搜索功能的案例,本地保存搜索历史记录......
- Appium的一点一滴:Android KEYCODE键值
- 对Android 软键盘向下的监听
- Android隐式启动Activity可能存在的坑
- Android - 进阶之自定义视图浅析
- Android UI(矢量图使用)
- Android 的漂浮动画,下雪动画
- Android中活动条ActionBar的详细使用