1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 【鸿蒙OS开发入门】06 - 启动流程代码分析之KernelOS:之启动Linux-4.19 Kernel内核 启动init进程

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

时间:2023-02-04 20:36:58

相关推荐

【鸿蒙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.c2.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.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:bstext// branch to kernel start, magicENTRY(stext)blpreserve_boot_argsblel2_setup// Drop to EL1, w0=cpu_boot_modeadrpx23, __PHYS_OFFSETandx23, x23, MIN_KIMG_ALIGN - 1// KASLR offset, defaults to 0blset_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_switchENDPROC(stext)__primary_switch:#ifdef CONFIG_RANDOMIZE_BASEmovx19, x0// preserve new SCTLR_EL1 valuemrsx20, sctlr_el1// preserve old SCTLR_EL1 value#endifbl__enable_mmu#ifdef CONFIG_RELOCATABLEbl__relocate_kernel#ifdef CONFIG_RANDOMIZE_BASEldrx8, =__primary_switchedadrpx0, __PHYS_OFFSETblrx8/** 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_workaroundmsrsctlr_el1, x20// disable the MMUisbbl__create_page_tables// recreate kernel mappingtlbivmalle1// Remove any stale TLB entriesdsbnshmsrsctlr_el1, x19// re-enable the MMUisbiciallu// flush instructions fetcheddsbnsh// via old mappingisbbl__relocate_kernel#endif#endifldrx8, =__primary_switchedadrpx0, __PHYS_OFFSETbrx8ENDPROC(__primary_switch)/** The following fragment of code is executed with the MMU enabled.** x0 = __PHYS_OFFSET*/__primary_switched:adrpx4, init_thread_unionaddsp, x4, #THREAD_SIZEadr_lx5, init_taskmsrsp_el0, x5// Save thread_infoadr_lx8, vectors// load VBAR_EL1 with virtualmsrvbar_el1, x8// vector table addressisbstpxzr, x30, [sp, #-16]!movx29, spstr_lx21, __fdt_pointer, x5// Save FDT pointerldr_lx4, kimage_vaddr// Save the offset betweensubx4, x4, x0// the kernel virtual andstr_lx4, kimage_voffset, x5// physical mappings// Clear BSSadr_lx0, __bss_startmovx1, xzradr_lx2, __bss_stopsubx2, x2, x0bl__pi_memsetdsbishst// Make zero page visible to PTW#ifdef CONFIG_KASANblkasan_early_init#endif#ifdef CONFIG_RANDOMIZE_BASEtstx23, ~(MIN_KIMG_ALIGN - 1)// already running randomized?b.ne0fmovx0, x21// pass FDT address in x0blkaslr_early_init// parse FDT for KASLR optionscbzx0, 0f// KASLR disabled? just proceedorrx23, x23, x0// record KASLR offsetldpx29, x30, [sp], #16// we must enable KASLR, returnret// to __primary_switch()0:#endifaddsp, sp, #16movx29, #0movx30, #0bstart_kernelENDPROC(__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.cstruct 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,并获取kkthreadtask 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.cstatic 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) 初始化共享内存shmemdevtmpfsbusesclassesfirmwarehypervisor、平台驱动platform_bus等初始化,这个函数干的都是kernel的一些大的子系统,我们后续单独列一章分析。(5.2) 注册及初始化irq中断的proc文件系统(5.3) 调用kernel constructor functionkernel构造函数,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进程

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

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

最终调用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递减。

比如:

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

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。