怎么使用Linux内核模块

技术怎么使用Linux内核模块这篇文章主要介绍“怎么使用Linux内核模块”,在日常操作中,相信很多人在怎么使用Linux内核模块问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么使用Li

这篇文章主要介绍"怎么使用Linux操作系统操作系统内核模块",在日常操作中,相信很多人在怎么使用Linux操作系统操作系统内核模块问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"怎么使用Linux操作系统操作系统内核模块"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一、模块定义

Linux内核使用xx_initcall_xx(fn)的宏来定义内核模块。

宏定义文件:包括/linux/init.h,定义如下:

/* * earlynitcallsrundisignizingsmp .* *仅适用于内置模块,不适用于模块*/# define _ early _ init call(fn)_ define _ init call(fn,early)/* *一个‘纯’的init callhasnodependenciesonanythingelse,和urely * initializesvariablethakouldn ' tbestaticalinitialized .* *此唯一存在或内置于代码中,而不是模块中保持主要。c :初始化调用级别名称[]同步.*/#定义纯_ init调用(fn)_定义_ init调用(fn,0)#定义core _ init调用(fn)_定义_ init调用(fn,1)#定义core _ init调用_ sync(fn)_定义_ init调用(fn,1s)#定义epostcore _ init调用(fn,2)#定义epostcore _ init调用_ sync(fn)_定义_ init调用(fn,2s)#定义搜索

nbsp;  __define_initcall(fn, 6s)#define late_initcall(fn)       __define_initcall(fn, 7)#define late_initcall_sync(fn)      __define_initcall(fn, 7s)

其中:early_initcall(fn)只针对内核的核心代码,不能描述模块。

从上面代码可以看出,每个宏的实现都是__define_initcall(),其定义如下:

#define __define_initcall(fn, id) \    static initcall_t __initcall_##fn##id __used \    __attribute__((__section__(".initcall" #id ".init"))) = fn; \    LTO_REFERENCE_INITCALL(__initcall_##fn##id)    typedef int (*initcall_t)(void);

对于上面定义,需要关注以下几点:

1、initcall_t:是一个函数指针类型,定义__initcall_##fn##id,指向fn

2、__used:在文件include/linux/compiler-gcc.h中定义为:# define __used __attribute__((__used__)),通知编译器在目标文件中保留一个静态函数,即使该函数未被使用。

3、__attribute__((__section__(".initcall" #id ".init"))):定义的函数指针位于.initcall*.init段中。

二、链接位置

在文件include/asm-generic/vmlinux.lds.h中,定义了宏INIT_CALLS,定义如下:

#define INIT_CALLS_LEVEL(level)                     \        VMLINUX_SYMBOL(__initcall##level##_start) = .;      \        *(.initcall##level##.init)              \        *(.initcall##level##s.init)             \#define INIT_CALLS                          \        VMLINUX_SYMBOL(__initcall_start) = .;           \        *(.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)                 \        VMLINUX_SYMBOL(__initcall_end) = .;

在文件arch/arm64/kernel/vmlinux.lds.S 中,设置了宏INIT_CALLS.init.data段中的分布位置,内容如下:

    .init.data : {        INIT_DATA        INIT_SETUP(16)        INIT_CALLS        CON_INITCALL        SECURITY_INITCALL        INIT_RAM_FS    }

在Linux内核编译链接后,会生成文件arch/arm64/kernel/vmlinux.lds。在该文件中将展开宏INIT_CALLS,并分配到.init.data段中,内容如下:

 .init.data : {...  __initcall_start = .; *(.initcallearly.init) __initcall0_start = .; *(.initcall0.init) *(.initcall0s.init) __initcall1_start = .; *(.initcall1.init) *(.initcall1s.init) __initcall2_start = .; *(.initcall2.init) *(.initcall2s.init) __initcall3_start = .; *(.initcall3.init) *(.initcall3s.init) __initcall4_start = .; *(.initcall4.init) *(.initcall4s.init) __initcall5_start = .; *(.initcall5.init) *(.initcall5s.init) __initcallrootfs_start = .; *(.initcallrootfs.init) *(.initcallrootfss.init) __initcall6_start = .; *(.initcall6.init) *(.initcall6s.init) __initcall7_start = .; *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;... }

注:在__initcall_start__initcall_end之间是按照从0到7的顺序进行排列,对应pure_initcall(fn)late_initcall_sync(fn)

三、模块加载

Linux内核模块可以直接编译到内核映像,在内核启动时加载;也可以在系统启动后,通过insmod命令加载到内核。

Linux内核启动时,kernel_init线程会实现静态编译的内核模块加载。

1、程序调用流程

## kernel/init/main.cstart_kernel()->    rest_init()->        kernel_thread(kernel_init, NULL, CLONE_FS) ## 1.创建内核线程kernel_init()->    kernel_init_freeable()->        do_basic_setup()->            do_initcalls()->      ## 2.模块加载过程                do_initcall_level()->                    do_one_initcall()

2、do_initcalls()函数

函数功能:按顺序扫描.init.data段中的每个等级,即:从__initcall0_start__initcall_end

## __initdata中的定义在static initcall_t *initcall_levels[] __initdata = {    __initcall0_start,     __initcall1_start,    __initcall2_start,    __initcall3_start,    __initcall4_start,    __initcall5_start,    __initcall6_start,    __initcall7_start,    __initcall_end,};static void __init do_initcalls(void){    int level;    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)        do_initcall_level(level);}

3、do_initcall_level()函数

函数功能:在同一个等级中,按顺序扫描.initcall*.init.initcall*s.init

static void __init do_initcall_level(int level){    ...    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)        do_one_initcall(*fn);}

4、do_one_initcall()函数

函数功能:调用每个定义的模块函数。

int __init_or_module do_one_initcall(initcall_t fn){    ...    if (initcall_debug)        ret = do_one_initcall_debug(fn);    else        ret = fn(); ##调用某个定义的initcall函数    ...    return ret;}

注:每个fn()对应第一部分模块定义中的fn,例:device_initcall(fn)

四、模块优先级

从上面函数的执行流程可以看出内核模块加载优先级如下:

early_initcall(fn)               ## 优先级最高,后续优先级依次降低pure_initcall(fn)core_initcall(fn)core_initcall_sync(fn)postcore_initcall(fn)postcore_initcall_sync(fn)arch_initcall(fn)arch_initcall_sync(fn)subsys_initcall(fn)subsys_initcall_sync(fn)fs_initcall(fn)fs_initcall_sync(fn)rootfs_initcall(fn)device_initcall(fn)device_initcall_sync(fn)late_initcall(fn)late_initcall_sync(fn)          ## 优先级最低

Linux内核模块的优先级决定了模块的加载顺序,在驱动开发时,需要关注有启动顺序要求的模块定义。

到此,关于“怎么使用Linux内核模块”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/113132.html

(0)

相关推荐

  • flinkstream flinksql 优缺点(flink sql和datastream的区别)

    技术如何正确使用FlinkStreamSQL本篇文章为大家展示了如何正确使用FlinkStreamSQL,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、前期准备项目路径:htt

    攻略 2021年12月23日
  • 租用美国服务器建站的5个注意点美国服务器

    技术租用美国服务器建站的5个注意点美国服务器虽然在租用美国服务器的时候,不同的网站需要的配置也不一样,但是无论有什么特殊需求,每个网站所有者都应该在美国服务器租用中坚持五件事。 1. 高水平的正常运行时间 对于以提供内容

    礼包 2021年10月20日
  • eclipse怎样配置mybatis(eclipsemybatis插件推荐)

    技术Eclipse的插件MyBatis Editor有什么用这篇文章将为大家详细讲解有关Eclipse的插件MyBatis Editor有什么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所

    攻略 2021年12月15日
  • MongoDB基本查询条件操作符都有哪些

    技术MongoDB基本查询条件操作符都有哪些MongoDB基本查询条件操作符都有哪些,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。简介Mon

    攻略 2021年11月3日
  • 如何将后端baas化(baas替代后端程序员)

    技术怎样将后端BaaS化怎样将后端BaaS化,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。BaaS 化的核心其实就是把我们的后端应用封装成 RESTfu

    攻略 2021年12月21日
  • 如何进行JavaScript数据扁平化分析

    技术如何进行JavaScript数据扁平化分析如何进行JavaScript数据扁平化分析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。什么是

    攻略 2021年12月4日