关于android:Android-init-学习

41次阅读

共计 6417 个字符,预计需要花费 17 分钟才能阅读完成。

1.init 过程运行过程

init 过程是有内核启动的第一个用户级过程。

2.init 过程源码剖析

1. 次要性能

  • 子过程的终止解决
  • 生成设施节点
  • 提供属性服务,保留运行所需的环境变量
  • 剖析 init.rc 启动脚本文件

2. 根本流程

(1)注册与子过程相干的 SIGCHLD 信号处理器,这里只是用作告诉,具体的工夫解决在 init 的事件处理循环中。

(2)创立并挂载启动所需的文件目录

open_devnull_stdio();// 创立日志输出设备
log_init();// 生成 /dev/__kmsg__设施节点文件,输入 log 信息。

(3)解析 init.rc 文件

init.rc 和 init.{hardware}.rc 两个文件

生成服务列表和动作列表。别离以链表的模式注册到 service_list 和 action_list 中。

(4)初始化 QEMU 设施,

(5)解析 init.{hardware}.rc 文件

(6)执行 early-init,片段中的命令

            init, early-boot, boot
action_for_each_trigger("early-init", action_add_queue_tail);// 将参数中的命令保留到运行队列中
drain_action_queue();// 将运行队列中的命令逐个取出执行。

(7)创立 init 过程中曾经定义好的设施节点文件。

device_fd = device_init();

(8)初始化属性服务

property_init();

(9)将 Android 启动 Logo 显示再 LCD 屏幕上。

#define INIT_IMAGE_FILE "/initlogo.rle"
load_565rle_image(INIT_IMAGE_FILE);

(10)设置属性:property_set()函数。

(11)执行动作列表中 init 片段中的命令。

action_for_each_trigger("init", action_add_queue_tail);
drain_action_queue();

(12)启动属性服务
(13)创立套接字 == 接管 == 子过程终止的 SIGCHLD 信号,调用相干 handler 过程解决。

(14)执行动作列表中 early-boot, boot, property 相干的命令。

(15)设置事件处理的监听事件

3.init.rc 脚本文件剖析与执行

两个性能

  • 设置零碎环境,记录待执行的过程
  • action_list 与 service_list 相干的内容。

3.1 动作列表

(1) on init

次要设置环境变量,生成零碎运行所需的文件或目录

(2)on boot

次要设置利用终止条件

(3)on property : <name> = <value>

记录属性扭转时执行的命令

3.2 服务列表

sevice 段落用来记录 init 过程启动的过程。

由 init 过程启动的子过程或是一次性程序,或是运行在后盾的与应用程序、零碎相干的守护过程。

service 段落中的服务全副注册在服务列表中,init 过程从该列表中顺次取出相应服务,并启动它。

3.3init.rc 文件剖析函数

parse_config_file() 用来剖析 init.rc 脚本文件。

int parse_config_file(const char *fn)
{
    char *data;
    data = read_file(fn, 0);  // 读取文件到内存中,保留为字符串格局,返回字符串在内存中的初始地址
    parse_config(fn, data);   // 剖析 read_file 函数返回的字符串,并生成动作列表和参数列表。}

(1)parse_config()函数

static void parse_config(const char *fn, char *s)
{for(;;){switch (next_token(&state)){   // 以行为单位宰割参数传递过去的字符串。case T_NEWLINE:
            if(nargs) {int kw = lookup_keyword(args[0]);// 返回每行首个单词在 keyword_list 构造体数组中的数组编号。if (kw_is(kw, SECTION)){ //SECTION 分组辨别动作列表和服务列表
                    parse_new_section(&state, kw, nargs, args);// 将筛选出的命令注册动作列表或者服务列表中。}
            }
        }
    }
}

3.4 动作列表与服务列表的运行

(1)动作列表的运行

1)获取动作列表的 head

2)从 action 列表取出动作列表 转换成 command 构造体

3)执行 动作列表中动作对应的函数。

(2)服务列表的运行

通过 on boot 段落中的最初一行命令 class_start 运行 service 段落中所有的程序。

service_start 通过执行 execve()零碎调用来运行服务列表中的程序。

4. 创立设施节点文件。

设施节点文件是设施驱动的逻辑文件

  • 冷插拔:以事后定义的设施信息为根底,当 init 过程被启动运行时,对立创立设施节点文件
  • 热插拔:零碎运行时,为插入的设施动态创建设施节点文件。

Linux 中 udev 守护过程创立设施节点文件

4.1 创立动态设施节点

冷插拔机制
在 init 过程启动后,在 /sys 下读取实现注册好的设施信息,而后引发与各设施绝对应的 uevent, 创立设施节点文件。

举例 Binder 驱动程序

(1)binder 驱动程序在初始化函数中调用 misc_rigister()函数,将创立设施节点的所需的信息保留在 /sys 目录下。

(2)相干的节点文件是曾经保留在 /system/core/init/device.c 中,devperms 构造体。
这里的这个文件充当一个列表,
(3)init 过程调用 device_init()函数

ini device_init(void)
{fd = open_uevent_socket();// 创立一个套接字,接管 uevent
    to = get_usecs();
    coldboot(fd, "/sys/class");// 调用 do_coldboot()函数。coldboot(fd, "/sys/block");
    coldboot(fd, "/sys/devices");
    t1 = get_usecs();
    log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
}

static void do_coldboot(int event_fd, DIR *d)
{fd = openat(dfd, "uevent", O_WRONLY);
    if(fd >= 0){write(fd, "add\n", 4);// 写入 "add" 信息,强制引起 uevent
        close(fd);
        handle_device_fd(event_fd);//handler_device_fd()函数接管相干的 uevent.}
}

void handle_device_fd(int fd)
{while((n = recv(fd, msg, UEVENT_MSG_LEN)) > 0){
        struct uevent uevent;
        parse_event(msg, &uevent);// 将 uevent 信息写入 uevent 构造体中
        handle_devive_event(&uevent);// 创立节点文件
    }
}

static void handle_device_event(strut uevent *uevent)
{if(!strncmp(uevent->subsystem, "block", 5)){
        block = 1;
        base = "/dev/block/";
        mkdir(base, 0755);
    }
    .
    .
    else
        base = "dev";
    if(!strcmp (uevent->action, "add)){make_device(devpath, block, uevent->major, uevent->minor);// 调用 mknod()函数,创立设施节点文件。}
}

总结:驱动程序将创立设施节点所需的信息保留到 /sys 目录下,
相干的节点文件 文件曾经保留在 /system/core/init/device.c 中
init 过程启动后调用 docoolboot(),写入 add 强制引起 uevent
从列表中获取相干的节点文件,创立设施节点文件。

uevent.

4.2 动静设施感知

热插拔由 init 过程的事件处理循环来实现。
调用 handle_device_fd()函数,创立设施节点文件。

5. 过程的终止与再启动。

过程再启动代码剖析

子过程终止时,init 过程接管传递过去的 SIGCHLD 信号,调用与之对应的处理函数 sigchld_handler(),

static void sigchld_handler(int s)
{write(signal_fd, &s, 1);
}

signal_fd 记录信号编号,调用 wait_for_one_process()函数被调用。

  • 产生 SIGCHLD 信号时,程序从监听状态中跳出,执行 poll()函数。
  • wait_for_one_process()函数在产生 SIGCHLD 信号的过程的服务列表中,查看过程的设置选项

wait_for_one_process()函数体:

static int wait_for_one_process(int block)
{
    ...
    // 回收过程所占用的资源。while((pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1
                                                && errno == EINTR );
    // 用来取出与服务列表中终止过程相干的服务项目。svc = service_find_by_pid(pid);
    // 在取出的服务项目选项中,查看 SVC_ONESHOT 是否被设置。如果曾经设置了,间接终止。if(!(svc->flags & SVC_ONESHOT)){kill(-pid, SIGKILL);
    }
    /* remove any socket we may have created*/
    for(si = svc->sockets; si; si = si->next){
    // 删除所有过程持有的 socketDescriptor
        unlink(tmp);
    }
    svc->pid = 0;
    svc->flags &= (~SVC_RUNNING);// 勾销正在运行标记。if(svc->flags & SVC_ONESHOT) {// 设置过程标记为 SVC_DISABLED, 从 wait_for_one_process 中跳出。svc->flags |= SVC_DISABLED;
    }
    if(svc->flags & SVC_DISABLED)
        return 0;
    list_for_each(node, &svc->onrestart.commands) {// 重新启动相干过程????cmd = node_to_item(node, struct command, clist);
        cmd->func(cmd->nargs, cmd->args);
    }
       svc->flags |= SVC_RESTARING;
}

wait_for_one_process()函数执行结束后,事件处理循环中的 restart_processes()函数就会被调用执行。

static void rstart_service_if_needed(struct service *svc)
{svc->flags &= (~SVC_RESTSRING);
    service_start(svc);
    return;
}
static void restart_process()
{
    process_needs_restart = 0;
    service_for_each_flags(SVC_RESARTING, restart_service_if_needed);
}
// 运行服务列表中带有 SVC_RESTART 标记的过程。当一个带有此标记的过程被终止,产生 SIGCHLD 信号时,restart_process()函数将重新启动它。

6. 属性服务

只有 init 过程能力批改属性指,其余过程批改属性值时,必须向 init 过程提出申请,init 查看权限后,再批改属性指。

<img src=”https://gitee.com/zhang_menglin965/blogimage/raw/master/img/202112011448356.png” alt=”image-20211201144822301″ style=”zoom:67%;” />

6.1 属性初始化

void property_init(void)
{init_property_area();// 属性域初始化。load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}
int main (int argc, char **argv)
{
    ...
    property_init();// 在共享内存中生成属性域
    ...
}
int start_property_service(void)
{
    int fd;
    // 读取存储在各文件中的根本设置,将他们设置为属性指。load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
    load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);    
    //read persistent properties after all default values have been
    // 读取零碎运行时其余过程新生成的属性值或者更改的属性值。load_persistent_properties();
    // 创立 /dev/socket/property_service 的 Unix 域套接字
    fd_createsocket(PROP_SERVICE NAME, SOCK STREAM, 0666, 0, 0);
    if(fd < 0) return -1;
    fcntl(fd, F_SETFD, FD CLOEXEC);
    fcntl(fd, F_SETFL, 0 NONBLOCK)

    listen(fd, 8);
    return fd
}
int main (int argc, char **argv)
{property_set_fd = start_property_service();//start_property_service()函数,创立启动属性服务所须要的 Unix 域套接字。}

6.2 属性变更申请解决

接管到属性变更申请后,init 过程就会调用 handle_property_set_fd()函数。

void handle_property_set_fd(int fd)
{
    ...
    /* check socket options here */
    if(getsockopt(s, SOL_SOCKER, SO_PEERCRED, &cr, &cr_size) < 0){
        // 获取 SO_PEERCRED 值,以便查看传递信息过程的拜访权限
        close(s);
        ERROR("Unable to recieve socket options\n");
        return;
    }
    ...
    switch(msg.cmd) {
        case PROP_MSG_SETPROP:
            ...
            if(memcmp(msg.name, "ctl." , 4) ==0 ){//ctl 音讯是申请过程启动与终止的音讯。if(check_control_perms(msg.value, cr.uid)){// 查看拜访权限
                    handle_control_message((char *)msg.name + 4, (char*) msg.value);
                }
                ...
            }else{if(check_perms(msg.name, cr.uid)){// 查看拜访权限
                    property_set((char*)msg.name, (char*)msg.value);// 更改属性值,}
                ...
            }
            ...
    }
}

正文完
 0