【鸿蒙OS开发入门】06 - 启动流程代码分析之KernelOS:之启动Linux-4.19 Kernel内核 启动init进程
【鸿蒙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()
本系列文章汇总:
- 《【鸿蒙OS开发入门】01 - 搭建Ubuntu虚拟机开发环境》
- 《【鸿蒙OS开发入门】02 - 启动流程代码分析之Uboot 第一阶段:之解压并引导加载u-boot.bin》
- 《【鸿蒙OS开发入门】03 - 启动流程代码分析之Uboot 第二阶段:之board_init初始化》
- 《【鸿蒙OS开发入门】04 - 启动流程代码分析之Uboot 第二阶段:之U_BOOT_CMD原理》
- 《【鸿蒙OS开发入门】05 - 启动流程代码分析之Uboot 第二阶段:之bootm引导加载Kernel OS》
- 《【鸿蒙OS开发入门】06 - 启动流程代码分析之KernelOS:之启动Linux-4.19 Kernel内核》
- 《【鸿蒙OS开发入门】07 - 安装docker环境编译openharmony 2.0代码》
- 《【鸿蒙OS开发入门】08 - 启动流程代码分析之KernelOS:之启动 liteos_a 内核》
- 《【鸿蒙OS开发入门】09 - 启动流程代码分析之KernelOS:之启动Linux-4.19 Kernel内核 中do_basic_setup()所干的大事》
- 《【鸿蒙OS开发入门】10 - 启动流程代码分析之第一个用户态进程:init 进程》
- 《【鸿蒙OS开发入门】11 - 启动流程代码分析之第一个用户态进程:init 进程 之 Services简介》
- 《【鸿蒙OS开发入门】12 - 启动流程代码分析之第一个用户态进程:init 进程 之 pre-init 任务详解》
- 《【鸿蒙OS开发入门】13 - 启动流程代码分析之第一个用户态进程:init 进程 之 init 任务详解》
- 《【鸿蒙OS开发入门】14 - 启动流程代码分析之第一个用户态进程:init 进程 之 post-init 任务详解》
- 《【鸿蒙OS开发入门】15 - 启动流程代码分析之第一个用户态进程:init 进程 之 libuv 做了啥?》
在鸿蒙Kernel OS
目录下,目前支持linux-4.19
,liteos_a
,liteos_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()
中主要工作如下:
- 启动
kernel_init
线程,在线程中启动第一个系统进程init
,如果是在android里面,则是由init来初始化和启动属性服务,并且启动Zygote进程,至于鸿蒙的init
干了啥,现在我也不知道 ,我们后续边看代码边分析,哈哈。 - 通过
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
中看下。
创建
kthreadd
线程,进程号为2
,并获取kkthread
的task struct
保存在kthreadd_task
中,kthreadd
用于处理scheule
调度相关事宜,负责所有内核线程的调度和管理
它的任务就是管理和调度其他内核线程kernel_thread
, 会循环执行一个kthreadd
的函数,该函数的作用就是运行kthread_create_list
全局链表中维护的kthread
, 当我们调用kernel_thread
创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd
为父进程kernel_init
线程启动后,刚开始一直处于等待状态,当kthreadd
线程创建好后,会置位标志位kthreadd_done
,让处于等待中的kernel_init
继续运行至此,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
主要干了啥?
调用
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) 初始化共享内存
shmem
、devtmpfs
、buses
、classes
、firmware
、hypervisor
、平台驱动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
进程- (5.1) 初始化共享内存
等待所有
__init
的函数运行完毕,然后释放__init
占用的空间及jump
占用的空间至此系统算初始化完毕了,配置系统状态
system_state
为SYSTEM_RUNNING
最终调用
run_init_process
启动/init
进程,很明显linux为了避免各位开发者乱放init或者定制为其他名字,默认是/init
,当然可以放在/sbin/init、/bin/init/、/etc/init
,如果配置execute_command
运行其他的程序,如果这些都没有,那就启动/bin/sh
。
至此,留下/init进程这个子嗣后,我们也该和kernel_init
线程说再见了,kernel内核其实最无情了,一jio
把kernel_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递减。
比如:
pure_initcall
优先级最高,为0core_initcall
优先级次之,为1postcore_initcall
优先级次之,为2arch_initcall
优先级次之,为3,用于arch板级信息初始化subsys_initcall
优先级次之,为4,用于各个子系统框架再初始化fs_initcall
优先级次之,为5,用于文件系统fs初如化device_initcall
优先级次之,为6,用于初始化内核驱动模块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进程相关推荐
- 【鸿蒙OS开发入门】18 - HDF驱动子系统:加速度计传感器 Driver层驱动代码分析
[鸿蒙OS开发入门]18 - HDF驱动子系统:加速度计传感器 Driver层代码分析 一.如何添加速度计传感器驱动代码(代码.编译.配置) 1.驱动代码实现 2.驱动编译配置 2.1 linux 编 ...
- 【鸿蒙OS开发入门】16 - 重头搭建Ubuntu新环境编译OpenHarmony 3.0 LTS
[鸿蒙OS开发入门]16 - 重头搭建Ubuntu新环境编译OpenHarmony 3.0 LTSv 一.配置Telnet 和 samba 二.下载code-v3.0-LTS.tar.gz源码 三.配 ...
- 【鸿蒙OS开发入门】13 - 启动流程代码分析之第一个用户态进程:init 进程 之 init 任务详解
[鸿蒙OS开发入门]13 - 启动流程代码分析之第一个用户态进程:init 进程 之 init 任务详解 一. /etc/init.cfg 系统默认cfg:启动lo回环网卡 1.1 init.Hi35 ...
- 鸿蒙APP开发入门到进阶 | 入门完整篇。
大家好,我是 码工,一个有十年工作经验的码农,一心追求技术. 先说说我的开发经历,10年毕业,到目前已经十多年开发经验,大学学习了多种语言,Java,Android,c,c++,到毕业时真正掌握了几乎 ...
- ZYNQ启动流程之分析BootRoM
ZYNQ启动流程之分析BootRoM 一.fsbl由谁来启动 1.背景 2.需要做的事 3.这些事由谁来做----BootRoM 二.何为BootRoM 三.SD卡启动方式 一.fsbl由谁来启动 1 ...
- U-BOOT启动流程详细分析[转]
http://www.cnblogs.com/heaad/archive/2010/07/17/1779829.html U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶 ...
- PCIe学习笔记之pcie初始化枚举和资源分配流程代码分析
本文主要是对PCIe的初始化枚举.资源分配流程进行分析.代码对应的是linux 4.19, 平台是arm64. 文章首发于这里 1. PCIe architecture 1.1 pcie的拓扑结构 在 ...
- 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析
[Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...
- USB NCM usbnet 枚举流程代码分析
USB NCM usbnet 枚举流程代码分析 一.cdc_ncm.c 1.1 [id_table]struct usb_device_id结构体 1.1.1 match_flags 设备类型 1.1 ...
最新文章
- CS231n课程笔记翻译
- oracle 测试试题,oracle试题
- Leetcode刷题系列汇总
- Redis 安装部署介绍
- RSA加解密VS加签与验签
- Windows无法连接到打印机、打印机连接出现0X00000bcb错误应该如何解决?这应该是是最全面的解决方法啦~~
- 最新批量搜狗域名添加绑定工具
- java编译的类包含美元符号 $
- python下载第三方库超时报错pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host=‘files.pyt
- 石大师装机大师怎么重装系统
- (超详细)大数据Hadoop之HDFS组件
- SQL如何进行帕累托分析?(窗口函数、累计百分比分类)
- PTA - 数据库合集27
- 穿越火线枪战王者服务器维护,CF手游体验服6.10更新维护公告 六月版本来袭
- css渐变斑马条纹_创建斑马条纹表
- Kubernetes安装系列之coredns安装
- Android默认短信应用常识
- Hbase安装流程及踩坑心得
- Android 使用自带的MediaCodec 框架进行本地视频压缩
- 第三课 用OD脱壳的基本方法