Linux内核模块学习
注:本文是《Linux设备驱动开发详解:基于最新的Linux 4.0内核 by 宋宝华 》一书学习的笔记,大部分内容为书籍中的内容。
书籍可直接在微信读书中查看:Linux设备驱动开发详解:基于最新的Linux4.0内核-宋宝华-微信读书 (qq.com)
1 简介
模块(Module)具有以下特点:
- 模块本身不编译进内核映像
- 内核加载之后,和其它内核中的部分完全一样。
一个简单的示例:
#include <linux/init.h>
#include <linux/module.h>
static int __init hello_init(void)
{
printk(KERN_INFO "Hello world enter\n");
return 0;
}
module_init(hello_init); //内核加载函数
static void __exit hello_exit(void)
{
printk(KERN_INFO "Hello world exit\n");
}
module_exit(hello_exit); //内核卸载函数
MODULE_AUTHOR("Test Hello");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("A simple Hello World Module");
MODULE_ALIAS("A simple module");
Makefile:
KVERS = $(shell uname -r)
# Kernel modules
obj-m += hello.o
# Specify flags for the module compilation.
#EXTRA_CFLAGS=-g -O0
build: kernel_modules
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
clean:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean
编译,并且插入ko:
$ make
$ sudo insmod hello.ko
插入ko时,可能没有打印信息,可以使用dmesg命令查看:
可以通过lsmod命令查看当前系统插入了哪些ko,lsmod命令查看的结果对应/proc/modules文件,内核中已经加载模块信息也存在于/sys/module目录中:
$ lsmod | grep "hello"
Module Size Used by
hello 12425 0
$ cat /proc/modules | grep "hello"
hello 12425 0 - Live 0xffffffffc06fc000 (OE)
$ ls /sys/module/hello/
coresize initsize notes/ rhelversion srcversion uevent
holders/ initstate refcnt sections/ taint
modinfo命令可以获得模块的信息,包括模块作者、模块的说明、模块所支持的参数以及vermagic:
$ modinfo hello.ko
filename: /home/grace/driver_study/code/modules/hello.ko
alias: A simple module
description: A simple Hello World Module
license: GPL
author: Test Hello
rhelversion: 7.4
srcversion: E4D5379B55084D8ED2D94E8
depends:
vermagic: 3.10.0-693.el7.x86_64 SMP mod_unload modversions
卸载模块命令rmmod:
$ rmmod hello
2 模块程序结构
一个Linux内核模块主要由以下几个部分组成:
(1)模块加载函数
通过insmod或者modprobe命令加载模块时,模块的加载函数会自动被内核执行,完成模块的初始化工作。
(2)模块卸载函数
通过rmmod命令卸载模块时,模块的卸载函数会自动被内核执行,完成模块相关的卸载功能。
(3)模块许可证声明
许可证(LICENSE)声明描述了内核模块的许可权限,如果不申明LICENSE,模块加载时会收到内核被污染(Kernel Tainted)的警告。
[10688.585888] hello: module license 'unspecified' taints kernel.
在Linux内核模块中可接受的LICENSE包括:GPL、GPL v2等。大多数情况下,内核模块应遵循GPL兼容许可证。
(4)模块参数(可选)
模块参数是模块被加载时可以传递给它的值,它本身对应模块内部的全局变量。
(5)模块导出符号(可选)
内核模块可以导出的符号(symbol,对应于函数或变量),若导出,其它模块可以使用本模块的变量或函数。
(6)模块作者等信息声明(可选)
3 模块加载函数
Linux模块加载函数一般以__init标识声明,典型的模块加载函数的形式如下:
static int __init init_func(void)
{
/* 初始化代码 */
}
module_init(init_func);
模块加载函数module_init(函数名)的方式指定,返回整型值,若初始化成功,返回0;初始化失败,返回错误编码。
Linux内核中,错误码是一个接近0的负值,定义在<lnux/errno.h>中。
Linux内核中,可以使用request_module(const char *fmt, ...)函数加载内核模块,使用方式:
request_module(module_name);
初始化数据可以定义为__initdata,对于只在初始化阶段所需要的数据,内核在初始化完成之后会释放它们占用的内存。
static int hello_data __initdata = 1;
static int __init hello_init(void)
{
printk(KERN_INFO "Hello world enter %d\n", hello_data);
return 0;
}
module_init(hello_init); //内核加载函数
4 模块卸载函数
模块卸载函数一般以__exit标识声明,常见的用法如下:
static void __exit clean_func(void)
{
/* 释放代码 */
}
module_exit(clean_func); //内核卸载函数
模块卸载函数在模块卸载的时候执行,不返回任何值,且必须以module_exit(函数名)的方式使用。
5 模块参数
可使用"module_param(参数名, 参数类型, 参数读/写权限)"为模块定义一个参数。
在加载模块时,用户可以向模块传递参数,形式为:
insmod 模块名 参数名=参数值
如果不传递,参数使用模块定义的缺省值。如果模块被内置无法insmod,在bootloader中可以通过bootargs设置"模块名.参数名=值"的形式给内核的模块传递参数。
参数的类型可以是:
byte short ushort int uint long ulong charp(字符指针) bool invbool(布尔的反)
在模块编译时会将module_param中的声明类型和变量定义的类型进行比较,判断是否一致。
模块也可以有参数数组,形式为:
module_param_array(数组名, 数组类型, 数组长, 参数读/写权限)
模块参数举例:
#include <linux/init.h>
#include <linux/module.h>
static char *book_name = "dissection Linux Device Driver";
module_param(book_name, charp, S_IRUGO);
static int book_num = 400;
module_param(book_num, int, S_IRUGO);
static int __init hello_init(void)
{
printk(KERN_INFO "book name:%s\n", book_name);
printk(KERN_INFO "book num:%d\n", book_num);
return 0;
}
module_init(hello_init); //内核加载函数
static void __exit hello_exit(void)
{
printk(KERN_INFO "Hello world exit\n");
}
module_exit(hello_exit); //内核卸载函数
MODULE_AUTHOR("Test Hello");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple Hello World Module");
MODULE_ALIAS("A simple module");
编译并且加载不带参数,可以看出输出的是默认的参数值:
$ make
$ insmod para.ko
$ dmesg
[13186.787598] book name:dissection Linux Device Driver
[13186.787601] book num:400
加载时带参数,打印的是输入的参数:
$ insmod para.ko book_name="LDD3" book_num=500
$ dmesg
[13423.036831] book name:LDD3
[13423.036835] book num:500
在/sys/module/模块名/parameters目录下也可以看到模块的参数:
$ ls /sys/module/para/parameters/
book_name book_num
$ cat /sys/module/para/parameters/book_num
500
$ cat /sys/module/para/parameters/book_name
LDD3
6 导出符号
Linux下内核符号表在/proc/kallsyms下,它记录了符号以及符号所在的内存地址:
$ more /proc/kallsyms
ffffffff8109f320 T sys_kill
ffffffff8109f330 T SyS_tgkill
模块可以使用以下方式导出符号到符号表中,导出的符号可以被其它模块使用,使用前需要进行声明:
EXPORT_SYMBOL(符号名);
EXPORT_SYMBOL_GPL(符号名); //只适用于包含GPL许可权的模块
测试用例:
#include <linux/init.h>
#include <linux/module.h>
int add_integer(int a, int b)
{
return a + b;
}
EXPORT_SYMBOL_GPL(add_integer);
int sub_integer(int a, int b)
{
return a - b;
}
EXPORT_SYMBOL_GPL(sub_integer);
MODULE_LICENSE("GPL v2");
加载并且查看相关信息:
$ insmod export_symb.ko
ffffffffc0701000 t add_integer [export_symb]
ffffffffc0701010 t sub_integer [export_symb]
7 模块声明与描述
Linux内核模块中,使用以下函数进行声明:
MODULE_AUTHOR //作者
MODULE_DESCRIPTION //描述
MODULE_VERSION //版本
MODULE_DEVICE_TABLE //设备表,对于USB、PCI驱动,表明该驱动模块所支持的设备
MODULE_ALIAS //别名
8 模块的使用计数
Linux2.6之后的模块计数管理接口为:try_module_get(&module)和module_put(&module)。模块的使用计数不用模块自己管理。
/* 用于增加模块使用计数;返回为0,表示调用失败,希望使用的模块没有加载或正在卸载 */
int try_module_get(struct module *module);
/* 用于减少模块的使用计数 */
void module_put(struct module *module);
9 模块的编译
简单的Makefile:
KVERS = $(shell uname -r)
# Kernel modules
obj-m += hello.o
# Specify flags for the module compilation.
#EXTRA_CFLAGS=-g -O0 #是否使用调试信息
build: kernel_modules
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
clean:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean
该makefile和源码hello.c在同一个目录,运行make命令得到模块hello.ko。
如果需要包括多个.c文件,需要更改:
obj-m += modulename.o
modulename-objs := file1.o file2.o
Linux内核模块学习的更多相关文章
- 3、Linux内核模块学习
一.内核模块的学习 内核的整体框架是非常的大,包含的组件也是非常多,如何将需要的组件包含在内核中呢?选择一,就是将所有的组件全部编译进内核,虽然需要的组件都可以使用,但是内核过分庞大,势必带来效率 ...
- 5.linux内核模块基础,内核模块学习
linux内核模块基础 一.定义 Linux 内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢: 方法 1:把所有的组件都编译进内核文件,即:zImage 或 bzImage,但这样会 ...
- Smart210学习记录-------linux内核模块
Linux 驱动工程师需要牢固地掌握 Linux 内核的编译方法以为嵌入式系统构建可运行的Linux 操作系统映像.在编译 LDD6410 的内核时,需要配置内核,可以使用下面命令中的 一个: #ma ...
- linux 驱动学习笔记01--Linux 内核的编译
由于用的学习材料是<linux设备驱动开发详解(第二版)>,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式. #make confi ...
- 关于Linux内核学习的误区以及相关书籍介绍
http://www.hzlitai.com.cn/article/ARM9-article/system/1605.html 写给Linux内核新手-关于Linux内核学习的误区 先说句正经的:其实 ...
- linux内核学习之二:编译内核
在linux内核学习系列的第一课中讲述了搭建学习环境的过程(http://www.cnblogs.com/xiongyuanxiong/p/3523306.html),环境搭好后,马上就进入到下一环节 ...
- 【转】Linux 中断学习之小试牛刀篇
原文网址:http://www.linuxidc.com/Linux/2011-02/32129.htm 前言 在前面分析了中断的基本原理后,就可以写一个内核中断程序来体验以下,也可以借此程序继续深入 ...
- Linux内核学习笔记-1.简介和入门
原创文章,转载请注明:Linux内核学习笔记-1.简介和入门 By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...
- Linux入门学习教程:虚拟机体验之KVM篇
本文中可以学习到的命令: 1. aptitude 是apt-get 不会产生垃圾的版本 2. dpkg -L virtualbox 显示属于该包的文件 lsmod | grep kvmfi ...
随机推荐
- 微信小程序第一步
微信小程序开发文档https://developers.weixin.qq.com/miniprogram/dev/#小程序简介
- 3、Spring的DI依赖注入
一.DI介绍 1.DI介绍 依赖注入,应用程序运行依赖的资源由Spring为其提供,资源进入应用程序的方式称为注入. Spring容器管理容器中Bean之间的依赖关系,Spring使用一种被称为&qu ...
- Linux 内核到底长啥样
目录 一.简介 二.结构 地基 地面层 进程表 http进程 21进程 22进程 到文件系统 定时任务 管道 411进程 跃层 一.简介 今天,我来为大家解读一幅来自 TurnOff.us 的漫画 & ...
- CF289B Polo the Penguin and Matrix 题解
Content 有一个 \(n\times m\) 的矩阵 \(A\),每次操作你可以将某一个元素增加或减少 \(d\),求是所有元素相等的最小操作次数,或者不存在这样的操作方案. 数据范围:\(1\ ...
- java IO操作和计算操作:工作内存和主内存 volatile关键字作用;原子操作对象AtomicInteger ....
应该停止但无法停止的计算线程 如下线程示例,线程实例中while循环中的条件,在主线程中通过调用实例方法更新后,while循环并没有更新判断变量是否还成立.而是陷入了while(true)死循环. i ...
- java 输入输出IO流 IO异常处理try(IO流定义){IO流使用}catch(异常){处理异常}finally{死了都要干}
IO异常处理 之前我们写代码的时候都是直接抛出异常,但是我们试想一下,如果我们打开了一个流,在关闭之前程序抛出了异常,那我们还怎么关闭呢?这个时候我们就要用到异常处理了. try-with-resou ...
- java 数据类型:ArrayList;LinkList性能分析
各种线性表的性能分析. java提供的List就是一个线性表接口,ArrayList和LinkedList是线性表的两种实现.基于数组的线性表和基于链表的线性表. 一般来说,我们无需理会ArrayLi ...
- python requests库的简单运用
python requests的简单运用 使用pycharm获取requests包 ctrl+alt+s Project:pythonProject pythoninterpreter 点+号搜索 使 ...
- 10分钟uniapp实现即时通讯,腾讯云IM的正确打开方式get
官方的demo基本上覆盖了所有功能点 今天在使用uniapp开发即时通讯IM的时候遇到了瓶颈,便在uniapp的插件市场搜寻一波看看有没有成熟的轮子借鉴,终于发现了这个宝藏插件--"智密 - ...
- c++指针常量和常量指针概述
个人理解,欢迎指正 这个简单,简单,简单(不要有心里压力:认为很难) 本文将会解决: A.变与不变 B.判断指针常量和常量指针. C.常量指针指针常量.本文不涉及. 1.概述 A.指针: 说到底,还 ...