【鸿蒙OS开发入门】06 - 启动流程代码分析之KernelOS:之启动Linux-4.19 Kernel内核

  • 一、head.S 启动start_kernel()
    • 1.1 start_kernel() 初始化内核工作环境,创建kernel_init线程
  • 二、main.c
    • 2.1 rest_init() 创建kernel_init、kthreadd线程
    • 2.2 kernel_init 线程启动 init 进程,启动多核CPU调度功能
  • 三、do_basic_setup() 所干的大事分析
  • 四、内核驱动初始化顺序initcall分析
    • 3.1 early_initcall()
    • 3.2 module_init()

本系列文章汇总:

  1. 《【鸿蒙OS开发入门】01 - 搭建Ubuntu虚拟机开发环境》
  2. 《【鸿蒙OS开发入门】02 - 启动流程代码分析之Uboot 第一阶段:之解压并引导加载u-boot.bin》
  3. 《【鸿蒙OS开发入门】03 - 启动流程代码分析之Uboot 第二阶段:之board_init初始化》
  4. 《【鸿蒙OS开发入门】04 - 启动流程代码分析之Uboot 第二阶段:之U_BOOT_CMD原理》
  5. 《【鸿蒙OS开发入门】05 - 启动流程代码分析之Uboot 第二阶段:之bootm引导加载Kernel OS》
  6. 《【鸿蒙OS开发入门】06 - 启动流程代码分析之KernelOS:之启动Linux-4.19 Kernel内核》
  7. 《【鸿蒙OS开发入门】07 - 安装docker环境编译openharmony 2.0代码》
  8. 《【鸿蒙OS开发入门】08 - 启动流程代码分析之KernelOS:之启动 liteos_a 内核》
  9. 《【鸿蒙OS开发入门】09 - 启动流程代码分析之KernelOS:之启动Linux-4.19 Kernel内核 中do_basic_setup()所干的大事》
  10. 《【鸿蒙OS开发入门】10 - 启动流程代码分析之第一个用户态进程:init 进程》
  11. 《【鸿蒙OS开发入门】11 - 启动流程代码分析之第一个用户态进程:init 进程 之 Services简介》
  12. 《【鸿蒙OS开发入门】12 - 启动流程代码分析之第一个用户态进程:init 进程 之 pre-init 任务详解》
  13. 《【鸿蒙OS开发入门】13 - 启动流程代码分析之第一个用户态进程:init 进程 之 init 任务详解》
  14. 《【鸿蒙OS开发入门】14 - 启动流程代码分析之第一个用户态进程:init 进程 之 post-init 任务详解》
  15. 《【鸿蒙OS开发入门】15 - 启动流程代码分析之第一个用户态进程:init 进程 之 libuv 做了啥?》

在鸿蒙Kernel OS目录下,目前支持linux-4.19liteos_aliteos_m 这三种kernel 内核,
接下来,我们一个一个来分析下它们。

据我的理解,
linux-4.19: 完整的linux kernel 4.19版本,属于分时操作系统(TSOS)
liteos_a:对应的是 ARM Cotex A系列的CPU,且目前只支持32位系统, 属于精简版的linux 分时操作系统(TSOS)
liteos_m:对应的是 ARM Cotex M系列的CPU,目前仅支持 Cortex-M3/M4/M7/M33这四款,属于实时操作系统(RTOS)。

我们本文重点分析下 linux kernel 4.19 的启动流程,开始吧^_^

一、head.S 启动start_kernel()

head.S中,调用顺序为:
b stext =>=>=> b __primary_switch =>=>=> blr __primary_switched =>=>=> b start_kernel

# \kernel\linux-4.19\arch\arm64\kernel\head.S__HEAD_head:b   stext               // branch to kernel start, magicENTRY(stext)bl  preserve_boot_argsbl    el2_setup           // Drop to EL1, w0=cpu_boot_modeadrp   x23, __PHYS_OFFSETand   x23, x23, MIN_KIMG_ALIGN - 1    // KASLR offset, defaults to 0bl    set_cpu_boot_mode_flagbl    __create_page_tables/** The following calls CPU setup code, see arch/arm64/mm/proc.S for* details.* On return, the CPU will be ready for the MMU to be turned on and* the TCR will have been set.*/bl   __cpu_setup         // initialise processorb    __primary_switch
ENDPROC(stext)__primary_switch:
#ifdef CONFIG_RANDOMIZE_BASEmov x19, x0             // preserve new SCTLR_EL1 valuemrs  x20, sctlr_el1          // preserve old SCTLR_EL1 value
#endifbl    __enable_mmu
#ifdef CONFIG_RELOCATABLEbl __relocate_kernel
#ifdef CONFIG_RANDOMIZE_BASEldr x8, =__primary_switchedadrp    x0, __PHYS_OFFSETblr    x8/** If we return here, we have a KASLR displacement in x23 which we need* to take into account by discarding the current kernel mapping and* creating a new one.*/pre_disable_mmu_workaroundmsr   sctlr_el1, x20          // disable the MMUisbbl __create_page_tables        // recreate kernel mappingtlbi  vmalle1             // Remove any stale TLB entriesdsb  nshmsr  sctlr_el1, x19          // re-enable the MMUisbic   iallu               // flush instructions fetcheddsb    nsh             // via old mappingisbbl __relocate_kernel
#endif
#endifldr   x8, =__primary_switchedadrp    x0, __PHYS_OFFSETbr x8
ENDPROC(__primary_switch)
/** The following fragment of code is executed with the MMU enabled.**   x0 = __PHYS_OFFSET*/
__primary_switched:adrp x4, init_thread_unionadd    sp, x4, #THREAD_SIZEadr_l   x5, init_taskmsr    sp_el0, x5          // Save thread_infoadr_l    x8, vectors         // load VBAR_EL1 with virtualmsr    vbar_el1, x8            // vector table addressisbstp   xzr, x30, [sp, #-16]!mov    x29, spstr_l    x21, __fdt_pointer, x5      // Save FDT pointerldr_l    x4, kimage_vaddr        // Save the offset betweensub   x4, x4, x0          // the kernel virtual andstr_l  x4, kimage_voffset, x5      // physical mappings// Clear BSSadr_l   x0, __bss_startmov  x1, xzradr_l    x2, __bss_stopsub   x2, x2, x0bl    __pi_memsetdsb  ishst               // Make zero page visible to PTW#ifdef CONFIG_KASANbl   kasan_early_init
#endif
#ifdef CONFIG_RANDOMIZE_BASEtst x23, ~(MIN_KIMG_ALIGN - 1)  // already running randomized?b.ne  0fmov   x0, x21             // pass FDT address in x0bl kaslr_early_init        // parse FDT for KASLR optionscbz   x0, 0f              // KASLR disabled? just proceedorr  x23, x23, x0            // record KASLR offsetldp   x29, x30, [sp], #16     // we must enable KASLR, returnret                  // to __primary_switch()
0:
#endifadd   sp, sp, #16mov  x29, #0mov  x30, #0b    start_kernel
ENDPROC(__primary_switched)

PS: 由于最近的任务是搞移植鸿蒙OS到RK px30上,打算先从liteos-a 搞起,加上Linux-4.19 Kernel 其实和安卓流程一样,
所以本文Linux-4.19 Kernel代码分析先放后,后面有空,我们再补回。
现在先跳到:《【鸿蒙OS开发入门】08 - 启动流程代码分析之KernelOS:之启动 liteos_a 内核》

1.1 start_kernel() 初始化内核工作环境,创建kernel_init线程

start_kernel() 中的代码比较多,如果一个个函数去分析,那光看完kernel 就不知道得要看多久,我们抓住内核主线来分析析,会更好理解一些。

在kernel 中,前一部分代码,我们可以统称为环境初始化,包括:

  • CPU初始化
  • 内存分页初始化
  • command_line解析
  • stack栈初化
  • 内存初始化
  • 调度器初始化
  • 工作队列初始化
  • RCU初始化
  • IRQ中断初始化
  • 时钟初始化
  • console终端初始化
  • 内存加密相关初始化
  • thread 线程初始化
  • vfs 虚拟文件系统、proc、sys cgroup初始化

在前面这些基础功能初始化好了,之后,引出重点 rest_init();
可以说,在start_kernel()中前面这么多初始化,都是为了 rest_init() 它来服务的,我们下一章来看下它主要干了啥。

# \kernel\linux-4.19\init\main.casmlinkage __visible void __init start_kernel(void)
{char *command_line;char *after_dashes;// 1. 设置系统第一个进程 init_task 的栈末尾地址为0x57AC6E9D, init_task信息定义在:\kernel\linux-4.19\init\init_task.c中set_task_stack_end_magic(&init_task);// 2. 获取cpu id,  并打印:Booting Linux on physical CPU 0x%010lx [0x%08x]// arm64采用的是对称多处理模型,这个模型下:多个CPU之间是平等关系,共享全部总线,内存和IO。smp_setup_processor_id();debug_objects_early_init();cgroup_init_early();// 禁止IRQ中断local_irq_disable();         // setipl(IPL_MAX);early_boot_irqs_disabled = true;// 初始化CPU 0,此处直接返回的是0, 初始化的是多核CPU中的第一个核心CPU0boot_cpu_init();      // # define smp_processor_id() raw_smp_processor_id()  // 内存分页初始化page_address_init();pr_notice("%s", linux_banner);// 初始化command_line 用于接收u-boot上报的command_line, uboot中是在EmmcInitParam()函数中拼凑command_line保存在.init.data段中。// 从.data段中将command_line取出保存在&command_line 中setup_arch(&command_line);        // linux-4.19\arch\riscv\kernel\setup.c/* * Set up the the initial canary and entropy after arch* and after adding latent and command line entropy.*/add_latent_entropy();add_device_randomness(command_line, strlen(command_line));// 初始化stack 栈boot_init_stack_canary();mm_init_cpumask(&init_mm);// 将boot_command_line中的数据保存在saved_command_line中,将command_line中的数据保存setup_command_line(command_line);setup_nr_cpu_ids();setup_per_cpu_areas();smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */boot_cpu_hotplug_init();build_all_zonelists(NULL);page_alloc_init();pr_notice("Kernel command line: %s\n", boot_command_line);/* parameters may set static keys */jump_label_init();parse_early_param();after_dashes = parse_args("Booting kernel",static_command_line, __start___param, __stop___param - __start___param,  -1, -1, NULL, &unknown_bootoption);if (!IS_ERR_OR_NULL(after_dashes))parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, NULL, set_init_arg);setup_log_buf(0);// 初始化内存mm_init();// 使能trace_printkftrace_init();/* trace_printk can be enabled here */early_trace_init();// 调度器初始化/* Set up the scheduler prior starting any interrupts (such as the* timer interrupt). Full topology setup happens at smp_init()* time - but meanwhile we still have a functioning scheduler. */sched_init();/* Disable preemption - early bootup scheduling is extremely* fragile until we cpu_idle() for the first time. */preempt_disable();if (WARN(!irqs_disabled(),"Interrupts were enabled *very* early, fixing it\n"))local_irq_disable();radix_tree_init();/** Set up housekeeping before setting up workqueues to allow the unbound* workqueue to take non-housekeeping into account.*/housekeeping_init();// 工作队初始化/** Allow workqueue creation and work item queueing/cancelling* early.  Work item execution depends on kthreads and starts after* workqueue_init().*/workqueue_init_early();// RCU 初始化,RCU适用于需要频繁的读取数据,而相应修改数据并不多的情景rcu_init();/* Trace events are available after this */trace_init();if (initcall_debug)initcall_debug_enable();context_tracking_init();// irq 初始化,时钟初始化/* init some links before init_ISA_irqs() */early_irq_init();init_IRQ();tick_init();rcu_init_nohz();init_timers();hrtimers_init();softirq_init();timekeeping_init();time_init();perf_event_init();profile_init();call_function_init();WARN(!irqs_disabled(), "Interrupts were enabled early\n");// 使能irq功能early_boot_irqs_disabled = false;local_irq_enable();kmem_cache_init_late();// 终端初始化/** HACK ALERT! This is early. We're enabling the console before* we've done PCI setups etc, and console_init() must be aware of* this. But we do want output early, in case something goes wrong.*/console_init();lockdep_init();/* This needs to be called before any devices perform DMA* operations that might use the SWIOTLB bounce buffers. It will* mark the bounce buffers as decrypted so that their usage will* not cause "plain-text" data to be decrypted when accessed.*/mem_encrypt_init();#ifdef CONFIG_BLK_DEV_INITRDif (initrd_start && !initrd_below_start_ok &&page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",page_to_pfn(virt_to_page((void *)initrd_start)),min_low_pfn);initrd_start = 0;}
#endifkmemleak_init();debug_objects_mem_init();setup_per_cpu_pageset();numa_policy_init();acpi_early_init();if (late_time_init)late_time_init();sched_clock_init();calibrate_delay();pid_idr_init();anon_vma_init();
#ifdef CONFIG_X86if (efi_enabled(EFI_RUNTIME_SERVICES))efi_enter_virtual_mode();
#endif// thread 线程相关初始化thread_stack_cache_init();cred_init();fork_init();proc_caches_init();uts_ns_init();buffer_init();key_init();security_init();dbg_late_init();vfs_caches_init();pagecache_init();signals_init();seq_file_init();proc_root_init();nsfs_init();cpuset_init();cgroup_init();taskstats_init_early();delayacct_init();check_bugs();acpi_subsystem_init();arch_post_acpi_subsys_init();sfi_init_late();if (efi_enabled(EFI_RUNTIME_SERVICES)) {efi_free_boot_services();}// 内核启动/* Do the rest non-__init'ed, we're now alive */rest_init();prevent_tail_call_optimization();
}

二、main.c

2.1 rest_init() 创建kernel_init、kthreadd线程

rest_init() 中主要工作如下:

  1. 启动 kernel_init 线程,在线程中启动第一个系统进程 init,如果是在android里面,则是由init来初始化和启动属性服务,并且启动Zygote进程,至于鸿蒙的init 干了啥,现在我也不知道 ,我们后续边看代码边分析,哈哈。
  2. 通过pid 来找到init进程的task_struct 结构体,kernel_init进程的pid=1
# linux-4.19\kernel\pid.c
struct pid_namespace init_pid_ns = {.kref = KREF_INIT(2),.idr = IDR_INIT(init_pid_ns.idr),.pid_allocated = PIDNS_ADDING,.level = 0,.child_reaper = &init_task,.user_ns = &init_user_ns,.ns.inum = PROC_PID_INIT_INO,
#ifdef CONFIG_PID_NS.ns.ops = &pidns_operations,
#endif
};
EXPORT_SYMBOL_GPL(init_pid_ns);

init_task定义在\linux-4.19\init\init_task.c 中,其中定义了init_task的pid,结构体为struct pid init_struct_pid ,结构体没啥好看的,且代码有点长,我就不贴出来了,可以自行去init_task.c中看下。

  1. 创建kthreadd线程,进程号为2,并获取kkthreadtask struct保存在kthreadd_task中,kthreadd用于处理scheule调度相关事宜,负责所有内核线程的调度和管理
    它的任务就是管理和调度其他内核线程kernel_thread, 会循环执行一个kthreadd的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread, 当我们调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程

  2. kernel_init线程启动后,刚开始一直处于等待状态,当kthreadd 线程创建好后,会置位标志位kthreadd_done,让处于等待中的kernel_init继续运行

  3. 至此,CPU 就主要交到了kernel_init 线程手中

# linux-4.19\init\main.c
static noinline void __ref rest_init(void)
{struct task_struct *tsk;int pid;// RCU (Read-Copy Update)是一种同步机制,是对读写锁的优化, 启动RCU机制,用于多核心读取数据时的同步rcu_scheduler_starting();/** We need to spawn init first so that it obtains pid 1, however* the init task will end up wanting to create kthreads, which, if* we schedule it before we create kthreadd, will OOPS.*/// 启动 kernel_init 线程,在线程中启动第一个系统进程 init,进程号为0pid = kernel_thread(kernel_init, NULL, CLONE_FS);/** Pin init on the boot CPU. Task migration is not properly working* until sched_init_smp() has been run. It will set the allowed* CPUs for init to the non isolated CPUs.*/// 通过pid 来找到init进程的task_struct 结构体rcu_read_lock();tsk = find_task_by_pid_ns(pid, &init_pid_ns);set_cpus_allowed_ptr(tsk, cpumask_of(smp_processor_id()));rcu_read_unlock();numa_default_policy();// 创建kthreadd进程,进程号为2,并获取kkthread的task struct保存在kthreadd_task中,kthreadd用于处理scheule调度相关事宜,负责所有内核线程的调度和管理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();/** Enable might_sleep() and smp_processor_id() checks.* They cannot be enabled earlier because with CONFIG_PREEMPT=y* kernel_thread() would trigger might_sleep() splats. With* CONFIG_PREEMPT_VOLUNTARY=y the init task might have scheduled* already, but it's stuck on the kthreadd_done completion.*/system_state = SYSTEM_SCHEDULING;// 置位标志位,让处于等待中的kernel_init继续运行complete(&kthreadd_done);/** The boot idle thread must execute schedule()* at least once to get things moving:*/schedule_preempt_disabled();/* Call into cpu_idle with preempt disabled */cpu_startup_entry(CPUHP_ONLINE);
}

2.2 kernel_init 线程启动 init 进程,启动多核CPU调度功能

此时,当kthreadd创建好之后,kernel 内核的基本任务都完结了,此时,CPU交到kernel_init线程中,kernel_init就成为了最靓的仔,我们来看下kernel_init主要干了啥?

  1. 调用kernel_init_freeable()函数,在其中干的事可不少,并且都是大事。
    (1)等待kthreadd线程启动完成
    (2)调用do_pre_smp_initcalls() 函数初始化内核中所有 early_initcall的驱动模块,这些驱动函数是在多核CPU启动前初始化的。
    (3)调用smp_init() 启动剩余的CPU核心,至此多核 CPU才是真正的多核CPU了,因为之前都是单核心运行的
    (4)调用sched_init_smp() 启动多核 CPU调度功能
    (5)调用do_basic_setup(void) 函数,中干了不少kernel中的大事

    • (5.1) 初始化共享内存shmemdevtmpfsbusesclassesfirmwarehypervisor、平台驱动platform_bus等初始化,这个函数干的都是kernel的一些大的子系统,我们后续单独列一章分析。
    • (5.2) 注册及初始化 irq中断的proc文件系统
    • (5.3) 调用kernel constructor function kernel构造函数,kernel中的构造函数保存在.ctors 中,但貌似从来没用过这个。
    • (5.4) 使能usermodehelper_enable(),usermodeheler主要是为了让我们在内核中能够直接新建和运行具备root权限的用户空间程序,很明显,这是为后续启动init进程做准备的。
    • (5.5) 调用do_initcalls(); 初始化所有需要module_init的驱动入口函数,我们各个驱动模块就是在这里开始加载的,有并驱动加载顺序,我们后续单列一章来分析。

    (6)启动/dev/console
    (7)配置 ramdisk_execute_command = '/init',如果文件系统不存在/init,则说明当前没有文件系统,此时则调用prepare_namespace()来挂载ramdisks等其他的文件系统,创建/dev/ram,加载/initrd.image镜像。有关ramdisks的,此处就不深入分析,还是回到重点/init 进程

  2. 等待所有 __init 的函数运行完毕,然后释放__init占用的空间及jump占用的空间

  3. 至此系统算初始化完毕了,配置系统状态system_stateSYSTEM_RUNNING

  4. 最终调用run_init_process 启动/init 进程,很明显linux为了避免各位开发者乱放init或者定制为其他名字,默认是/init,当然可以放在/sbin/init、/bin/init/、/etc/init,如果配置execute_command运行其他的程序,如果这些都没有,那就启动/bin/sh

至此,留下/init进程这个子嗣后,我们也该和kernel_init线程说再见了,kernel内核其实最无情了,一jiokernel_init线程踢开,接着把kernel 的财产继承权完整的交给了它的弟弟kthreadd,不停的维护内核线程的调度和管理

static int __ref kernel_init(void *unused)
{int ret;// 1. 等待kthreadd线程启动完成// 2. 调用do_pre_smp_initcalls() 函数初始化内核中所有 module_init的驱动模块。// 3. 调用smp_init() 启动剩余的CPU核心,至此多核 CPU才是真正的多核CPU了,因为之前都是单核心运行的// 4. 调用sched_init_smp() 启动多核 CPU调度功能、devtmpfs、buses、classes、firmware、kernel_init_freeable();+--------------------->static noinline void __init kernel_init_freeable(void){wait_for_completion(&kthreadd_done);do_pre_smp_initcalls();smp_init();sched_init_smp();do_basic_setup();+===================>+ static void __init do_basic_setup(void)+   {+     cpuset_init_smp();+        shmem_init();+     driver_init();              +      init_irq_proc();            //注册及初始化 `irq`中断的`proc`文件系统+       do_ctors();                 //调用`kernel constructor function` kernel构造函数+        usermodehelper_enable();    //在内核中能够直接新建和运行具备root权限的用户空间程序+        do_initcalls();             //初始化所有需要`module_init`的驱动入口函数+   }+<===================/* Open the /dev/console on the rootfs, this should never fail */if (ksys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)if (!ramdisk_execute_command)ramdisk_execute_command = "/init";integrity_load_keys();load_default_modules();}+<---------------------/* need to finish all async __init code before freeing the memory */// 等待所有 __init 的函数运行完毕,然后释放__init占用的空间及jump占用的空音async_synchronize_full();ftrace_free_init_mem();jump_label_invalidate_initmem();free_initmem();mark_readonly();/* Kernel mappings are now finalized - update the userspace page-table* to finalize PTI. */pti_finalize();// 至此系统算初始化完毕了,配置系统状态system_state 为SYSTEM_RUNNINGsystem_state = SYSTEM_RUNNING;numa_default_policy();rcu_end_inkernel_boot();if (ramdisk_execute_command) {ret = run_init_process(ramdisk_execute_command);}/** 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) {ret = run_init_process(execute_command);if (!ret)return 0;panic("Requested init %s failed (error %d).",execute_command, ret);}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/admin-guide/init.rst for guidance.");
}

三、do_basic_setup() 所干的大事分析

本章见:《【鸿蒙OS开发入门】09 - 启动流程代码分析之KernelOS:之启动Linux-4.19 Kernel内核 中do_basic_setup()所干的大事》

四、内核驱动初始化顺序initcall分析

有关initcall ,简单说下吧,也很好理解,
initcall保存在.init 段中,如果展开来,其实就是一个函数指针数组,只不过它用宏控所封装了下,方便驱动开发人员灵活的增删,
它有8个区域,分为8个等级,依次从0-7递减。

比如:

  1. pure_initcall 优先级最高,为0
  2. core_initcall 优先级次之,为1
  3. postcore_initcall 优先级次之,为2
  4. arch_initcall 优先级次之,为3,用于arch板级信息初始化
  5. subsys_initcall 优先级次之,为4,用于各个子系统框架再初始化
  6. fs_initcall 优先级次之,为5,用于文件系统fs初如化
  7. device_initcall 优先级次之,为6,用于初始化内核驱动模块
  8. late_initcall 优先级最次,为7

从这也能看出内核启动过程,core最先启动(如CPU),接着arch板级信息初始化,接着subsystem 各个子系统框架再初始化,接着文件系统fs初如化,最后才是我们的设务驱动初始化。

3.1 early_initcall()

# linux-4.19\include\linux\init.h
#define ___define_initcall(fn, id, __sec) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(#__sec ".init"))) = fn;#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)/** Early initcalls run before initializing SMP.** Only for built-in code, not modules.*/
#define early_initcall(fn)      __define_initcall(fn, early)/** A "pure" initcall has no dependencies on anything else, and purely* initializes variables that couldn't be statically initialized.** This only exists for built-in code, not for modules.* Keep main.c:initcall_level_names[] in sync.*/
#define pure_initcall(fn)           __define_initcall(fn, 0)#define core_initcall(fn)           __define_initcall(fn, 1)
#define core_initcall_sync(fn)      __define_initcall(fn, 1s)
#define postcore_initcall(fn)       __define_initcall(fn, 2)
#define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)
#define arch_initcall(fn)           __define_initcall(fn, 3)
#define arch_initcall_sync(fn)      __define_initcall(fn, 3s)
#define subsys_initcall(fn)         __define_initcall(fn, 4)
#define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)
#define fs_initcall(fn)             __define_initcall(fn, 5)
#define fs_initcall_sync(fn)        __define_initcall(fn, 5s)
#define rootfs_initcall(fn)         __define_initcall(fn, rootfs)
#define device_initcall(fn)         __define_initcall(fn, 6)
#define device_initcall_sync(fn)    __define_initcall(fn, 6s)
#define late_initcall(fn)           __define_initcall(fn, 7)
#define late_initcall_sync(fn)      __define_initcall(fn, 7s)#define __initcall(fn) device_initcall(fn)#define __exitcall(fn)                       \static exitcall_t __exitcall_##fn __exit_call = fn

3.2 module_init()

# linux-4.19\include\linux\module.h#ifndef MODULE
/*** module_init() - driver initialization entry point* @x: function to be run at kernel boot time or module insertion** module_init() will either be called during do_initcalls() (if* builtin) or at module insertion time (if a module).  There can only* be one per module.*/
#define module_init(x)  __initcall(x);/*** module_exit() - driver exit entry point* @x: function to be run when driver is removed** module_exit() will wrap the driver clean-up code* with cleanup_module() when used with rmmod when* the driver is a module.  If the driver is statically* compiled into the kernel, module_exit() has no effect.* There can only be one per module.*/
#define module_exit(x)  __exitcall(x);#else /* MODULE *//** In most cases loadable modules do not need custom* initcall levels. There are still some valid cases where* a driver may be needed early if built in, and does not* matter when built as a loadable module. Like bus* snooping debug drivers.*/
#define early_initcall(fn)      module_init(fn)
#define core_initcall(fn)       module_init(fn)
#define core_initcall_sync(fn)      module_init(fn)
#define postcore_initcall(fn)       module_init(fn)
#define postcore_initcall_sync(fn)  module_init(fn)
#define arch_initcall(fn)       module_init(fn)
#define subsys_initcall(fn)     module_init(fn)
#define subsys_initcall_sync(fn)    module_init(fn)
#define fs_initcall(fn)         module_init(fn)
#define fs_initcall_sync(fn)        module_init(fn)
#define rootfs_initcall(fn)     module_init(fn)
#define device_initcall(fn)     module_init(fn)
#define device_initcall_sync(fn)    module_init(fn)
#define late_initcall(fn)       module_init(fn)
#define late_initcall_sync(fn)      module_init(fn)#define console_initcall(fn)     module_init(fn)
#define security_initcall(fn)       module_init(fn)/* Each module must use one module_init(). */
#define module_init(initfn)                 \static inline initcall_t __maybe_unused __inittest(void)       \{ return initfn; }                 \int init_module(void) __copy(initfn) __attribute__((alias(#initfn)));/* This is only required if you want to be unloadable. */
#define module_exit(exitfn)                 \static inline exitcall_t __maybe_unused __exittest(void)       \{ return exitfn; }                 \void cleanup_module(void) __copy(exitfn) __attribute__((alias(#exitfn)));#endif

【鸿蒙OS开发入门】06 - 启动流程代码分析之KernelOS:之启动Linux-4.19 Kernel内核 启动init进程相关推荐

  1. 【鸿蒙OS开发入门】18 - HDF驱动子系统:加速度计传感器 Driver层驱动代码分析

    [鸿蒙OS开发入门]18 - HDF驱动子系统:加速度计传感器 Driver层代码分析 一.如何添加速度计传感器驱动代码(代码.编译.配置) 1.驱动代码实现 2.驱动编译配置 2.1 linux 编 ...

  2. 【鸿蒙OS开发入门】16 - 重头搭建Ubuntu新环境编译OpenHarmony 3.0 LTS

    [鸿蒙OS开发入门]16 - 重头搭建Ubuntu新环境编译OpenHarmony 3.0 LTSv 一.配置Telnet 和 samba 二.下载code-v3.0-LTS.tar.gz源码 三.配 ...

  3. 【鸿蒙OS开发入门】13 - 启动流程代码分析之第一个用户态进程:init 进程 之 init 任务详解

    [鸿蒙OS开发入门]13 - 启动流程代码分析之第一个用户态进程:init 进程 之 init 任务详解 一. /etc/init.cfg 系统默认cfg:启动lo回环网卡 1.1 init.Hi35 ...

  4. 鸿蒙APP开发入门到进阶 | 入门完整篇。

    大家好,我是 码工,一个有十年工作经验的码农,一心追求技术. 先说说我的开发经历,10年毕业,到目前已经十多年开发经验,大学学习了多种语言,Java,Android,c,c++,到毕业时真正掌握了几乎 ...

  5. ZYNQ启动流程之分析BootRoM

    ZYNQ启动流程之分析BootRoM 一.fsbl由谁来启动 1.背景 2.需要做的事 3.这些事由谁来做----BootRoM 二.何为BootRoM 三.SD卡启动方式 一.fsbl由谁来启动 1 ...

  6. U-BOOT启动流程详细分析[转]

    http://www.cnblogs.com/heaad/archive/2010/07/17/1779829.html U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶 ...

  7. PCIe学习笔记之pcie初始化枚举和资源分配流程代码分析

    本文主要是对PCIe的初始化枚举.资源分配流程进行分析.代码对应的是linux 4.19, 平台是arm64. 文章首发于这里 1. PCIe architecture 1.1 pcie的拓扑结构 在 ...

  8. 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析

    [Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...

  9. USB NCM usbnet 枚举流程代码分析

    USB NCM usbnet 枚举流程代码分析 一.cdc_ncm.c 1.1 [id_table]struct usb_device_id结构体 1.1.1 match_flags 设备类型 1.1 ...

最新文章

  1. CS231n课程笔记翻译
  2. oracle 测试试题,oracle试题
  3. Leetcode刷题系列汇总
  4. Redis 安装部署介绍
  5. RSA加解密VS加签与验签
  6. Windows无法连接到打印机、打印机连接出现0X00000bcb错误应该如何解决?这应该是是最全面的解决方法啦~~
  7. 最新批量搜狗域名添加绑定工具
  8. java编译的类包含美元符号 $
  9. python下载第三方库超时报错pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host=‘files.pyt
  10. 石大师装机大师怎么重装系统
  11. (超详细)大数据Hadoop之HDFS组件
  12. SQL如何进行帕累托分析?(窗口函数、累计百分比分类)
  13. PTA - 数据库合集27
  14. 穿越火线枪战王者服务器维护,CF手游体验服6.10更新维护公告 六月版本来袭
  15. css渐变斑马条纹_创建斑马条纹表
  16. Kubernetes安装系列之coredns安装
  17. Android默认短信应用常识
  18. Hbase安装流程及踩坑心得
  19. Android 使用自带的MediaCodec 框架进行本地视频压缩
  20. 第三课 用OD脱壳的基本方法

热门文章

  1. 数据存储大讲堂:谈磁盘列阵与RAID技巧
  2. SpringBoot 整合WebSocket 简单实战案例
  3. vue.js iView组件官网
  4. 政务数据共享开放的意义?
  5. 私有镜像制作及上传harbor私有仓库
  6. 存储在icloud云盘文件夹顶层_iCloud云盘文件夹共享功能使用方法
  7. int 到底是什么
  8. 上海贝尔阿尔卡特移动通信系统有限公司
  9. 公司注册地址同区变更和跨区变更准备材料是一样的吗
  10. 【soliworks】安装soliworks2014