内核模块基础

l  什么是内核模块?

1. 内核模块是一种没有经过链接,不能独立运行的目标文件,实在内核空间中运行的程序。经过链接装载到内核里面成为内核的一部分,可以访问内核的公用符号(函数和变量)

2. 内核模块可以让操作系统内核在需要时载入和执行,在不需要的时候由操作系统卸载。它们扩展了操作系统内核的功能却不需要重新启动系统

3. 如果没有内核模块,我们不得不一次又一次重新编译生成单内核操作系统的内核镜像来加入新的功能,这还意味着一个臃肿的内核。

l  模块机制的优缺点

优点:1.减小内核映像尺寸,增加系统灵活性

2.节省开发时间;修改内核,不必重新编译整个内核

3.模块的目标代码一旦被链入内核,作用和静态链接的内核目标代码完全等价

缺点:1.对系统性能有一定的损失

2.使用不当会导致系统崩溃

Hello world模块

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

static int __init hello_init(void)

{

printk("<1>hello world\n");

return 0;

}

static void __exit hello_exit(void)

{

printk(KERN_ALERT "hello module exit\n");

}

module_init(hello_init);

module_exit(hello_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("luzheng");

MODULE_DESCRIPTION("this is my first module");

l  __init和__exit宏

1.如果该模块被编译进内核,而不是动态加载,则__init的使用会在模块初始化完成后丢弃该函数并回收所占内存

2.如果该模块被编译进内核,__exit宏将忽略“清理收尾”的函数

3.这些宏在头文件Linux/init.h定义,用来释放内核占用的内存。例如在启动时看到的信息“Freeint unused kernel memory:236k freed”,正是内核释放这些函数所占用空间时的打印信息。

l  Makefile

ifneq ($(KERNELRELEASE),)

obj-m += hello.o

#     obj-m += startstop.o

#     startstop-objs := start.o stop.o      /*把两个文件编译成一个模块 */

else

KERNELDIR ?= /lib/modules/$(shell uname -r)/build  /*定义内核路径*/

PWD := $(shell pwd)      /*表示在当前目录下编译*/

default:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

endif

clean:

rm hello.ko hello.mod.c hello.mod.o hello.o Module.symvers -rf

l  printk

内核通过 printk() 输出的信息具有日志级别,日志级别是通过在 printk() 输出的字符串前加一个带尖括号的整数来控制的,如 printk("<6>Hello, world!/n");。内核中共提供了八种不同的日志级别,在 linux/kernel.h 中有相应的宏对应。

#define KERN_EMERG    "<0>"    /*
system is unusable紧急事件消息,系统崩溃之前提示,表示系统不可用 */
#define KERN_ALERT    "<1>"    /*
action must be taken immediately报告消息,表示必须立即采取措施 */
#define KERN_CRIT     "<2>"  
 /* critical conditions 临界条件,通常涉及严重的硬件或软件操作失败*/
#define KERN_ERR     
"<3>"    /* error conditions 错误条件,驱动程序常用这个来报告硬件的错误*/
#define KERN_WARNING  "<4>"    /* warning
conditions 警告条件,对可能出现问题的情况进行警告*/
#define KERN_NOTICE   "<5>"    /*
normal but significant 正常但又重要的条件,用于提醒*/
#define KERN_INFO     "<6>"  
 /* informational 提示信息,如驱动程序启动时,打印硬件信息*/
#define KERN_DEBUG    "<7>"    /*
debug-level messages 调试级别的消息*/

所以 printk() 可以这样用:printk(KERN_INFO
"Hello, world!/n");。

未指定日志级别的 printk() 采用的默认级别是
DEFAULT_MESSAGE_LOGLEVEL,这个宏在 kernel/printk.c 中被定义为整数 4,即对应KERN_WARNING。

在 /proc/sys/kernel/printk 会显示4个数值(可由 echo 修改),分别表示当前控制台日志级别、未明确指定日志级别的默认消息日志级别、最小(最高)允许设置的控制台日志级别、引导时默认的日志级别。当 printk() 中的消息日志级别小于当前控制台日志级别时,printk 的信息(要有/n符)就会在控制台上显示。但无论当前控制台日志级别是何值,通过 /proc/kmsg (或使用dmesg)总能查看。另外如果配置好并运行了 syslogd 或 klogd,没有在控制台上显示的 printk 的信息也会追加到 /var/log/messages.log 中。

char myname[] =
"chinacodec/n";
printk(KERN_INFO "Hello, world %s!/n", myname);

l  写内核程序需要注意

1. 内核编程时不能访问C库

2. 内核编程时必须使用GUN C

3. 内核编程时缺乏像用户空间那样的内存保护机制

4. 内核编程时浮点数很难使用

5. 内核只有一个很小的定长堆栈

6. 由于内核支持异步中断、抢占和SMP,因此必须时刻注意同步和并发

7. 要考虑可移植性的重要性

l  内核模块参数

Linux2.6允许用户insmod的时候往内核模块里面传递参数,它主要使用module_param宏定义来实现这一功能。

module_param的定义可以在include/linux/moduleparam.h文件里面查看到,它的原型为:

module_param(name, type, perm);

module_param_array(name, type, nump,
perm);

其中module_param是用来传递变量参数的,module_param_array是用来传递数组参数的。

name是在模块中定义的变量名称,type是变量的类型,perm是权限掩码,用来做一个辅助的sysfs入口。

nump是传入数组的数目,是一个int指针。

module_param支持传递的参数类型有:

bool:布尔型
invbool:一个布尔型( true 或者 false)值(相关的变量应当是 int 类型).
invbool 类型颠倒了值, 所以真值变成 false, 反之亦然.
charp :一个字符指针值. 内存为用户提供的字串分配, 指针因此设置.
int:整形
long:长整形
short:短整形
uint:无符号整形
ulong:无符号长整形
ushort:无符号短整形
基本的变长整型值. 以 u 开头的是无符号值.

perm 字段是一个权限值,表示此参数在sysfs文件系统中所对应的文件节点的属性。你应当使用
<linux/stat.h> 中定义的值. 这个值控制谁可以存取这些模块参数在 sysfs 中的表示.当perm为0时,表示此参数不存在 sysfs文件系统下对应的文件节点。 否则, 模块被加载后,在/sys/module/ 目录下将出现以此模块名命名的目录, 带有给定的权限.。

权限在include/linux/stat.h中有定义
比如:
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
使用 S_IRUGO 参数可以被所有人读取, 但是不能改变; S_IRUGO|S_IWUSR 允许 root 来改变参数. 注意, 如果一个参数被 sysfs 修改, 你的模块看到的参数值也改变了, 但是你的模块没有任何其他的通知. 你应当不要使模块参数可写, 除非你准备好检测这个改变并且因而作出反应.

下面看一下实验的例子:

#include <linux/init.h>

#include <linux/module.h>

#include <linux/moduleparam.h>

///////////////////////////////////////////////////////////

MODULE_LICENSE("Dual BSD/GPL");

char *msg_buf = "Hello world!";

int n_arr[] = {1,2,3,4,5};

int n = 7;

//module_param(n, int, S_IRUSR);

module_param_array(n_arr, int, &n, S_IRUSR);

module_param(msg_buf, charp, S_IRUSR);

///////////////////////////////////////////////////////////

static __init int hello_init(void)

{

int i;

printk("%s/n", msg_buf);

for (i=0; i<n; i++)

{

printk("n_arr[%d]=%d/n", i, n_arr[i]);

}

return 0;

}

///////////////////////////////////////////////////////////

static __exit void hello_exit(void)

{

printk("Goodbye, kernel!/n");

}

module_init(hello_init);

module_exit(hello_exit);

运行命令:

sudo insmod hello.ko
msg_buf=veryCD

然后使用dmesg可以查看到printk的输出:

[35983.685059] veryCD

[35983.685067] n_arr[0]=1

[35983.685072] n_arr[1]=2

[35983.685075] n_arr[2]=3

[35983.685079] n_arr[3]=4

[35983.685083] n_arr[4]=5

[35983.685087] n_arr[5]=7

[35983.685091] n_arr[6]=1

可以看出,实现n_arr的长度应该为5,而n为7,驱动里面并没有检测出,n_arr[5],n_arr[6]已经越界了,,,

但是insmod的时候却是会检测n_arr的长度的,,

输入命令:

sudo insmod hello.ko msg_buf=veryCD n_arr=1,2,3,4,5,6

但是提示出错了,:

insmod: error inserting ‘hello.ko‘: -1 Invalid parameters

因为n_arr的数组长度为5,当输入的数组长度小于等于5的时候,insmod可以加载模块成功,

sudo insmod hello.ko msg_buf=veryCD n_arr=1,2,3

[36315.732903] veryCD

[36315.732908] n_arr[0]=1

[36315.732909] n_arr[1]=2

[36315.732911] n_arr[2]=3

可以看出,module_param_array中的nump的值为实际的输入数组参数长度。

然后当使用insmod加载内核模块,并传递数据参数的时候,系统会自动检测数组的长度,当输入的数组长度小于模块的数组长度时,insmod才会成功。

l  内核模块静态编译

  1. 将.c文件拷贝到/drivers/char/下
  2. 修改/drivers/char下的Kconfig文件
    在Kconfig中增加如下代码:
  1. 回到内核根目录下面make
    menuconfig,发现已经添加进去

编译成模块的话选择<M>或者在下一步写成obj-m += hello.o

  1. 修改/drivers/char下的Makefile文件,增加如下
    obj-$(CONFIG_HELLO)        += hello.o
    当然前提是你的hello.c必须放在当前的目录了
  2. 编译内核
时间: 2024-08-04 22:28:34

内核模块基础的相关文章

Linux内核驱动学习(三)----内核模块基础 | 设计 | 可选项

内核模块基础--特点及其命令使用 1.模块本身并不被编译进内核文件(zImage或bzImage) 2.可以根据需要在内核运行时动态加载.卸载---->进而达到节省空间的目的 命令详解(以下载驱动DNW为例): insmod 模块名称(注意有.ko后缀)--安装 insmod dnw_usb.ko lsmod-->查看安装的内核模块 rmmod 模块名称(注意没有.ko后缀)---->卸载内核模块 rmmod dnw_usb 内核模块设计--简单的模块编写 根据上图范例代码,比较应用程序

专题5-内核模块开发1

内核模块基础 1.什么是内核模块 内核模块特点: 模块本身并不被编译进内核文件(zImage或bzImage). 可以根据需求,在内核运行期间动态的安装或卸载 2.安装与卸载 a.安装insmod eg:insmod /home/dnw_usb.ko b.卸载rmmod eg:rmmod dnw_usb c.查看lsmod(系统所有内核模块) eg:lsmod 在linux中,驱动模块大多以内核模块存在的.

java web 开发三剑客 -------电子书

Internet,人们通常称为因特网,是当今世界上覆盖面最大和应用最广泛的网络.根据英语构词法,Internet是Inter + net,Inter-作为前缀在英语中表示“在一起,交互”,由此可知Internet的目的是让各个net交互.所以,Internet实质上是将世界上各个国家.各个网络运营商的多个网络相互连接构成的一个全球范围内的统一网,使各个网络之间能够相互到达.各个国家和运营商构建网络采用的底层技术和实现可能各不相同,但只要采用统一的上层协议(TCP/IP)就可以通过Internet

Linux内核模块开发基础【转】

本文转载自:http://blog.csdn.net/coding__madman/article/details/51298180 1. 什么是内核模块 内核模块具有以下两个特点:1. 模块本身并不被编译进内核文件(zImage或bzImage),可以根据需求,在内核运行期间动态的安装或卸载. 2. 为什么需要内核模块 原因:Linux内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢,方法1:把所有的组件都编译键内核,即:zImage或bzImage,但这样会导致一个问题:占用

linux内核模块开发基础

1. 什么是内核模块 内核模块具有以下两个特点:1. 模块本身并不被编译进内核文件(zImage或bzImage),可以根据需求,在内核运行期间动态的安装或卸载. 2. 为什么需要内核模块 原因:Linux内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢,方法1:把所有的组件都编译键内核,即:zImage或bzImage,但这样会导致一个问题:占用内存过多.然后内核模块就诞生了,可以不用被编译进内核但是可以动态的添加到正在运行的内核中! 3. 如何使用内核模块 1> 安装模块 i

计算机及Linux基础简介

一.计算机的组成及其功能 计算机由运算器,控制器,存储器,输入装置和输出装置五大部件组成计算机,每一部件分别按要求执行特定的基本功能. 运算器或称算术逻辑单元(Arithmetical and Logical Unit) 运算器的主要功能是对数据进行各种运算.这些运算除了常规的加.减.乘.除等基本的算术运算之外,还包括能进行"逻辑判断"的逻辑处理能力,即"与"."或"."非"这样的基本逻辑运算以及数据的比较.移位等操作. 存储

Linux入门基础命令(四)

Linux入门基础命令(四)文件系统与目录结构: 1.文件和被组织成一个单根倒置树结构2.文件系统从根目录下开始,用"/表示"3.以.开头的文件为隐藏文件4.路径分割的 /5.文件有两类数据(元数据:metadata 数据:data)6.文件系统分层结构:LSB(Linux Standard Base)FHS:(Filesystem Hierarchy Standard)7.文件名最长255个字节8.包括路径在内文件名称最长4095个字节9.蓝色-->目录 绿色-->可执行

Python学习第一天----计算机基础

一.学习计算机基础的目的 再高级的编程语言都是运行在操作系统之上的,而操作系统又是运行在硬件基础之上.所以在开始学习编程之前需要深刻的了解并熟知计算机的基础知识.包括硬件基础及操作系统基础. 二.计算机硬件发展史 计算机的定义:是现代用于高速计算的一种电子计算机器,可以进行数值计算,又可以进行逻辑计算,还具有存储记忆功能. 发展史: 原型或者说灵感起源于中国 1946年2月14日情人节这天,世界上第一台电子计算机"电子数字积分计算机ENIAC"在美国宾夕法尼亚大学问世. 电子管时代--

Linux目录结构、bash的基础特性、I/O重定向及管道

Linux目录结构 LSB(Linux StandardsBase)是一套核心标准,它保证了LINUX发行版同LINUX应用程序之间的良好结合规定了一系列标准,其中包括文件系统层级结构标准(FHS Filesystem Hierarchy Standard) Linux系统根目录下有众多子目录,这些目录都参照了FHS协定 / ├── bin ├── boot ├── dev ├── etc ├── home ├── lib ├── lib64 ├── lost+found ├── media ├