android init 进程分析
int main(int argc, char **argv)
{
int device_fd = -1;
int property_set_fd = -1;
int signal_recv_fd = -1;
int keychord_fd = -1;
int fd_count;
int s[2];
int fd;
struct sigaction act;
char tmp[PROP_VALUE_MAX];
struct pollfd ufds[4];
char *tmpdev;
char* debuggable;
/**
安装SIGCHLD信号。如果父进程不等待子进程结束,子进程将成为僵尸进程,
其占用的系统资源将得不到释,必须注册此信号处理。
*/
act.sa_handler = sigchld_handler;
act.sa_flags = SA_NOCLDSTOP;
sigemptyset(&act.sa_mask);
sigaction(SIGCHLD, &act, 0);
/**
创建文件系统需要的基本目录。mount一些必要的分区
*/
/* clear the umask */
umask(0); /* 设置文件的默认权限,umask和chmod的权限刚好反的,umask的0000相当于chmod的0777 */
mkdir("/dev", 0755);
mkdir("/proc", 0755);
mkdir("/sys", 0755);
mount("tmpfs", "/dev", "tmpfs", 0, "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/null结点,重定向标准输入,输出以及标准出错。同时为了跟踪系统的行为Android使用kmsg系统。*/
open_devnull_stdio();
**********************************************************
void open_devnull_stdio(void)
{
int fd;
static const char *name = "/dev/__null__";
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
fd = open(name, O_RDWR);
unlink(name);
if (fd >= 0) {
dup2(fd, 0);
dup2(fd, 1); //1为标准输出stdout、2为标准错误stderr,这里调用dup2重定向0,1,2文件描述符到/dev/null
dup2(fd, 2);
if (fd > 2) {
close(fd);
}
return;
}
}
exit(1);
}
关于mknod系统调用
int mknod(const char *pathname, mode_t mode, dev_t dev)
mknod创建一个设备文件,mode参数指定了文件的类型和访问权限,文件类型必须是S_IFREG, S_IFCHR, S_IFBLK, S_IFIFO or S_IFSOCK
如果文件类型是S_IFCHR or S_IFBLK,那么dev指定了新创建的设备的主次设备号,否则,dev被忽略
************************************************************
log_init(); //创建/dev/kmsg设备结点,我们可以利用这个设备输出调试信息
INFO("reading config file\n");
//定义在init.h中
//#define INFO(x...) log_write(6, "<6>init: " x)
//6表示Log等级,我们用全局变量LOG_DEFAULT_LEVEL定义默认可输出的Log等级,如果INFO在终端无输出,那么可修改此变量
/*解析初始化脚本,这里只是parse,将脚本解析到一个链表中,没有执行。 */
parse_config_file("/init.rc");
/*获得内核命令行参数*/
/* pull the kernel commandline and ramdisk properties file in */
qemu_init();
import_kernel_cmdline(0);
/* 根据上一部获得的hardware参数信息,解析额外的硬件相关init脚本, 一般qemu为init.goldfish.rc */
get_hardware_name();
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
parse_config_file(tmp);
/* 找到init.rc paser链表中为early-init属性的项目,将其添加到action queue中 */
action_for_each_trigger("early-init", action_add_queue_tail);
/* 执行这些queue中的动作 */
drain_action_queue();
INFO("device init\n");
device_fd = device_init();
*******************************************************************
int device_init(void)
{
suseconds_t t0, t1;
int fd;
fd = open_uevent_socket(); //建立NETLINK的socket,用于内核空间和用户空间的异步通信,
if(fd < 0)
return -1;
fcntl(fd, F_SETFD, FD_CLOEXEC);
fcntl(fd, F_SETFL, O_NONBLOCK);
t0 = get_usecs();
coldboot(fd, "/sys/class"); //递归遍历/sys/devices目录,查找uevent文件,如果存在,则向uevent文件节点写“add”字符串,触发uevent
//事件,通过socket接收到内核的msg,分析msg字符串,建立相应的设备文件结点
coldboot(fd, "/sys/block"); //对于subsystem是firmware,action是add的uevent事件,fork一个子进程,调用process_firmware_event
//处理相应的uevent事件
coldboot(fd, "/sys/devices");
t1 = get_usecs();
log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
make_device("/dev/pvrsrvkm", 0, 240, 0);
make_device("/dev/fb0", 0, 29, 0);
make_device("/dev/fb1", 0, 29, 1);
make_device("/dev/fb2", 0, 29, 2);
make_device("/dev/fb3", 0, 29, 3);
make_device("/dev/fb4", 0, 29, 4);/*make_device在全局数组qemu_perms,devperms和链表devperms_partners中,查找相匹配的struct
perms,如果存在,则获得相应的uid,gid和mode;否则,uid=gid=0, mode为0600;然后创建设备结点*/
return fd;
}
*******************************************************************
property_init();/*初始化属性系统,系统属性是设备文件/dev/ashmem,大小为32Kb的一块区域.用全局变量__system_property_area__引用,
属性信息存储在这块区域1kb之后,全局变量pa_info_array指向其起始元素;初始化之后,读取根文件系统的/default.prop
文件并保存*/
// only listen for keychords if ro.debuggable is true
debuggable = property_get("ro.debuggable");
if (debuggable && !strcmp(debuggable, "1")) {
keychord_fd = open_keychord();
} /*遍历service_list中的struct service svc,检测其成员keycodes是否为空
keycodes是通过/dev/keychord来触发这个service,如果keycodes不为空,那么就添加一个新的keychord到链表keychords中*/
if (console[0]) {
snprintf(tmp, sizeof(tmp), "/dev/%s", console);
console_name = strdup(tmp);
}
fd = open(console_name, O_RDWR);
if (fd >= 0)
have_console = 1;
close(fd);
/*打开console ,/dev/console
/*加载logo图片,格式是rgb565的raw data(/initlogo.rle),如果不存在此文件,则直接在console上打印android文字,
注意的是: android在首次加载时,会非常慢, 这个图就是提醒下我们是在加载模式下,此图显示后会被自动删除,因此默认只有烧code完毕后
看到一次 */
if( load_565rle_image(INIT_IMAGE_FILE) ) {
fd = open("/dev/tty0", O_WRONLY);
if (fd >= 0) {
const char *msg;
msg = "\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n" // console is 40 cols x 30 lines
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
" A N D R O I D ";
write(fd, msg, strlen(msg));
close(fd);
}
}
if (qemu[0])
import_kernel_cmdline(1);
/*设置相关的属性到属性系统中,默认32kb的属性区可以最大存储247个信息
(8 header words + 247 toc words) = 1020 bytes
1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */
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");
property_set("ro.serialno", serialno[0] ? serialno : "");
property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");
property_set("ro.baseband", baseband[0] ? baseband : "unknown");
property_set("ro.carrier", carrier[0] ? carrier : "unknown");
property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");
property_set("ro.hardware", hardware);
snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
property_set("ro.revision", tmp);
/* execute all the boot actions to get us started */
action_for_each_trigger("init", action_add_queue_tail); /*将action_list中name为init的项添加到action_queue中*/
drain_action_queue();/*执行init section所有的command,主要是安装全局环境变量,加载动态模块,建立相关的文件结构,挂载mtd分区等*/
property_set_fd = start_property_service();/*读取/system/build.prop,/system/default.prop,/data/local.prop到系统属性区,
然后读取目录/data/property,如果有persist.开头的属性文件,则读取值并添加到系统属性
区,开启persistent_properties_loaded=1,创建socket /dev/socket/property_service,监
听等待连接*/
/* create a signalling mechanism for the sigchld handler */
if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
signal_fd = s[0];
signal_recv_fd = s[1];
fcntl(s[0], F_SETFD, FD_CLOEXEC);
fcntl(s[0], F_SETFL, O_NONBLOCK);
fcntl(s[1], F_SETFD, FD_CLOEXEC);
fcntl(s[1], F_SETFL, O_NONBLOCK);
}
/* make sure we actually have all the pieces we need */
if ((device_fd < 0) ||
(property_set_fd < 0) ||
(signal_recv_fd < 0)) {
ERROR("init startup failure\n");
return 1;
}/*确认几个重要的socket成功建立*/
/* execute all the boot actions to get us started */
action_for_each_trigger("early-boot", action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail);
drain_action_queue(); /*添加early-boot,boot SECTION到将要发生的action队列中,执行队列中的所有action,启动service_list中
classname为default并且没有disabled服务,比如启动控制台程序/system/bin/sh,修改属性值init.svc.console
为running;启动程序/system/bin/pvrsrvinit,修改属性值init.svc.pvrsrvinit为running;启动程
序/system/bin/servicemanager,/system/bin/vold,/system/bin/debuggerd,/system/bin/rild,
/system/bin/app_process(zygote) ,/system/bin/mediaserver, /system/bin/playmp3,/system/bin/dbus-daemon
/system/bin/installd等,并修改相应的属性*/
/* run all property triggers based on current state of the properties */
queue_all_property_triggers();
drain_action_queue();
/*在action_list链表中查找名字包含串‘property:’的action,分析得到其属性名,在属性系统中查找属性名对应的值,如果值不为空
则将此action添加到action_queue链表中,触发执行相应的action,这里启动adbd服务,fork一个子进程,加载执行程序/sbin/adbd*/
注册轮询事件:
- device_fd
- property_set_fd
-signal_recv_fd
-如果有keychord,则注册keychord_fd
如果支持BOOTCHART,则初始化BOOTCHART
进入主进程循环:
- 重置轮询事件的接受状态,revents為0
- 查询action队列,并执行。
- 重启需要重启的服务
- 轮询注冊的事件
- 如果signal_recv_fd的revents为POLLIN,则得到一个信号,获取并处理
- 如果device_fd的revents为POLLIN,调用handle_device_fd
- 如果property_fd的revents为POLLIN,调用handle_property_set_fd
- 如果keychord_fd的revents为POLLIN,调用handle_keychord