在上一篇文章中详细的分析了kthreadd进程的启动,init进程也是有idle进程去触发启动的,init进程分为前后两部分,前一部分是在内核启动的,主要是完成创建和内核初始化工作,内容都是跟Linux内核相关的;后一部分是在用户空间启动的,主要完成Android系统的初始化工作。
本文着重分析init进程的前一部分,init进程的后一部分将在下一篇文章中讲述。
在rest_init函数中启动了init进程,定义在kernel/msm-4.4/init/main.c中
static noinline void __init_refok rest_init(void)
{
......
//用kernel_thread方式创建kthreadd进程,CLONE_FILES 子进程与父进程共享相同的文件描述符(file descriptor)表
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
......
}
kernel_thread会调用do_fork函数用于创建进程,进程创建成功后会通过函数指针回调执行kernel_init函数;进程创建过程在此就不做分析,着重来分析一下kernel_init函数
定义在kernel/msm-4.4/init/main.c中
static int __ref kernel_init(void *unused) //__ref 这个跟之前讲的__init作用一样
{
int ret;
kernel_init_freeable(); //进行init进程的一些初始化操作
/* need to finish all async __init code before freeing the memory */
// 等待所有异步调用执行完成,,在释放内存前,必须完成所有的异步 __init 代码
async_synchronize_full();
// 释放所有init.* 段中的内存
free_initmem();
mark_readonly();
system_state = SYSTEM_RUNNING;// 设置系统状态为运行状态
numa_default_policy(); // 设定NUMA系统的默认内存访问策略
flush_delayed_fput();// 释放所有延时的struct file结构体
if (ramdisk_execute_command) { //ramdisk_execute_command的值为"/init"
ret = run_init_process(ramdisk_execute_command);//运行根目录下的init程序
if (!ret)
return 0;
pr_err("Failed to execute %s (error %d)\n",
ramdisk_execute_command, ret);
}
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) {//execute_command的值如果有定义就去根目录下找对应的应用程序,然后启动
ret = run_init_process(execute_command);
if (!ret)
return 0;
panic("Requested init %s failed (error %d).",
execute_command, ret);
}
//如果ramdisk_execute_command和execute_command定义的应用程序都没有找到,
//就到根目录下找 /sbin/init,/etc/init,/bin/init,/bin/sh 这四个应用程序进行启动
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
return 0;
panic("No working init found. Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance.");
}
kernel_init主要工作是完成一些init的初始化操作,然后去系统根目录下依次找ramdisk_execute_command和execute_command设置的应用程序,如果这两个目录都找不到,就依次去根目录下找 /sbin/init,/etc/init,/bin/init,/bin/sh 这四个应用程序进行启动,只要这些应用程序有一个启动了,其他就不启动了
ramdisk_execute_command和execute_command的值是通过bootloader传递过来的参数设置的,ramdisk_execute_command通过”rdinit”参数赋值,execute_command通过”init”参数赋值,ramdisk_execute_command如果没有被赋值,kernel_init_freeable函数会赋一个初始值”/init”
定义在device/qcom/XXX/BoardConfig.mk
ifeq ($(BOARD_KERNEL_CMDLINE),)
ifeq ($(TARGET_KERNEL_VERSION),4.4)
BOARD_KERNEL_CMDLINE += console=ttyMSM0,115200,n8 init=/init androidboot.console=ttyMSM0 earlycon=msm_serial_dm,0xc170000
else
BOARD_KERNEL_CMDLINE += console=ttyHSL0,115200,n8 init=/init androidboot.console=ttyHSL0 earlycon=msm_hsl_uart,0xc1b0000
endif
BOARD_KERNEL_CMDLINE += androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x37 ehci-hcd.park=3 lpm_levels.sleep_disabled=1 sched_enable_hmp=1 sched_enable_power_aware=1 service_locator.enable=1 swiotlb=1 androidboot.configfs=true androidboot.usbcontroller=a800000.dwc3
endif
定义在kernel/msm-4.4/init/main.c中
static noinline void __init kernel_init_freeable(void)
{
/*
* Wait until kthreadd is all set-up.
*/
//等待&kthreadd_done这个值complete,这个在rest_init方法中有写,在ktreadd进程启动完成后设置为complete
wait_for_completion(&kthreadd_done);
/* Now the scheduler is fully set up and can do blocking allocations */
gfp_allowed_mask = __GFP_BITS_MASK;//设置bitmask, 使得init进程可以使用PM并且允许I/O阻塞操作
/*
* init can allocate pages on any node
*/
set_mems_allowed(node_states[N_MEMORY]);//init进程可以分配物理页面
/*
* init can run on any cpu.
*/
set_cpus_allowed_ptr(current, cpu_all_mask); //init进程可以在任意cpu上执行
//设置到init进程的pid号给cad_pid,cad就是ctrl-alt-del,设置init进程来处理ctrl-alt-del信号
cad_pid = task_pid(current);
//设置smp初始化时的最大CPU数量,然后将对应数量的CPU状态设置为present
smp_prepare_cpus(setup_max_cpus);
//调用__initcall_start到__initcall0_start之间的initcall_t函数指针
do_pre_smp_initcalls();
//开启watchdog_threads,watchdog主要用来监控、管理CPU的运行状态
lockup_detector_init();
smp_init();//启动cpu0外的其他cpu核
sched_init_smp();//进程调度域初始化
page_alloc_init_late();
do_basic_setup();//初始化设备,驱动等,这个方法比较重要,将在下面单独讲
/* Open the /dev/console on the rootfs, this should never fail */
// 打开/dev/console,文件号0,作为init进程标准输入
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
pr_err("Warning: unable to open an initial console.\n");
(void) sys_dup(0);// 标准输入
(void) sys_dup(0);// 标准输出
/*
* check if there is an early userspace init. If yes, let it do all
* the work
*/
//如果 ramdisk_execute_command 没有赋值,则赋值为"/init",之前有讲到
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
// 尝试进入ramdisk_execute_command指向的文件,如果失败则重新挂载根文件系统
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*
* rootfs is available now, try loading the public keys
* and default modules
*/
integrity_load_keys();
load_default_modules();// 加载I/O调度的电梯算法
}
kernel_init_freeable函数做了很多重要的事情,启动了smp,smp全称是Symmetrical Multi-Processing,即对称多处理,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构。初始化设备和驱动程序、打开标准输入和输出、初始化文件系统等等
定义在kernel/msm-4.4/init/main.c中
static void __init do_basic_setup(void)
{
cpuset_init_smp();//针对SMP系统,初始化内核control group的cpuset子系统。
shmem_init();// 初始化共享内存
driver_init();// 初始化设备驱动,比较重要下面单独讲
init_irq_proc();//创建/proc/irq目录, 并初始化系统中所有中断对应的子目录
do_ctors();// 执行内核的构造函数
usermodehelper_enable();// 启用usermodehelper
//遍历initcall_levels数组,调用里面的initcall函数,这里主要是对设备、驱动、文件系统进行初始化,之所有将函数封装到数组进行遍历,主要是为了好扩展
do_initcalls();
random_int_secret_init();//初始化随机数生成池
}
定义在kernel/msm-4.4/drivers/base/init.c中
void __init driver_init(void)
{
/* These are the core pieces */
devtmpfs_init();// 注册devtmpfs文件系统,启动kdevtmpfs进程
devices_init();// 初始化驱动模型中的部分子系统,kset:devices 和 kobject:dev、 dev/block、 dev/char
buses_init();// 初始化驱动模型中的bus子系统,kset:bus、devices/system
classes_init();// 初始化驱动模型中的class子系统,kset:class
firmware_init();// 初始化驱动模型中的firmware子系统 ,kobject:firmware
hypervisor_init();// 初始化驱动模型中的hypervisor子系统,kobject:hypervisor
/* These are also core pieces, but must come after the
* core core pieces.
*/
// 初始化驱动模型中的bus/platform子系统,这个节点是所有platform设备和驱动的总线类型,即所有platform设备和驱动都会挂载到这个总线上
platform_bus_init();
cpu_dev_init();// 初始化驱动模型中的devices/system/cpu子系统,该节点包含CPU相关的属性
memory_dev_init();//初始化驱动模型中的/devices/system/memory子系统,该节点包含了内存相关的属性,如块大小等
container_dev_init();//初始化系统总线类型为容器
of_core_init();//初始化创建,访问和解释设备树的过程。
}
这个函数完成驱动子系统的构建,实现了Linux设备驱动的一个整体框架,但是它只是建立了目录结构,具体驱动的装载是在do_initcalls函数,kernel_init_freeable函数告一段落了,我们接着讲kernel_init中剩余的函数
定义在kernel/msm-4.4/arch/arm64/mm/init.c中中
void free_initmem(void)
{
free_initmem_default(0);
fixup_init();
}
void fixup_init(void)
{
/*
* Unmap the __init region but leave the VM area in place. This
* prevents the region from being reused for kernel modules, which
* is not supported by kallsyms.
*/
unmap_kernel_range((u64)__init_begin, (u64)(__init_end - __init_begin));
}
所有使用__init标记过的函数和使用__initdata标记过的数据,在free_initmem函数执行后,都不能使用,它们曾经获得的内存现在可以重新用于其他目的。
定义在kernel/msm-4.4/arch/arm64/mm/init.c中,
void flush_delayed_fput(void)
{
delayed_fput(NULL);
}
static LLIST_HEAD(delayed_fput_list);
static void delayed_fput(struct work_struct *unused)
{
struct llist_node *node = llist_del_all(&delayed_fput_list);
struct llist_node *next;
for (; node; node = next) {
next = llist_next(node);
__fput(llist_entry(node, struct file, f_u.fu_llist));//释放struct file
}
}
这个函数主要用于释放&delayed_fput_list这个链表中的struct file,struct file即文件结构体,代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的 struct file。
定义在kernel/msm-4.4/init/main.c中
static int run_init_process(const char *init_filename)
{
argv_init[0] = init_filename;
return do_execve(getname_kernel(init_filename), //do_execve就是执行一个可执行文件
(const char __user *const __user *)argv_init,
(const char __user *const __user *)envp_init);
}
run_init_process就是运行可执行文件了,从kernel_init函数中可知,系统会依次去找根目录下的init,execute_command,/sbin/init,/etc/init,/bin/init,/bin/sh这六个可执行文件,只要找到其中一个,其他就不执行。
上述分析了kernel部分的init进程启动过程,然而在Android系统一般会在根目录下放一个init的可执行文件,也就是说Linux系统的init进程在内核初始化完成后,就直接通过run_init_process函数执行init可执行文件,该可执行文件的源代码在system/core/init/init.cpp,下一篇文章中我将以这个文件为入口,分析Android系统的init进程启动过程。