linux启动流程

  1. KASLR kernel address space layout randomization 内核地址空间布局随机化
    本质是将链接的地址做了随机偏移,这样System.map中的地址也不一样了,需要用 cat /proc/kallsyms才能查看到实际的地址。主要还是为了安全,这样地址布局随机化,来增加安全,被攻击的难度会增加。
  • 防止利用内核漏洞。由于struct等数据结构会产生内核漏洞,通过随机化的方式将这些地址随机,这样攻击者就难以定位。
  • 提高安全性。每次引导得到的内核和内存地址都不用,即使得到上一次的内存布局,下一次就不再有效。
  • 减少信息泄露的影响

通过 CONFIG_RANDOMIZE_BASE 开启

启动流程

设备树加载与解析

设备树最开始在Image末尾,由uboot加载到.init.data中,存在的地址相当于物理地址。

start_kernel-> 
	setup_arch-> (对架构进行解析、包括设备树)
		setup_machine_fdt 
			(本函数得到在.init.data中地址(物理地址)转换后的虚拟地址)
		unflatten_device_tree->
			__unflatten_device_tree->
				unflatten_gt_nodes
					(执行两次,第一次得到转换成struct device_node所需要的空间后,分配内存。第二次真正开始解析设备树节点。)

unflatten_dt_nodes
从根节点开始:

  1. fdt_next_node 找下一个节点
  2. populate_node 为当前节点申请空间,并对node进行初始化,并且根据内容进行填充
  3. 会对深度进行设置从而得到父子关系

各个子系统初始化

子系统初始化和设备的初始化,都在最后一步rest_init中的do_initcalls中

rest_init->
	user_mode_thread(kernel_init, NULL, CLONE_FS)-> (开启0号进程)
		kernel_init->
			kernel_init_freeable->
				do_basic_setup->
					do_initcalls

顺便说一下,kernel_init中,会执行1号进程,首先判断是不是ramdisk(/init),其次会检查传入的命令中指定的1号进程,最后会依次判断 "/sbin/init" "/etc/init" "/bin/init" "/bin/sh"

do_initcalls中会从level0依次执行各种初始化函数。

其中 宏定义__define_initcall(fn, id)会将函数指针fn放在section段 .initcall##id.init,通过段名设置优先级(各个段依次执行)。

这样就可以回答下面几个问题

  1. 设备什么时候开始注册
    注册函数 of_platform_default_populate_init被放到了 .init.text中,被定义到 .initcall3s中(arch_initcall_sync(of_platform_default_populate_init))

所以在执行 do_initcalls 就会执行到of_platform_default_populate_init进行设备的注册

of_platform_default_populate_init->
	of_platform_default_populate->
		of_platform_populate->
			(从根节点开始遍历所有节点)
			of_platform_bus_create->
				of_device_alloc
				dev->dev.bus = &platform_bus_type
				of_device_add->
					device_add
  1. 各个子系统的注册
    会通过 __define_initcall(fn,id) 注册到段中,依次注册

  2. Bus的初始化流程

同样注册也是在 rest_init中执行

rest_init->
	kernel_init->
		kernel_init_freeable->
			do_basic_setup->
				driver_init->
					devices_init->
						devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
						(在/sys/下创建device)
						dev_kobj = kobject_create_and_add("dev", NULL)
						(在sys/下创建dev)
					buses_init->
						bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL)
						(在sys/下创建bus kset)
					classes_init->
						class_kset = kset_create_and_add("class", NULL, NULL);
						(在sys/下创建class kset)
					platform_bus_init->
						device_register
						bus_register 
						(注册总线)
				do_initcalls

device和driver匹配流程


总的来说,总线将设备树中的设备描述(device)与驱动文件(driver)统一管理,并且总线提供了match函数,用来将device与driver匹配上,然后调用driver的probe进行初始化

  1. 设备和驱动在注册到bus管理的链表中的时候都会去遍历匹配对应的驱动或者设备。
  2. 几种匹配
    1. pdev中dirver_override和drv的name匹配
    2. of_driver_match_device(设备树匹配)
    3. acpi_driver_match_device(acpi匹配)
    4. id_table匹配
    5. pdev和drv中name匹配

文件系统挂载

start_kernel
	>vfs_caches_init(fs/dcache.c)
	初始化根目录/
	>mnt_init((fs/namespace.c))
		>sysfs_init(fs/sysfs/mount.c)注册sysfs文件系统(创建根目录,注册到全局链表file_system中)
		>fs_kobj = kobject_create_and_add("fs", NULL);在sys/下创建fs kobject
		>shmem_init(mm/shmem.c)注册tmpfs文件系统
		>init_rootfs(init/do_mounts.c)
		>init_mount_tree(fs/namespace.c)
	>proc_root_init(fs/proc/root.c)注册proc文件系统
	>rest_init
		>kernel_init
			>kernel_init_freeable(init/main.c)
				>do_basic_setup
					>do_initcalls
						>core_initcall(debugfs_init)注册debugfs文件系统
						解压根文件系统
						>rootfs_initcall(populate_rootfs)(init/initramfs.c)
							>populate_rootfs(init/initramfs.c)
								>do_populate_rootfs(init/initramfs.c)解压cpio包
				>prepare_namespace(/init不存在等情况,initramfs不涉及)
			>run_init_process(ramdisk_execute_command)执行ramdisk_execute_command指定参数,即init=的值,默认为/init。调用进程会替换当前进程,执行成功不再返回
posted @ 2024-09-06 09:32  LIalan  阅读(64)  评论(0)    收藏  举报