大家好,欢迎来到IT知识分享网。
本章关键点总结 & 说明:

说明:思维导图是基于之前文章不断迭代的,本章内容我们关注➕”loop {属性变化 & 子进程signal & 组合按键} “部分即可
1 循环中关键语句execute_one_command(),实现如下:
C++ void execute_one_command(void) { int ret, i; char cmd_str[256] = ""; / list_tail(&act->commands) == &cmd->clist 判定是否到达cmd list的结尾,即是否是当前action的最后一个命令 */ if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) { /* action_remove_queue_head 从action_queue{即qlist}中获取action 从原来链表中移除后重新初始化,返回该action */ cur_action = action_remove_queue_head();//获取当前的action cur_command = NULL; if (!cur_action) return; cur_command = get_first_command(cur_action);//获取当前action的第一个command } else { cur_command = get_next_command(cur_action, cur_command);//获取当前action的next command } if (!cur_command) return; //当前的action和command均有效,则执行command的func方法 ret = cur_command->func(cur_command->nargs, cur_command->args); ...//日志处理相关 }
每次循环都会进入到execute_one_command方法,这里的逻辑是,顺序执行所有action的command列表。
2 循环中关键语句restart_processes();
C++ //restart_processes->service_for_each_flags(SVC_RESTARTING,restart_service_if_needed) void service_for_each_flags(unsigned matchflags,void (func)(struct service svc)) { struct listnode node; struct service svc; list_for_each(node, &service_list) { svc = node_to_item(node, struct service, slist); if (svc->flags & matchflags) { / 这里传递的func是restart_service_if_needed,实现的核心是: restart_service_if_needed(svc)->service_start(svc, NULL);//在这里启动service */ func(svc); } } }
关键点service_start的处理如下所示:
C++ void service_start(struct service svc, const char dynamic_args) { ...//变量声明初始化 ...//标志位的处理 ...//SELinux相关 pid = fork();//fork进程 if (pid == 0) {//子进程的处理 ...//变量声明初始化 ...//属性context初始化 ...//环境变量初始化 ...//socket创建并将其写入到环境变量 if (svc->ioprio_class != IoSchedClass_NONE) { if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { ERROR("Failed to set pid %d ioprio = %d,%d: %s\n", getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno)); } } ...//是否需要控制台,输入输出是否重定向 setpgid(0, getpid()); ...//设置gid,group,uid,seclabel等 子进程中调用execve执行svc->args[0]{即对应的service},{如果传递了参数用传递的;如果没有用本身的svc->args} } if (pid < 0){}...//fork错误处理 svc->time_started = gettime(); svc->pid = pid; svc->flags |= SVC_RUNNING; if (properties_inited()) //如果属性初始化完毕,则设置"init.svc.{$service_name}" "running" notify_service_state(svc->name, "running"); }
3 循环中的其他逻辑{bootchart除外,property部分除外}
抽离后,仅关注如下关键部分代码即可,如下所示:
C++ queue_builtin_action(keychord_init_action, "keychord_init");//这里将action{keychord_init}添加到action list中 queue_builtin_action(signal_init_action, "signal_init");//这里将action{signal_init}添加到action list中 ... for(;;) { / execute_one_command() 这里执行循环执行到相应action的command时,会分别执行keychord_init_action和signal_init_action signal_init_action->signal_init() keychord_init_action->keychord_init() */ execute_one_command(); //关键语句1 restart_processes(); //关键语句2 { ... if (!signal_fd_init && get_signal_fd() > 0) {//只有第一次会进入,ufds初始化工作 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初始化工作 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); if (nr <= 0) continue; for (i = 0; i < fd_count; i++) { if (ufds[i].revents & POLLIN) {//poll到事件 ... if (ufds[i].fd == get_keychord_fd())//组合按键事件 handle_keychord();//关键点1,处理keychord else if (ufds[i].fd == get_signal_fd())//子进程挂掉信号 handle_signal();//关键点2,处理signal } } ... } }
3.1 处理组合按键handle_keychord的流程
{关于keychords处理,关键分析两个方法,keychords_init和handle_keychords}
keychords本身是组合按键,多点触控的意思,实现的功能主要是,满足组合按键的要求,就会触发特定service的启动
3.1.1 keychords_init的实现如下:
C++ void keychord_init() { int fd, ret; / service_for_each 遍历整个service_list列表,对每个svc均执行add_service_keycodes操作 */ service_for_each(add_service_keycodes); if (!keychords)/ nothing to do if no services require keychords */ return; fd = open("/dev/keychord", O_RDWR); ...//open错误处理 fcntl(fd, F_SETFD, FD_CLOEXEC);//确保子进程无法使用该fd ret = write(fd, keychords, keychords_length);//向驱动写入长度为keychords_length的keychords ...//write错误处理 free(keychords);//使用后释放资源 keychords = 0; keychord_fd = fd;//当init中执行get_keychord_fd时获取该fd }
这里继续专注于add_service_keycodes的实现,如下:
C++ void add_service_keycodes(struct service svc) { ... if (svc->keycodes) {//只要svc的keycodes不为空就add一个全新的keychord到list中 /* 对于一个svc,有nkeycodes个keycodes,nkeycodes个keycodes需要申请的空间即为svc->nkeycodes * sizeof(keychord->keycodes[0]) 对于结构体struct input_keychord { __u16 version; __u16 id; __u16 count; __u16 keycodes[];//注意,该写法是动态数组,keycodes是可以根据申请的空间来扩充的 }; 整个keychord结构体的大小为sizeof(keychord) + nkeycodes个keycodes需要申请的空间 */ size = sizeof(keychord) + svc->nkeycodes sizeof(keychord->keycodes[0]); keychords = realloc(keychords, keychords_length + size);//realloc表示重新申请空间,基于原来申请空间的扩充 ... //keychord的初始化,实际上就是对keychords上一段地址空间初始化 keychord = (struct input_keychord )((char )keychords + keychords_length); keychord->version = KEYCHORD_VERSION; keychord->id = keychords_count + 1; keychord->count = svc->nkeycodes; svc->keychord_id = keychord->id; for (i = 0; i < svc->nkeycodes; i++) { keychord->keycodes[i] = svc->keycodes[i]; } keychords_count++; keychords_length += size; } }
3.1.2 handle_keychord实现如下:
C++ void handle_keychord() { struct service *svc; char adb_enabled[PROP_VALUE_MAX]; int ret; __u16 id; // Only handle keychords if adb is enabled. property_get("init.svc.adbd", adb_enabled);//读取属性init.svc.adbd到adb_enabled中 ret = read(keychord_fd, &id, sizeof(id));//从驱动中读取有效keychords ...//容错处理 if (!strcmp(adb_enabled, "running")) { svc = service_find_by_keychord(id);//通过keycordid获取对应的service if (svc) { INFO("starting service %s from keychord\n", svc->name); service_start(svc, NULL);//启动service } else { ERROR("service for keychord %d not found\n", id); } } }
这里总结下:处理组合按键的核心:
遍历所有的service,查找包含keychords触发机制的service,并将这些service对应的keychords初始化并注册到驱动中
驱动不断返回组合按键,当满足了某个service的keychords条件,则启动该service
3.2 处理子进程挂掉handle_signal的流程
{关于signal处理,关键分析两个方法,signal_init和handle_signal}
3.2.1 signal_init实现如下:
C++ void signal_init(void) { int s[2]; struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = sigchld_handler;//sigchld_handler(s)->write(signal_fd, &s, 1); act.sa_flags = SA_NOCLDSTOP; / sigaction->handle_signal 这里注册了信号处理函数,即一旦收到子进程挂掉发出的SIGCHLD信号,就会执行sigchld_handler来向signal_fd中写入数据 相对于init的循环处理,就会监听到数据,通过ufds[i].fd == get_signal_fd()来触发handle_signal */ sigaction(SIGCHLD, &act, 0); / create a signalling mechanism for the sigchld handler/ if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {//创建socketpair,s[0]和s[1]两者是一边读一边写,完成操作 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); } /* handle_signal read(signal_recv_fd, tmp, sizeof(tmp));//阻塞读操作, while (!wait_for_one_process(0)); */ handle_signal(); }
3.2.2 handle_signal的核心由wait_for_one_process来处理,它的实现如下:
C++ static int wait_for_one_process(int block) { ...//变量初始化 / waitpid 方法说明:指定要等待的子进程{等待子进程结束,并获取到它的pid进程号,WNOHANG表明若没有进程结束,则立即返回} pid>0表示子进程ID;pid=0表示当前进程组中的子进程; pid=-1表示等待所有子进程;pid<-1表示进程组ID为pid绝对值的子进程。 */ while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); if (pid <= 0) return -1; INFO("waitpid returned pid %d, status = %08x\n", pid, status); svc = service_find_by_pid(pid);//通过pid查找对应服务 ...//svc==NULL的错误处理 /* 如果该服务进程没有设定SVC_ONESHOT标志,或者设置了SVC_RESTART标志,则先杀掉当前的进程,在重新创建新的进程; 以避免后面重启进程时,因当前服务进程已经存在而发生错误. */ if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) { kill(-pid, SIGKILL); NOTICE("process '%s' killing any children in process group\n", svc->name); } ...//遍历svc->sockets,释放该进程创建的socket资源 svc->pid = 0; ...//标志位处理{去掉SVC_RUNNING,对于SVC_ONESHOT,直接设置为SVC_DISABLED} ...//且对于服务不需要自动重启则设置属性"init.svc.{$service_name}" "stopped" //总结性说明:一个服务进程在init.rc中只要没有声明SVC_ONESHOT和SVC_RESET标志,当该进程死亡时,就会被重启; .../* 如果一个服务进程带有SVC_CRITICAL标志,且没有SVC_RESTART标志,当它crash、 一定时间内重启的次数超过4此时,系统会自动重启并进入recovery模式 */ svc->flags &= (~SVC_RESTART); svc->flags |= SVC_RESTARTING; ...//如果svc服务中有onrestart选项,则遍历进程重启时需要执行的command列表,并执行 notify_service_state(svc->name, "restarting");//设置属性"init.svc.{$service_name}" "restarting",供别处获取 return 0; }
处理子进程挂掉的核心:
通过挂掉的pid找到对应的svc,将僵尸进程kill掉,释放资源
根据标志位信息确认是否重启,如果重启则设置属性”init.svc.{$service_name}” “restarting”,供别处获取
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/183088.html