介绍背景本文简单介绍了linux中systemd进程的启动过程,内核源码参考2.6.34。启动过程在架构相关汇编代码具体初始化后调用x86_64_start_kernel/i386_start_kernel函数,进而调用start_kernel开始内核的初始化;start_kernel函数中挂载根文件系统的简要执行流程总结如下:>init_mount_tree()->mount_fs()//挂载rootfs文件系统->rest_init()->kernel_init()->do_basic_setup()->...->populate_rootfs()//解压处理initrd/initramfs根文件system->prepare_namespace()->initrd_load()//处理initrd根文件系统->init_post()->run_init_process()//执行init用户空间进程//代码位于/init/main.casmlinkagevoid__initstart_kernel(void){...vfs_caches_init(totalram_pages);...休息初始化();...}staticvoidrest_init(void){intpid;//创建一个pid为1的init进程,所有用户空间进程的父进程rcu_scheduler_starting();kernel_thread(kernel_init,NULL,CLONE_FS|CLONE_SIGHAND);numa_default_policy();//创建内核空间进程进行内核调度pid=kernel_thread(kthreadd,NULL,CLONE_FS|CLONE_FILES);rcu_read_lock();kthreadd_task=find_task_by_pid_ns(pid,&init_pid_ns);rcu_read_unlock();解锁内核();//创建一个pid为0的idle进程,用于cpu空闲时执行cpu_idle();}vfs_caches_init函数创建VFS根目录并初始化rootfs文件系统并挂载;rest_init函数依次创建三个进程,idle进程、init进程和kthreadd进程;//代码位于/init/main.cstaticintkernel_init(void*unused){do_basic_setup();//打开控制台设备,复制两次,得到0,1,2三个描述符,即标准输入,标准输出,标准错误//之后的所有子进程继承这三个描述符if(sys_open((constchar__user*)"/dev/console",O_RDWR,0)<0)printk(KERN_WARNING"警告:无法打开初始控制台。\n");(无效)sys_dup(0);(无效)sys_dup(0);//启动/在initramfs根文件系统init文件中,systemd是指向自身的init软链接而不是initif(!ramdisk_execute_command)ramdisk_execute_command="/init";如果(sys_access((constchar__user*)ramdisk_execute_command,0)!=0){ramdisk_execute_command=NULL;准备命名空间();}//启动init进程,即执行上面的ramdisk_execute_commandinit_post();}调用kernel_init函数中的do_basic_setup函数,如果内核支持initrd文件系统,则populate_rootfs函数会被编译进内核并调用;populate_rootfs函数调用unpack_to_rootfs将initramfs类型的文件系统解压释放到rootfs的根目录下,其中必须包含/init可执行文件;如果是ramdisk类型的initrd,外部的initrd通过bootloader文件系统读取内存的指定地址,然后在populate_rootfs函数中解压到rootfs文件系统的/initrd.image中;kernel_init函数通过sys_access判断当前挂载的文件系统中是否存在/init,如果不存在则认为是ramdisk类型的initrd,执行prepare_namespace函数;//代码位于/init/do_mounts.cvoid__initprepare_namespace(void){...//将设备名称转换为设备编号并保存ROOT_DEV=name_to_dev_t(root_device_name);...如果(initrd_load())退出;out://将rootfs替换为新根文件系统的根目录,使其成为LinuxVFS的根目录sys_mount(".","/",NULL,MS_MOVE,NULL);sys_chroot(".");}int__initinitrd_load(void){//是否加载initrd标志,默认为1,当内核启动参数包含noinitrd字符串时,mount_initrd会被设置为0if(mount_initrd){//创建一个设备文件,把r将amdisk类型的initrd文件系统释放到内存中,然后通过如下函数加载/initrd.image,在populate_rootfs阶段创建create_dev("/dev/ram",Root_RAM0);//如果内核启动参数中final指定如果根文件系统是新创建的内存文件系统,则不处理//留作后面真正的文件系统处理,否则先执行initrd中的linuxrcif(rd_load_image("/initrd.image")&&ROOT_DEV!=Root_RAM0){sys_unlink("/initrd.image");handle_initrd();返回1;}}sys_unlink("/initrd.image");return0;}prepare_namespace函数首先解析root=参数,将挂载指定的设置转换为设备号保存到ROOT_DEV变量中;在initrd_load函数中,首先在rootfs中创建一个设备号为Root_RAM0的设备/dev/ram0;rd_load_image将populate_rootfs函数中解压为/initrd.image的文件写入系统中的rootfs文件/dev/ram0;如果root=参数指定的挂载设备与Root_RAM0不同,则执行handle_initrd,主要是启动内核线程执行/linuxrc;按照以上步骤完成根文件系统的挂载,然后调用init_post函数;//代码在/init/main.cstaticintinit_post(void){...if(ramdisk_execute_command){run_init_process(ramdisk_execute_command);printk(KERN_WARNING"无法执行%s\n",ramdisk_execute_command);}如果(execute_command){run_init_process(execute_command);printk(KERN_WARNING"无法执行%s。正在尝试""默认...\n",execute_command);}run_init_process("/sbin/init");run_init_process("/etc/init");run_init_process("/bin/init");run_init_process("/bin/sh");panic("Noinitfound.Trypassinginit=optiontokernel.""SeeLinuxDocumentation/init.txtforguidance.");}staticvoidrun_init_process(char*init_filename){argv_init[0]=init_filename;//内核空间调用用户空间程序,启动一个用户空间进程,猜测是阻塞的如果根文件系统不是initramfs类型,ramdisk_execute_command为NULL,所以先执行init参数或/sbin/init指定的可执行文件;否则,直接执行rootfs文件系统中的/init可执行程序;按照以上步骤逐步找到要启动的用户进程,如果找不到内核,会启动失败,出现异常panic;启动systemd查看systemd进程的PID其实是上1,也就是上面系统的init进程,系统/boot/initrd.img一般是cpio格式的initramfs类型的文件系统;通过cpio工具解压后可以看到:drwxrwxr-x12alanalan4096March1209:57./drwxrwxrwt19rootroot4096Mar1214:22../lrwxrwxrwx1alanalan7Mar1209:57bin->usr/bin/drwxr-xr-x2alanalan4096Mar1209:55dev/drwxr-xr-x12alanalan4096Mar1209:57etc/lrwxrwxrwx1alanalan23Mar1209:57init->usr/lib/systemd/systemd*lrwxrwxrwx1alanalan7Mar1209:57lib->usr/lib/lrwxrwxrwx1alanalan9March1209:57lib64->usr/lib64/可以看到/init这个可执行文件其实是systemd的一个符号链接,所以在init_post函数中执行用户进程是systemd;在制作initramfs根文件系统时,将systemd程序打包进去,创建一个从/init到systemd的软链接,然后启动systemd;systemd启动后,完成linux操作系统的后续加载过程
