zoukankan      html  css  js  c++  java
  • DTB:内核中 xxx_initcall 的调用过程分析

    内核版本:linux-4.19

    上一篇文章提到了这段代码:

    arch_initcall_sync(of_platform_default_populate_init);
    

    它的功能是完成 device_node 到 platform_device 的转换。这篇文章就来大概的分析一下,它是怎样被调用的。

    arch_initcall_sync 定义如下:

    #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)
    
    #define arch_initcall_sync(fn)		__define_initcall(fn, 3s)
    

    根据 LDS 文件的定义,会将这些 data 存储在指定的位置:

    #define INIT_CALLS_LEVEL(level)						
    		__initcall##level##_start = .;				
    		KEEP(*(.initcall##level##.init))			
    		KEEP(*(.initcall##level##s.init))			
    
    #define INIT_CALLS							
        	__initcall_start = .;					
        	KEEP(*(.initcallearly.init))				
        	INIT_CALLS_LEVEL(0)					
        	INIT_CALLS_LEVEL(1)					
        	INIT_CALLS_LEVEL(2)					
        	INIT_CALLS_LEVEL(3)					
        	INIT_CALLS_LEVEL(4)					
        	INIT_CALLS_LEVEL(5)					
        	INIT_CALLS_LEVEL(rootfs)				
        	INIT_CALLS_LEVEL(6)					
        	INIT_CALLS_LEVEL(7)					
        	__initcall_end = .;	
    

    在内核中,想要调用到这些数据,就会用到

    __initcall##level##_start = .;
    

    这个标识。

    数据的位置已经搞定了,那么内核又是怎样调用的呢?接下来找到这些函数调用。

    调用流程如下:

    start_kernel
    	-->rest_init
    		-->kernel_thread(kernel_init, NULL, CLONE_FS);
    			-->kernel_init
    				-->kernel_init_freeable
    					-->do_basic_setup
    						-->do_initcalls
    							-->do_initcall_level
    

    do_initcall_level 代码如下:

    static void __init do_initcall_level(int level)
    {
    	initcall_entry_t *fn;
    
    	strcpy(initcall_command_line, saved_command_line);
    	parse_args(initcall_level_names[level],
    		   initcall_command_line, __start___param,
    		   __stop___param - __start___param,
    		   level, level,
    		   NULL, &repair_env_string);
    
    	trace_initcall_level(initcall_level_names[level]);
    	for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
    		do_one_initcall(initcall_from_entry(fn));
    }
    

    根据下表,分别调用 do_one_initcall 来执行每一个 initcall。

    static initcall_entry_t *initcall_levels[] __initdata = {
    	__initcall0_start,
    	__initcall1_start,
    	__initcall2_start,
    	__initcall3_start,
    	__initcall4_start,
    	__initcall5_start,
    	__initcall6_start,
    	__initcall7_start,
    	__initcall_end,
    };
    

    表中的 xxx_start 恰恰就是前面所提到的存储标识,那么这些调用就联系到了一起,实现函数的调用。

    分析到这里,相信关于 xxx_initcall 调用的云雾就已经拨开了吧!

  • 相关阅读:
    idea 连接 hive
    css img自适应
    测试视频文件
    ubuntu不显示ipv4地址的解决办法
    nginx path捕获
    union all两个结果集报ORA-12704: character set mismatch错误
    润乾报表试用指南
    报表工具对比之润乾报表与锐浪报表对比
    项目微管理36
    docker远程调用
  • 原文地址:https://www.cnblogs.com/GyForever1004/p/10400276.html
Copyright © 2011-2022 走看看