android init進程分析 ueventd
(懶人最近想起我還有csdn好久沒打理了,這個android init躺在我的草稿箱中快5年了,稍微改改發出來吧)
ueventd主要是負責裝置節點的建立、許可權設定等一些列工作。服務通過使用uevent,監控驅動發送的訊息,做進一步處理。
ueventd實際和init是同一個binary,只是走了不同分支,可參看前一部分。
ueventd的整體代碼比較簡單,主要是三部分:
解析ueventd.rc 初始化裝置資訊 迴圈polling uevent訊息
主函數及相關功能如下如下:
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訊息 }}
處理和解析ueventd.rc
這部分相比init.rc來說,巨簡單,沒什麼特別的。主要是通過rc檔案,來控制目錄節點的許可權。如:
/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,如下
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, ((long) (t1 - t0))); } else { log_event_print(skipping coldboot, already done); }}
open_uevent_socket();
這是開啟uevent的socket。這裡的uevent是用到netlink中的核心事件向使用者態通知(NETLINK_KOBJECT_UEVENT)功能,是核心和使用者態進行雙向資料轉送的非常好的方式,除了eventd外,netd和vold也是使用uevent的。
初始化函數中,比較要注意的coldboot這個函數,字面意思是冷啟動,它的作用是,對那些在uventd啟動前,已經add上的驅動,再重新操作一下,讓他們再發一次event訊息,上層號針對性處理。
這裡對 /sys/class,/sys/block和/sys/devices下的裝置遍曆一遍:
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, 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); } }}write(fd, add , 4)啟用核心,重發add事件的uevent,handle_device_fd();針對event訊息,做響應的處理。
uevent訊息處理
初始化好了之後,daemon程式只要polling新的event事件即可,polling到了,就調用handle_device_fd();來處理,可以看看這個函數:
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] = ''; msg[n+1] = ''; struct uevent uevent; parse_event(msg, &uevent); handle_device_event(&uevent); handle_firmware_event(&uevent); }}
功能就是接受核心發的event訊息,然後parser此訊息,處理對應的訊息事件。
這裡:
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); }}
主要功能,就是根據發過來的uevent,建立/刪除裝置節點,同時以ueventd.rc中描述的使用權限設定更新一些節點許可權。
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); }}如果有副處理器, 還要下載副處理器的firmware,這裡是處理副處理器要下載firmware的指令,fork一個子進程處理。