zoukankan      html  css  js  c++  java
  • 驱动模块(2)——模块信息与调试

    一、查看内核模块信息

    相关命令:modprobe、insmod、rmmod、modinfo、lsmod

    1.查看内核所有内置模块
    # cat /lib/modules/$(uname -r)/modules.builtin

    kernel/arch/arm64/crypto/sha1-ce.ko
    kernel/arch/arm64/crypto/sha2-ce.ko
    kernel/arch/arm64/crypto/ghash-ce.ko
    kernel/arch/arm64/crypto/aes-ce-cipher.ko
    ......

    2.查看有哪些内置模块
    # grep "=y" /boot/config-$(uname -r) | more

    3.查看内核模块参数和值
    # ls /sys/module/sd8xxx/parameters
    在/sys/module目录下,可以找到内核模块(包含内置和可加载的)命名的子目录。进入每个模块目录,这里有个“parameters”目录,列出了这个模块所有的参数。
    上面示例是要找出sd8xxx模块的参数。
    # cat /sys/module/sd8xxx/parameters/mfg_mode 查看参数mfg_mode的值。

    4.显示模块参数信息
    # modinfo -p sd8xxx 只显示参数

    5.显示模块全部信息
    # modinfo sd8xxx

    filename:       /lib/modules/4.14.35/extra/sd8xxx.ko 模块存放的位置
    license:        GPL
    version:        C546 模块版本
    author:         Marvell International Ltd.
    description:    M-WLAN Driver
    srcversion:     5F78B0AEFAD2117163CB186
    alias:          sdio:c*v02DFd9135*
    depends:        cfg80211,mlan 模块依赖
    name:           sd8xxx
    vermagic:       4.14.35 SMP preempt mod_unload aarch64  匹配内核版本信息
    parm:           cfg80211_drcs:1: Enable DRCS support; 0: Disable DRCS support (int)
    parm:           reg_alpha2:Regulatory alpha2 (charp) 模块参数
    ...

    二、模块声明信息

    MODULE_LICENSE ("GPL"); 许可证申明(最好有)
    MODULE_DESCRIPTION("Hello world Module"); 模块描述(可选)
    MODULE_VERSION("V1.0");模块版本(可选)
    MODULE_ALIAS("a simple module");模块别名(可选)
    Module_param(name,type,perm)Name是模块参数的名称,type是这个参数的类型,Perm是模块参数的访问权限
      type常见值: Bool,int,charp:字符串型
      perm常见值:S_IRUGO:任何用户都对/sys/module中出现的该参数具有读权限, S_IWUSR:允许root用户修改/sys/module中出现的该参数

    EXPORT_SYMBOL(符号名)
    EXPORT_SYMBOL_GPL(符号名)导出只能用于包含GPL许可证的模块
    /proc/kallsyms记录了内核中所有导出的符号的名字与地址(记录输出到系统当中可以给其他模块使用的函数的名字)

    三、内核模块的动态加载

    request_module("sound-slot-%i", unit>>4);表示让linux系统的用户空间调用/sbin/modprobe函数加载名为sound-slot-0.ko模块

    TODO:https://blog.csdn.net/liukun321/article/details/7057442

    四、模块加载次序

    1. 相关宏定义

    // include/linux/init.h
    #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)    /*gpio注册*/
    #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)     /*module_init()*/
    #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 /*exit call是没有等级的*/
    
    #define console_initcall(fn)    ___define_initcall(fn,, .con_initcall)
    #define security_initcall(fn)    ___define_initcall(fn,, .security_initcall)
    
    
    // include/linux/module.h
    #define module_init(x)    __initcall(x);
    #define module_exit(x)    __exitcall(x);

    2. do_initcall_level() 函数只在 do_initcalls() 中被调用一次,也就是说这七个等级的 initcall 没有任何一个等级做了特殊处理,同等对待,只是调用的先后次序不同而已。七个等级的 initcall 都被存放在 initcall_levels 这个数组中了,在 do_initcalls() 中一次性全部调用完。

    static initcall_t *initcall_levels[] __initdata = { //init/main.c
        __initcall0_start,
        __initcall1_start,
        __initcall2_start,
        __initcall3_start,
        __initcall4_start,
        __initcall5_start,
        __initcall6_start,
        __initcall7_start,
        __initcall_end,
    };
    
    static void __init do_initcall_level(int level)
    {
        initcall_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);
    
        for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
            do_one_initcall(*fn);
    }
    
    static void __init do_initcalls(void)
    {
        int level;
        /*这决定了pure/arch/late_initcall()等的先后顺序,越小越先被调用*/
        for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
            do_initcall_level(level);
    }

    3. 一个.c文件中可以有多个module_init()和多个module_exit()。

    4.module_init 的 initcall level 为6,设备树的解析在init call 3 时段。

  • 相关阅读:
    【转载】Dom篇
    【转载】Eclipse自动编译问题
    RabbitMQ
    分布式消息中间件
    分布式限流算法
    分布式限流和熔断
    数据库中间件
    redis 集群原理
    redis 哨兵模式(读写分离)
    redis 和memcache 区别
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/9431601.html
Copyright © 2011-2022 走看看