#include <linux/init.h>                     // __init   __exit
#include <linux/module.h> // module_init module_exit static int __init hello_init(void)
{
printk(KERN_ALERT "helloworld!\n");
return ;
} static __exit void hello_exit(void)
{
printk(KERN_ALERT "helloworld exit!\n");
} module_init(hello_init);
module_exit(hello_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Musk <qq:739112417>");
MODULE_DESCRIPTION("A Simple Hello World");
MODULE_ALIAS("A simplest module");

一. 分析module_init宏定义

1.1.   module_init宏被定义在kernel/include/linux/init.h文件里

#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn);
#define device_initcall(fn) __define_initcall(fn, 6);
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn

1.2.    module_init(hello_init)深入分析宏定义:

#define module_init(hello_init) __initcall(hello_init);
#define __initcall(hello_init) device_initcall(hello_init);
#define device_initcall(hello_init) __define_initcall(hello_init, 6);
#define __define_initcall(hello_init, 6) \
static initcall_t __initcall_hello_init6 __used \
__attribute__((__section__(".initcall6.init"))) = hello_init

1.3.   其中static initcall_t 这里 initcall_t的定义是 : typedef int           (*initcall_t)(void)   这里决定了hello_init函数类型。

上面大概的意思就是定义一个指向函数的指针,以hello_init为例,就是定义了__initcall_hello_init6,把hello_init函数的指针赋给它

并把__initcall_hello_init6放到在vmlinux.lds文件里边指定的.initcall6.init区间里边

1.4.   这里再介绍一下像int __init hello_init(void) { }这种用包含在module_init里边的函数定义,函数名字前面加上__init到底什么意                  思。

__init的定义是 #define __init __section(.init.text) __cold notrace

在init.h中对这些宏的说明如下:

/* These macros are used to mark some functions or
* initialized data (doesn't apply to uninitialized data)
* as `initialization' functions. The kernel can take this
* as hint that the function is used only during the initialization
* phase and free up used memory resources after
*
* Usage:
* For functions:
*
* You should add __init immediately before the function name, like:
*
* static void __init initme(int x, int y)
* {
* extern int z; z = x * y;
* }
*
* If the function has a prototype somewhere, you can also add
* __init between closing brace of the prototype and semicolon:
*
* extern int initialize_foobar_device(int, int, int) __init;
*
* For initialized data:
* You should insert __initdata between the variable name and equal
* sign followed by value, e.g.:
*
* static int init_variable __initdata = 0;
* static const char linux_logo[] __initconst = { 0x32, 0x36, ... };
*
* Don't forget to initialize data not at file scope, i.e. within a function,
* as gcc otherwise puts the data into the bss section and not into the init
* section.
*
* Also note, that this data cannot be "const".
*/
/* These are for everybody (although not all archs will actually
discard it in modules) */
#define __init __section(.init.text) __cold notrace //__cold 和notrace具体干嘛的?
#define __initdata __section(.init.data)
#define __initconst __constsection(.init.rodata)
#define __exitdata __section(.exit.data)
#define __exit_call __used __section(.exitcall.exit)

就是说如果某个函数或者变量只在初始化的时候用到,后面可以被清理掉也        没有关系的话,就最好定义成上面的一种,以便释放更多空间。

这里具体怎么释放加了上面定义的的函数和变量? 应该是放到了特定区域里        边一起释放,具体后面再说。

还有这种__init定义方式需要注意,不要用在函数里边的

二. 模块操作命令

2.1. 常用模块操作命令

2.1.1. lsmod(list module,将模块列表显示),功能是打印出当前内核中已经        安装的模块列表

2.1.2. insmod(install module,安装模块),功能是向当前内核中去安装        一个模块,用法是insmod xxx.ko

2.1.3. modinfo(module information,模块信息),功能是打印出一个内        核模块的自带信息。

用法是modinfo xxx.ko,注意要加.ko,也就是说是一个静态的文件形式

2.1.4. rmmod(remove module,卸载模块),功能是从当前内核中卸载        一个已经安装了的模块,

用法是rmmod xxx.ko  rmmod xxx都可以

2.2. 模块的安装

2.2.1. insmod与module_init宏。模块源代码中用module_init宏声明了一        个函数(在我们这个例子里是hello_init函数),

作用就是指定hello_init这个函数和insmod命令绑定起来,也就是说当我们            insmod xxx.ko时,insmod命令内部实际执行的操作就是帮我们调用hello_init        函数。

2.2.2. 模块安装时insmod内部除了帮我们调用module_init宏所声明的函数        外,实际还做了一些别的事(譬如lsmod能看到多了一个模块也是insmod帮我        们在内部做了记录,

也就是将我们的模块加入到内核的一个数据结构中去),但是我们就不用管了

2.3. 模块的卸载

2.3.1. module_exit和rmmod的对应关系当我们执行rmmod命令的时候,        就会执行模块的module_exit宏声明的函数,

同样也会将我们这个模块信息从我们内核的模块管理的数据结构中将其删除

2.4. 模块的版本信息

2.4.1.  使用modinfo查看模块的版本信息

2.4.2. 内核zImage中也有一个确定的版本信息

2.4.3. insmod时模块的vermagic必须和内核的相同,否则不能安装,报错        信息为:insmod: ERROR: could not insert module module_test.ko: Invalid     module format

2.4.4. 模块的版本信息是为了保证模块和内核的兼容性,是一种安全措施

2.4.5. 如何保证模块的vermagic和内核的vermagic一致?编译模块的内核源        码树就是我们编译正在运行的这个内核的那个内核源码树即可。说白了就是模        块和内核要同出一门

2.5. 模块中常用宏

2.5.1. MODULE_LICENSE("GPL"),模块的许可证。一般声明为GPL许证,而且最好不要少,否则可能会出现莫名其妙的错误(譬如一些明显存在            的函数提升找不到)。

2.5.2. MODULE_AUTHOR(Musk <qq:739112417> "),用来添加模块的作者信息

2.5.3. MODULE_DESCRIPTION("xxx"),用来添加模块的描述信息

2.5.4. MODULE_ALIAS("xxxx"),用来添加模块的别名

2.6. printk函数详解

2.6.1.printk在内核源码中用来打印信息的函数,用法和printf非常相似

2.6.2. printk和printf最大的差别:printf是C库函数,是在应用层编程中使用        的,不能在linux内核源代码中使用;printk是linux内核源代码中自己封装出来的        一个打印函数,是内核源码中的一个普通函数,只能在内核源码范围内使用,不        能在应用编程中使用

2.6.3. printk相比printf来说还多了个:打印级别的设置。printk的打印级别        是用来控制printk打印的这条信息是否在终端上显示的。应用程序中的调试信        息要么全部打开要么全部关闭,一般用条件编译来实现(DEBUG宏),但是在内核中,因为内核非常庞大,打印信息非常多,有时候整体调试内核时打印信息要么太多找不到想要的要么一个没有没法调试。所以才有了打印级别这个概念

2.6.4. 命令行设置级别的信息会被放行打印出来,大于的就被拦截的。譬如我的ubuntu中的打印级别默认是4,那么printk中设置的级别比4小的就能打印出来,比4大的就不能打印出来.可以通过这个命令查看 :  cat /proc/sys/kernel/printk。

2.6.4. ubuntu中这个printk的打印级别控制没法实践,ubuntu中不管你把级别怎么设置都不能直接打印出来,必须dmesg命令去查看。

2.7. 关于驱动模块中的头文件

驱动源代码中包含的头文件和原来应用编程程序中包含的头文件不是一回事。应用编程中包含的头文件是应用层的头文件,是应用程序的编译器带来的(譬如gcc的头文件路径在 /usr/include下,这些东西是和操作系统无关的)。驱动源码属于内核源码的一部分,驱动源码中的头文件其实就是内核源代码目录下的所有include目录下的头文件。

2.8. 用开发板调试模块

2.8.1. 设置bootcmd使开发板通过tftp下载自己建立的内核源码树编译得到的zImage:  set bootcmd 'tftp 0x30008000 zImage;bootm 0x30008000'

2.8.2. 设置bootargs使开发板从nfs去挂载rootfs(内核配置时需要使其支持挂载NFS文件系统,这个在uboot中已经说过)setenv bootargs root=/dev/nfs nfsroot=192.168.1.141:/root/porting_x210/rootfs/rootfs ip=192.168.1.10:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off  init=/linuxrc console=ttySAC2,115200

2.8.3. 修改Makefile中的KERN_DIR使其指向自己建立的内核源码树

2.8.4. 将自己编译好的驱动.ko文件放入nfs共享目录下去

2.8.5 开发板启动后使用insmod、rmmod、lsmod等去进行模块实验

索引文献:https://www.cnblogs.com/deng-tao/p/6165573.html

索引文献:https://blog.csdn.net/hongzg1982/article/details/54836465

Linux内核模块(Module)初解的更多相关文章

  1. 5.linux内核模块基础,内核模块学习

    linux内核模块基础 一.定义 Linux 内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢: 方法 1:把所有的组件都编译进内核文件,即:zImage 或 bzImage,但这样会 ...

  2. Linux内核模块简介

    一. 摘要 这篇文章主要介绍了Linux内核模块的相关概念,以及简单的模块开发过程.主要从模块开发中的常用指令.内核模块程序的结构.模块使用计数以及模块的编译等角度对内核模块进行介绍.在Linux系统 ...

  3. Smart210学习记录-------linux内核模块

    Linux 驱动工程师需要牢固地掌握 Linux 内核的编译方法以为嵌入式系统构建可运行的Linux 操作系统映像.在编译 LDD6410 的内核时,需要配置内核,可以使用下面命令中的 一个: #ma ...

  4. linux内核模块相关命令:lsmod,depmod,modprobe,modinfo,insmod,rmmod 使用说明

    加载内核驱动的通常流程: 1.先将.ko文件拷贝到/lib/module/`uname -r`(内核版本号)/kernel/driver/...目录下, 根据具体用途的区别分为net.ide.scsi ...

  5. Linux内核模块编程——Hello World模块

    Linux内核模块编程 编程环境 Ubuntu 16.04 LTS 什么是模块 内核模块的全称是动态可加载内核模块(Loadable Kernel Modul,KLM),可以动态载入内核,让它成为内核 ...

  6. linux内核模块的安全

    linux可以动态的加载内核模块,在很多场合可能需要确保加载内核的安全性.如果被攻击者加载恶意内核模块,将会使得内核变得极其危险. 当然,稳妥的做法就是给内核模块进行签名,内核只加载能正确验证的签名. ...

  7. Linux内核模块编写详解

    内核编程常常看起来像是黑魔法,而在亚瑟 C 克拉克的眼中,它八成就是了.Linux内核和它的用户空间是大不相同的:抛开漫不经心,你必须小心翼翼,因为你编程中的一个bug就会影响到整个系统,本文给大家介 ...

  8. Linux内核模块编程与内核模块LICENSE -《具体解释(第3版)》预读

    Linux内核模块简单介绍 Linux内核的总体结构已经很庞大,而其包括的组件或许多.我们如何把须要的部分都包括在内核中呢?一种方法是把全部须要的功能都编译到Linux内核.这会导致两个问题.一是生成 ...

  9. linux内核模块编译makefile

    linux内核可加载模块的makefile 在开发linux内核驱动时,免不了要接触到makefile的编写和修改,尽管网上的makefile模板一大堆,做一些简单的修改就能用到自己的项目上,但是,对 ...

随机推荐

  1. 转 git 本地文件添加远程git

    好的博客膜拜一下 https://www.liaoxuefeng.com/wiki/896043488029600/898732864121440 现在的情景是,你已经在本地创建了一个Git仓库后,又 ...

  2. java web中乱码的种类和一些解决方式

    在java web课堂测试中遇到了一些乱码问题 ,从百度上找到了许多种解决方法和乱码的种类,在这里总结一下. 一.文件出现乱码 [右击文件]->[Properties]->[Resourc ...

  3. 决策树剪枝算法-悲观剪枝算法(PEP)

    前言 在机器学习经典算法中,决策树算法的重要性想必大家都是知道的.不管是ID3算法还是比如C4.5算法等等,都面临一个问题,就是通过直接生成的完全决策树对于训练样本来说是“过度拟合”的,说白了是太精确 ...

  4. linux运维、架构之路-KVM虚拟化技术

    一.云计算概述 云计算:是一种资源使用和交付模式 虚拟化:一种具体的技术,用来将物理机虚拟成为多个相互独立的虚拟机.云计算不等于虚拟化,云计算是使用了虚拟化的技术做支撑 二.KVM配置使用 1.系统环 ...

  5. Quick BI的SQL传参建模可以用在什么场景

    Quick B的SQL传参建模功能提供基于SQL的数据加工处理能力,减轻了IT支撑人员的工作量.在即席查询SQL中,我们用物理字段显示别名来表示参数的占位符,配置完占位符后,就可以在查询控件中进行参数 ...

  6. POJ 3046 Ant Counting ( 多重集组合数 && 经典DP )

    题意 : 有 n 种蚂蚁,第 i 种蚂蚁有ai个,一共有 A 个蚂蚁.不同类别的蚂蚁可以相互区分,但同种类别的蚂蚁不能相互区别.从这些蚂蚁中分别取出S,S+1...B个,一共有多少种取法. 分析 :  ...

  7. 【PowerOJ1753&网络流24题】分配问题(KM)

    题意: 思路:费用流可做 最好的算法是KM板子 #include<bits/stdc++.h> using namespace std; typedef long long ll; typ ...

  8. Bellman-ford算法与SPFA算法思想详解及判负权环(负权回路)

    我们先看一下负权环为什么这么特殊:在一个图中,只要一个多边结构不是负权环,那么重复经过此结构时就会导致代价不断增大.在多边结构中唯有负权环会导致重复经过时代价不断减小,故在一些最短路径算法中可能会凭借 ...

  9. input 的 type 等于 file

    高版本浏览器由安全问题没法获得文件的绝对路径, 因此使用浏览器自制播放器只能使用其他的手段实现. 使用相对路径, 把浏览器与文件放在同一路径下即可使用.通用性受到限制.

  10. 使用html5中required属性

    直接上代码 <form action="${pageContext.request.contextPath}/login/main.do" method="post ...