代码解析
首先引入依赖文件init.rc,这个是init执行依赖的文件。还有uevetd.rc,其他rc文件我就不在这里列举了。关于以rc为后缀的文件的说明请看,它里面介绍了The Android Init Language consists of four broad classes ofstatements,
which are Actions, Commands, Services, and Options。init.h头文件也有定义结构。
然后看一个重要的文件Makefile--Android.mk。我摘几行重要的代码:
要进行编译的源文件
LOCAL_SRC_FILES:= \
builtins.c \
init.c \
devices.c \
property_service.c \
util.c \
parser.c \
logo.c \
keychords.c \
signal_handler.c \
init_parser.c \
ueventd.c \
ueventd_parser.c \
watchdogd.c
ifeq ($(strip$(INIT_BOOTCHART)),true) 可能会编译的文件条件是ifeq成立
LOCAL_SRC_FILES +=bootchart.c
endif
LOCAL_MODULE:= init 编译出来的模块,看见了嘛,这个就是那个可执行的init
# Make a symlink from /sbin/ueventdand /sbin/watchdogd to /init 创建一个符号链接从/sbin/ueventd和 /sbin/watchdogd 到 /init
SYMLINKS :=\
$(TARGET_ROOT_OUT)/sbin/ueventd\
$(TARGET_ROOT_OUT)/sbin/watchdogd
然后让我们看一下init.c和其他文件
init.c 三个并列程序流程 uevent、watchdog、init
int main(intargc, char**argv)
{//init的入口,一切从这里开始
…………
if(!strcmp(basename(argv[0]),"ueventd"))basename函数作用是去掉输入string中的/。如果传入参数去掉/不等于ueventd则执行ueventd_main。
return 1ueventd_main(argc,argv);
网上说这实际是init启动的自己的进程,它的主要作用根据uevent是创建或删除/dev/xxx(xxx设备名),我们知道在linux下面创建设备节点的接口mknod。
ueventd_main这个函数就是init启动的自己的进程的入口。这个函数比较复杂,并且代码涉及大部分底层API,我个人看起来还是比较吃力的,不过我尽量去给源码加上自己合理的注释,并且粘贴一些我在网上搜索出来的认为比较好的说明。
1###################################################################################################
int ueventd_main(int argc, char **argv)
{//ueventd入口,在ueventd.c
…………
import_kernel_cmdline(0, import_kernel_nv);//从设备文件/dev/cmdline(内核读取命令行参数方式)读取hardware硬件名称
get_hardware_name(hardware, &revision);//变成软件可识别硬件名字,因为需要带有设备号
ueventd_parse_config_file("/ueventd.rc");//解析配置文件uevevtd.rc
snprintf(tmp,sizeof(tmp),"/ueventd.%s.rc",hardware); ueventd_parse_config_file(tmp); //解析ueventd.xxx.rc,xxx是刚刚获取的hardware
device_init();//这个函数比较重要这里做一个流程描述,由于代码过多我就不粘贴出来了。
这里是方法跳转的顺序流,且不是在同一个文件中,大家可以去查看相关源码/system/core/init/*
devicd_init流程(|表示并列、||表示一种流程结束、|表示有先后关系,粗体字表示重要的方法):__start :uevent_open_socket(注册socketHandle,为和内核打交道)(system/core/libcutils/uevetd.c)------->
| coldboot("/sys/class"); |
| coldboot("/sys/block"); |
| coldboot("/sys/devices"); |--------> coldboot ----------->do_coldboot--->
| handle_device_fd(重要的设备处理入口)|---> parse_event -------->
|handle_firmware_event |-------->process_firmware_event------>
| open底层设备驱动open,打开驱动。firmware流程结束||
|handle_device_event |--------->
| fixup_sys_perms uevent->action={add,change} 安装系统权限/sys下的------------>
| handle_block_device_event uevent->subsystem={block} 处理/dev/block下驱------>
| parse_device_name------->parse_platform_block_device-------->handle_device--->
| make_device ----->get_device_perm(设置设备权限)----->makedev(等同于linux创建设备 宏MKDEV,创建设备dev_tdev)----->setegid(gid)(设置有效组id)------>mknod(path, mode, dev)(真正创建特殊设备文件)------>chown(path, uid, -1)(修改属组)--->setegid(AID_ROOT)|
remove_link删除文件(此方法在make_device后执行)||
| handle_platform_device_event uevent->subsystem={platform},处理/devices/platform下-------> add_platform_device(添加/devices/platform下设备)------> remove_platform_device(移除相应设备)||
| handle_generic_device_eventuevent->subsystem ={!platform,!block},处理常规的驱动和相应的驱动映射文件/dev,其中包含usb,mnt,相机,input,图形等不一一列举,详细看源代码。--->
handle_device_fd(同上)||
回顾
至此这个device_init就讲完了,流程比较复杂,现在我们在稍微回顾一下:
首先注册了一个套接字socket和内核互发消息(这个我没太自信看,不是非常确认),之后触发/sys/class,/sys/block,/sys/devices这三个目录及其子目录下的uevent,然后接受并创建设备节点,至此设备节点才算创建。最终handle_device_fd这才是真正处理驱动的入口的开始,会最终调用mknod创建设备节点或者open打开真正的驱动(firmware),流程如下:
handle_device_fd->handle_device_event-> make_device-> mknod(firmware不同,见上面)。
继续下面代码:
ufd.events= POLLIN;
ufd.fd =get_device_fd();
while(1){
ufd.revents = 0;
nr = poll(&ufd, 1, -1);//类Unix 经典poll模式和select模式相似
if (nr <= 0)
continue;
if (ufd.revents == POLLIN)
handle_device_fd();//接受kernel传过来的uevent,动态创建或删除节点,同上
}
}
1###################################################################################################
if(!strcmp(basename(argv[0]),"watchdogd"))同上
return 2watchdogd_main(argc,argv);启动watchdog,相关看门狗介绍可以google
umask(0);//把文件掩码设置为空
//以下是生成安卓系统中的一些基本的系统目录并挂载对应的文件系统3它完成工作和ueventd相似,但是包含一些系统服务进程的启动。
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);
close(open("/dev/.booting",O_WRONLY | O_CREAT, 0000));
open_devnull_stdio();//生成类似linux的/dev/null的设备
klog_init();//日志系统
property_init();//打开/dev/__properties__,使之成为共享区域,用来保存一些属性
get_hardware_name(hardware, &revision);//同上面讲到的
process_kernel_cmdline();//同上
#ifdef HAVE_SELINUX //是否启用了安全linux,如果启动了设置一些SELinux处理器
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);
if (selinux_android_load_policy() <0) {
selinux_enabled = 0; } else {
selinux_init_all_handles();
}
restorecon("/dev");
restorecon("/dev/socket");
#endif
is_charger = !strcmp(bootmode, "charger");
property_load_boot_defaults();//设置加载boot默认properties,放在共享区域
// 解析 /init.rc 和 /init.$hardware.rc 脚本,其中 $hardware 参数从 /proc/cpuinfo 中读取
init_parse_config_file("/init.rc");//我们终于看到这个了init.rc了
//寻找 early-init 触发器,加到 action_queue 中
action_for_each_trigger("early-init", action_add_queue_tail);
//等待完成coldboot动作同上ueventd
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
queue_builtin_action(keychord_init_action, "keychord_init");//keychord初始化
queue_builtin_action(console_init_action, "console_init");//控制台初始化
//为开始执行所有引导的actions,执行action(不管is_charger是什么模式) init、property_service_init、signal_init、check_startup、charger、queue_property_triggers,非is_charger模式下下执行early-fs、fs、post-fs、post-fs-data、early-boot、boot。
action_for_each_trigger("init", action_add_queue_tail);
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);
}
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);
}
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
#if BOOTCHART //是否启动bootchart_init,这里就不介绍了
queue_builtin_action(bootchart_init_action,"bootchart_init");
#endif
//init进程开始执行逻辑
for(;;){
int nr, i, timeout = -1;
execute_one_command();//command是存放在队列中的,具体action和command、service等信息定义在init.h头中,我不一一详细介绍了,可以查看相关文档和代码。但是这一个函数就是执行一条command,这个时候就启动了如下的服务,摘自init.rc:
service adbd /sbin/adbd //adb
service servicemanager/system/bin/servicemanager //servicemanager
service surfaceflinger/system/bin/surfaceflinger//触摸
service zygote/system/bin/app_process -Xzygote /system/bin --zygote --start-system-server //zygote就在这个时候启动的 等
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) {//keychord相关计数
ufds[fd_count].fd = get_keychord_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
keychord_fd_init = 1;
}
…………
nr = poll(ufds, fd_count, timeout);//开始进入poll模式,注册各类handle,都很复杂
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();
}
}
}
return0;
}
回顾
至此init相关的代码我们就差不多讲完了,大家都看了init在安卓启动过程中都做了些什么,而且都是怎么做的。
我稍作说明:
引自ueventd.rc
/dev/binder 0666 root root
看到binder驱动映射的时候了吧,呵呵。
引自init.rc
serviceadbd /sbin/adbd
service servicemanager/system/bin/servicemanager
service zygote /system/bin/app_process-Xzygote /system/bin --zygote --start-system-server
看到adb启动了吧,看到servicemanager启动了吧,看到zygote启动了吧。所以我们的疑惑是不是打开了呢,我们不管底层linux做了什么有一些什么,但是我们经常会听到别人提前什么binder驱动binder通讯,什么zygote、servicemanager等,今天我们终于知道它是在init的时候启动的了。并且按照我上面代码流程,你会在源文件中看见详细的启动过程。