- 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
从根节点开始:
- fdt_next_node 找下一个节点
- populate_node 为当前节点申请空间,并对node进行初始化,并且根据内容进行填充
- 会对深度进行设置从而得到父子关系
各个子系统初始化
子系统初始化和设备的初始化,都在最后一步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,通过段名设置优先级(各个段依次执行)。
这样就可以回答下面几个问题
- 设备什么时候开始注册
注册函数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_allocdev->dev.bus = &platform_bus_typeof_device_add->device_add
-
各个子系统的注册
会通过__define_initcall(fn,id)
注册到段中,依次注册 -
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_registerbus_register (注册总线)do_initcalls
device和driver匹配流程
总的来说,总线将设备树中的设备描述(device)与驱动文件(driver)统一管理,并且总线提供了match函数,用来将device与driver匹配上,然后调用driver的probe进行初始化
- 设备和驱动在注册到bus管理的链表中的时候都会去遍历匹配对应的驱动或者设备。
- 几种匹配
- pdev中dirver_override和drv的name匹配
- of_driver_match_device(设备树匹配)
- acpi_driver_match_device(acpi匹配)
- id_table匹配
- 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。调用进程会替换当前进程,执行成功不再返回