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。调用进程会替换当前进程,执行成功不再返回

__EOF__

本文作者alan
本文链接https://www.cnblogs.com/alanli07/p/18399618.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   LIalan  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
点击右上角即可分享
微信分享提示