标签:des android blog http io ar os 使用 sp
int main(int argc, char **argv) { int fd_count = 0; struct pollfd ufds[4]; char *tmpdev; char* debuggable; char tmp[32]; int property_set_fd_init = 0; int signal_fd_init = 0; int keychord_fd_init = 0; bool is_charger = false; if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); if (!strcmp(basename(argv[0]), "watchdogd")) return watchdogd_main(argc, argv); /* clear the umask */ umask(0); // 创建用户空间的目录,例如/dev,/proc,/sys等。 mkdir("/dev", 0755); mkdir("/proc", 0755); mkdir("/sys", 0755); mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"); mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); mount("devpts", "/dev/pts", "devpts", 0, NULL); mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); // 检测/dev/.booting文件是否可读写和可创建。 close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000)); // 将标准输入、输出、错误输出重定向到/dev/__null__。 open_devnull_stdio(); // 将init的日志输出设备设置为/dev/__kmsg__。 klog_init(); // 初始化和属性相关的资源 property_init(); get_hardware_name(hardware, &revision); // 处理内核命令行 process_kernel_cmdline(); union selinux_callback cb; cb.func_log = klog_write; selinux_set_callback(SELINUX_CB_LOG, cb); cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); selinux_initialize(); /* These directories were necessarily created before initial policy load * and therefore need their security context restored to the proper value. * This must happen before /dev is populated by ueventd. */ restorecon("/dev"); restorecon("/dev/socket"); restorecon("/dev/__properties__"); restorecon_recursive("/sys"); is_charger = !strcmp(bootmode, "charger"); INFO("property init\n"); if (!is_charger) property_load_boot_defaults(); INFO("reading config file\n"); // 分析/init.rc文件的内容 init_parse_config_file("/init.rc"); // 解析完init.rc配置文件后,会得到一系列的Action动作。 // init将动作的执行时间划分为四个阶段:early-init,init,early-boot,boot action_for_each_trigger("early-init", action_add_queue_tail); queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); queue_builtin_action(keychord_init_action, "keychord_init"); queue_builtin_action(console_init_action, "console_init"); /* execute all the boot actions to get us started */ action_for_each_trigger("init", action_add_queue_tail); /* skip mounting filesystems in charger mode */ if (!is_charger) { action_for_each_trigger("early-fs", action_add_queue_tail); action_for_each_trigger("fs", action_add_queue_tail); action_for_each_trigger("post-fs", action_add_queue_tail); action_for_each_trigger("post-fs-data", action_add_queue_tail); } /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random * wasn't ready immediately after wait_for_coldboot_done */ queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(signal_init_action, "signal_init"); queue_builtin_action(check_startup_action, "check_startup"); if (is_charger) { action_for_each_trigger("charger", action_add_queue_tail); } else { action_for_each_trigger("early-boot", action_add_queue_tail); action_for_each_trigger("boot", action_add_queue_tail); } /* run all property triggers based on current state of the properties */ queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); #if BOOTCHART queue_builtin_action(bootchart_init_action, "bootchart_init"); #endif // 进入无限循环,建立init的子进程(init是所有进程的父进程) for(;;) { int nr, i, timeout = -1; execute_one_command(); restart_processes(); if (!property_set_fd_init && get_property_set_fd() > 0) { ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; property_set_fd_init = 1; } if (!signal_fd_init && get_signal_fd() > 0) { ufds[fd_count].fd = get_signal_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; signal_fd_init = 1; } if (!keychord_fd_init && get_keychord_fd() > 0) { ufds[fd_count].fd = get_keychord_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; keychord_fd_init = 1; } if (process_needs_restart) { timeout = (process_needs_restart - gettime()) * 1000; if (timeout < 0) timeout = 0; } if (!action_queue_empty() || cur_action) timeout = 0; #if BOOTCHART if (bootchart_count > 0) { if (timeout < 0 || timeout > BOOTCHART_POLLING_MS) timeout = BOOTCHART_POLLING_MS; if (bootchart_step() < 0 || --bootchart_count == 0) { bootchart_finish(); bootchart_count = 0; } } #endif nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; for (i = 0; i < fd_count; i++) { if (ufds[i].revents == POLLIN) { if (ufds[i].fd == get_property_set_fd()) handle_property_set_fd(); else if (ufds[i].fd == get_keychord_fd()) handle_keychord(); else if (ufds[i].fd == get_signal_fd()) handle_signal(); } } } return 0; }我们可以看到init.c的main函数还是非常复杂的,但是我们不需要每条语句都很清楚(没这个必要,而且这样搞难度太大)。我们通常只需要了解init的主线就可以了。从上述main函数分析,可以看出,init实际上就分为两个部分:
void get_hardware_name(char *hardware, unsigned int *revision) { char data[1024]; int fd, n; char *x, *hw, *rev; // 如果hardware已经有值,说明hardware通过内核命令行提供,直接返回 if (hardware[0]) return; // 已只读方式打开/proc/cpuinfo文件 fd = open("/proc/cpuinfo", O_RDONLY); if (fd < 0) return; // 读取/proc/cpuinfo文件的内容 n = read(fd, data, 1023); close(fd); if (n < 0) return; data[n] = 0; // 从/proc/cpuinfo文件中读取Hardware第一次出现的位置 hw = strstr(data, "\nHardware"); rev = strstr(data, "\nRevision"); if (hw) { x = strstr(hw, ": "); if (x) { x += 2; n = 0; while (*x && *x != '\n') { if (!isspace(*x)) // 将Hardware字段的值都转换为小写,并更新hareware参数的值 // hardware也就是在init.c文件中定义的hardware数组 hardware[n++] = tolower(*x); x++; if (n == 31) break; } hardware[n] = 0; } } if (rev) { x = strstr(rev, ": "); if (x) { *revision = strtoul(x + 2, 0, 16); } } }从get_hardware_name方法的代码可知,该方法主要用于确定hardware和version变量的值。获取hardware的来源是Linux命令行或者/proc/cpuinfo文件的内容。我们可以直接从手机adb模式看一下/proc/cpuinfo的内容,如下图所示:
static void process_kernel_cmdline(void) { /* don't expose the raw commandline to nonpriv processes */ chmod("/proc/cmdline", 0440); /* first pass does the common stuff, and finds if we are in qemu. * second pass is only necessary for qemu to export all kernel params * as props. */ import_kernel_cmdline(0, import_kernel_nv); if (qemu[0]) import_kernel_cmdline(1, import_kernel_nv); // 用属性值设置内核变量 export_kernel_boot_props(); }在process_kernel_cmdline函数中,除了使用import_kernel_cmdline函数导入内核变量外,主要的功能就是调用export_kernel_boot_props函数通过属性设置内核变量。我们来看一下export_kernel_boot_props函数的源码:
static void export_kernel_boot_props(void) { char tmp[PROP_VALUE_MAX]; int ret; unsigned i; struct { const char *src_prop; const char *dest_prop; const char *def_val; } prop_map[] = { { "ro.boot.serialno", "ro.serialno", "", }, { "ro.boot.mode", "ro.bootmode", "unknown", }, { "ro.boot.baseband", "ro.baseband", "unknown", }, { "ro.boot.bootloader", "ro.bootloader", "unknown", }, }; // 通过内核的属性设置应用层配置文件的属性 for (i = 0; i < ARRAY_SIZE(prop_map); i++) { ret = property_get(prop_map[i].src_prop, tmp); if (ret > 0) property_set(prop_map[i].dest_prop, tmp); else property_set(prop_map[i].dest_prop, prop_map[i].def_val); } // 根据ro.boot.console属性的值设置console变量 ret = property_get("ro.boot.console", tmp); if (ret) strlcpy(console, tmp, sizeof(console)); /* save a copy for init's usage during boot */ property_get("ro.bootmode", tmp); strlcpy(bootmode, tmp, sizeof(bootmode)); // 获取ro.boot.hardware的值,保存在tmp字符串数组中 ret = property_get("ro.boot.hardware", tmp); if (ret) strlcpy(hardware, tmp, sizeof(hardware)); // 利用hardware的值设置ro.hardware属性。 // 这个属性就是前面提到的初始化文件名的属性 property_set("ro.hardware", hardware); snprintf(tmp, PROP_VALUE_MAX, "%d", revision); property_set("ro.revision", tmp); /* TODO: these are obsolete. We should delete them */ if (!strcmp(bootmode,"factory")) property_set("ro.factorytest", "1"); else if (!strcmp(bootmode,"factory2")) property_set("ro.factorytest", "2"); else property_set("ro.factorytest", "0"); }从export_kernel_boot_props函数的代码可以看出,该函数实际上就是来回设置一些属性值。
property_init(); start_property_service();
void property_init(void) { init_property_area(); }可以看到。property_init函数中,只是简单的调用了init_property_area函数,那我们接下来看一下init_property_area函数是如何工作的,源码如下:
static int init_property_area(void) { if (property_area_inited) return -1; if(__system_property_area_init()) return -1; if(init_workspace(&pa_workspace, 0)) return -1; fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); property_area_inited = 1; return 0; }从init_property_area函数,我们可以看出,函数首先判断内存区域是否已经初始化过,如果已经初始化,则直接返回-1。如果没有初始化,我们接下来会发现有两个关键函数__system_property_area_init和init_workspace应该是真正的跟内存区域初始化有关。分别分析一下这两个函数。
int __system_property_area_init() { return map_prop_area_rw(); }这里__system_property_area_init也是简单的调用了map_prop_area_rw方法,我们只能继续跟踪了(蛋疼的嵌套啊,不过map_prop_area_rw确实是真正的内存分配函数)。map_prop_area_rw源码如下:
static int map_prop_area_rw() { prop_area *pa; int fd; int ret; /* dev is a tmpfs that we can use to carve a shared workspace * out of, so let's do that... * O_RDWR ==> 读写 * O_CREAT ==> 若不存在则创建 * O_NOFOLLOW ==> 如果filename是软链接,则打开失败 * O_EXCL ==> 如果使用O_CREAT时文件存在,则可返回错误信息 */ fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); if (fd < 0) { if (errno == EACCES) { /* for consistency with the case where the process has already * mapped the page in and segfaults when trying to write to it */ abort(); } return -1; } ret = fcntl(fd, F_SETFD, FD_CLOEXEC); if (ret < 0) goto out; if (ftruncate(fd, PA_SIZE) < 0) goto out; pa_size = PA_SIZE; pa_data_size = pa_size - sizeof(prop_area); compat_mode = false; // mmap映射文件实现共享内存 pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(pa == MAP_FAILED) goto out; memset(pa, 0, pa_size); pa->magic = PROP_AREA_MAGIC; pa->version = PROP_AREA_VERSION; /* reserve root node */ pa->bytes_used = sizeof(prop_bt); /* plug into the lib property services */ __system_property_area__ = pa; close(fd); return 0; out: close(fd); return -1; }代码还是比较好理解的,这块代码主要作用是建立了一块共享内存。
typedef struct { size_t size; int fd; } workspace; static int init_workspace(workspace *w, size_t size) { void *data; int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW); if (fd < 0) return -1; w->size = size; w->fd = fd; return 0; } static workspace pa_workspace;其实,上面两个函数最关键的一点在于 __system_property_area__ = pa的赋值。__system_property_area__是bionic_libc库中输出的一个变量,为什么这里要给它赋值呢?
void start_property_service(void) { int fd; /* * 加载属性文件,其实就是解析这些文件中的属性,然后把属性设置到共享内存中去。 * Android系统一共提供了四个存储属性的文件,它们分别是: * #define PROP_PATH_RAMDISK_DEFAULT "/default.prop" * #define PROP_PATH_SYSTEM_BUILD "/system/build.prop" * #define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop" * #define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop" */ load_properties_from_file(PROP_PATH_SYSTEM_BUILD); load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); // 创建一个socket,用于IPC通信。 fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0); if(fd < 0) return; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK); listen(fd, 8); property_set_fd = fd; }主要的宏我已经在代码中通过注释标记出来了。load_properties_from_file函数主要是从文件中读取property属性,并通过property_set函数写入到共享内存中,这里了解一下property_set函数源码:
int property_set(const char *name, const char *value) { prop_info *pi; int ret; size_t namelen = strlen(name); size_t valuelen = strlen(value); if (!is_legal_property_name(name, namelen)) return -1; if (valuelen >= PROP_VALUE_MAX) return -1; pi = (prop_info*) __system_property_find(name); if(pi != 0) { // 如果属性以"ro."开头,表示是只读属性,不能设置,所以直接返回-1 if(!strncmp(name, "ro.", 3)) return -1; __system_property_update(pi, value, valuelen); } else { ret = __system_property_add(name, namelen, value, valuelen); if (ret < 0) { ERROR("Failed to set '%s'='%s'\n", name, value); return ret; } } /* If name starts with "net." treat as a DNS property. */ if (strncmp("net.", name, strlen("net.")) == 0) { if (strcmp("net.change", name) == 0) { return 0; } /* * The 'net.change' property is a special property used track when any * 'net.*' property name is updated. It is _ONLY_ updated here. Its value * contains the last updated 'net.*' property. */ property_set("net.change", name); } else if (persistent_properties_loaded && strncmp("persist.", name, strlen("persist.")) == 0) { /* * Don't write properties to disk until after we have read all default properties * to prevent them from being overwritten by default values. */ write_persistent_property(name, value); } else if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) { selinux_reload_policy(); } property_changed(name, value); return 0; }客户端可以通过发送socket请求,来设置property。
标签:des android blog http io ar os 使用 sp
原文地址:http://blog.csdn.net/wzy_1988/article/details/40781253