Linux内核模块(Module)初解
#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)初解的更多相关文章
- 5.linux内核模块基础,内核模块学习
linux内核模块基础 一.定义 Linux 内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢: 方法 1:把所有的组件都编译进内核文件,即:zImage 或 bzImage,但这样会 ...
- Linux内核模块简介
一. 摘要 这篇文章主要介绍了Linux内核模块的相关概念,以及简单的模块开发过程.主要从模块开发中的常用指令.内核模块程序的结构.模块使用计数以及模块的编译等角度对内核模块进行介绍.在Linux系统 ...
- Smart210学习记录-------linux内核模块
Linux 驱动工程师需要牢固地掌握 Linux 内核的编译方法以为嵌入式系统构建可运行的Linux 操作系统映像.在编译 LDD6410 的内核时,需要配置内核,可以使用下面命令中的 一个: #ma ...
- linux内核模块相关命令:lsmod,depmod,modprobe,modinfo,insmod,rmmod 使用说明
加载内核驱动的通常流程: 1.先将.ko文件拷贝到/lib/module/`uname -r`(内核版本号)/kernel/driver/...目录下, 根据具体用途的区别分为net.ide.scsi ...
- Linux内核模块编程——Hello World模块
Linux内核模块编程 编程环境 Ubuntu 16.04 LTS 什么是模块 内核模块的全称是动态可加载内核模块(Loadable Kernel Modul,KLM),可以动态载入内核,让它成为内核 ...
- linux内核模块的安全
linux可以动态的加载内核模块,在很多场合可能需要确保加载内核的安全性.如果被攻击者加载恶意内核模块,将会使得内核变得极其危险. 当然,稳妥的做法就是给内核模块进行签名,内核只加载能正确验证的签名. ...
- Linux内核模块编写详解
内核编程常常看起来像是黑魔法,而在亚瑟 C 克拉克的眼中,它八成就是了.Linux内核和它的用户空间是大不相同的:抛开漫不经心,你必须小心翼翼,因为你编程中的一个bug就会影响到整个系统,本文给大家介 ...
- Linux内核模块编程与内核模块LICENSE -《具体解释(第3版)》预读
Linux内核模块简单介绍 Linux内核的总体结构已经很庞大,而其包括的组件或许多.我们如何把须要的部分都包括在内核中呢?一种方法是把全部须要的功能都编译到Linux内核.这会导致两个问题.一是生成 ...
- linux内核模块编译makefile
linux内核可加载模块的makefile 在开发linux内核驱动时,免不了要接触到makefile的编写和修改,尽管网上的makefile模板一大堆,做一些简单的修改就能用到自己的项目上,但是,对 ...
随机推荐
- 从输入URL到页面加载到底发生了什么
很多初学网络或者前端的初学者大多会有这样一个疑问:从输入URL到页面加载完成到底发生了什么?总的来说,这个过程分为下面几个步骤:1.DNS解析2.与服务器建立连接3.服务器处理并返回http报文4.浏 ...
- 造个自己的Vue的UI组件库类似Element
前言 随着前端的三大框架的出现,组件化的思想越来越流行,出现许多组件库.它能够帮助开发者节省时间提高效率, 如React的Ant-design,Vue的iView,Element等,它们的功能已经很完 ...
- Array 和 ArrayList 、 List 以及 LinkedList 的区别
下面列出了Array(数组)和ArrayList(集合)的不同点: Array可以包含基本类型和对象类型,ArrayList只能包含对象类型. Array大小是固定的,ArrayList的大小是动态变 ...
- NOIP2017 D1T1 小凯的疑惑
洛谷P3951 看到题目,很容易想到这一题是求使ax+by=c(a,b,c∈N)无非负整数解的最大c 由裴蜀定理可知方程一定有整数解(a,b互素,gcd(a,b)=1|c) 解法一:暴力枚举 看到题目 ...
- H5 图片上传
1.h5 图片异步上传 (1) 异步上传input触发onchange事件的时候,就把图片上传至服务器.后台可能会返回图片的链接等信息,前台可以把图片信息展示给用户看. (2) 另一种情况可能需要前台 ...
- 线程协作之threading.Condition
领会下面这个示例吧,其实跟java中wait/nofity是一样一样的道理 import threading # 条件变量,用于复杂的线程间同步锁 """ 需求: 男:小 ...
- React native 之 Promise
关键词:Promise Promise.all Promise是什么?=> https://www.runoob.com/w3cnote/es6-promise.html Promise.all ...
- CSS中的自适应单位vw、vh、vmin、vmax
1.vw.vh.vmin.vmax各单位的意义 上面的自适应单位可以统称为视口单位. 可以先了解一下视口指的是什么? 在PC端,视口指的是在PC端,指的是浏览器的可视区域:而在移动端,它涉及3个视口: ...
- ipcloud上传裁切图片
主页: <!doctype html> <html> <head> <meta charset="utf-8"> <meta ...
- [IOI2008] Fish 鱼
https://www.luogu.org/recordnew/lists?uid=56840 题解 首先可以发现我们对于每种颜色的鱼,长一点的能够覆盖的方案已定完全包含短一点的方案. 所以我们可以只 ...