Linux设备驱动程序 第三版 读书笔记(一)

  Bob Zhang

2017.08.25

编写基本的Hello World模块

#include <linux/init.h>
#include <linux/module.h> // 声明模块的许可证书
MODULE_LICENSE("Dual BSD/GPL"); static __init hello_init(void)
{
     // KERN_ALERT表示的是日志级别
printk(KERN_ALERT "Hello, world\n");
return ;
}
static __exit void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world\n");
} // 注册模块初始化函数,在模块安装到内核时会被调用
module_init(hello_init);
// 注册模块的退出函数,在模块从内核移除时会被调用
module_exit(hello_exit);

需要注意的地方是,内核代码不支持浮点数。

模块的编译

 obj-m += hello.o

但是如果需要编译的模块由多个文件组成,则可以使用下面的代码:

 obj-m := module.o
module-objs := file1.o file2.o

模块装载

一般使用insmod对模块进行装载:

 insmod hello.ko

但是insmod不会对要装载的模块的依赖做检查,如果模块引用了内核中没有的符号,则会报“unresolved symbol”的错误。如果要想检查模块的依赖再装载,可以使用modprobe命令。

modprobe, 如同 insmod, 加载一个模块到内核. 它的不同在于它会查看要加载的模块, 看是否它引用了当前内核没有定义的符号. 如果发现有, modprobe 在定义相关符号的当前模块搜索路径中寻找其他模块. 当 modprobe 找到这些模块( 要加载模块需要的 ), 它也把它们加载到内核.如果你在这种情况下代替以使用 insmod , 命令会失败, 在系统日志文件中留下一条 " unresolved symbols "消息.

模块查看

当模块装载好之后,可以使用lsmod检查模块是否真的装载到内核中了:

 #查看所有加载到内核的模块
lsmod #查看指定的模块,如hello.ko
lsmod | grep hello

lsmod 程序生成一个内核中当前加载的模块的列表. 一些其他信息, 例如使用了一个特定模块的其他模块, 也提供了. lsmod 通过读取 /proc/modules 虚拟文件工作. 当前加载的模块的信息也可在位于 /sys/module 的 sysfs 虚拟文件系统找到.

模块卸载

 #卸载hello.ko
rmmod hello.ko

我们可以使用rmmod工具从内核中移除模块。注意,如果内核认为模块仍然在使用状态(例如,某个程序正打开由该模块导出的设备文件),或者内核被配置为禁止移除模块,则无法移除该模块。配置内核并使得内核在模块忙的时候仍能“强制”移除模块也是可能的。但是,如果读者在某种情况下希望利用这种特性,则重新引导系统可能是更加合适的做法。

内核符号表

Linux内核头文件提供了一个方便的方法来管理符号对模块外部可见性,从而减少了可能造成的名字空间污染(名字空间中的名称可能会和内核其他地方定义的名称发生冲突),并且适当隐藏信息。如果一个模块需要向其他模块导出符号,则应该使用下面的宏。

EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);

这两个宏均用于将给定的符号导出到模块外部。_GPL版本使得要导出的模块只能被GPL许可证下的模块使用。符号符号必须在模块文件的全局部分导出,不能在函数中导出,这是因为上面这两个宏江被扩展成为一个特殊的变量声明,而该变量必须是全局的。该变量将在模块许可执行文件的特殊部分(即一个“ELF段”)中保存,在装载时,内核通过这个段来寻找模块导出的变量。

其他宏定义

大部分内核代码中都要包含相当数量的头文件,以便获得函数、数据类型和变量的定义。我们将在用到这些文件时向读者介绍,但是有几个头文件是专门用于模块的,因此必须出现在每个可装载的模块中。故而,所有的模块代码中都包含下面两行代码:

 #include <linux/module.h>
#include <linux/init.h>

module.h包含有可装载模块需要的大量符号和函数的定义。包含init.h的目的是指定初始化和清理函数,就像我们在hello模块中看到的那样。大部分的模块还包括了“moduleparam.h”头文件,这样我们就可以在装载模块的时候向模块传递参数。接下来介绍一些常用的宏。

尽管不是严格要求,但模块应该制定代码所使用的许可证。为此我们只需要包含MODULE_LICENSE行:

 MODULE_LICENSE("GPL");

内核能够识别的许可证有“GPL”(任一版本的GNU(GNU's Not Unix)通用公共许可证)、“GPL v2”(GPL版本2)、“GPL and additional rights(GPL及附加权利)”、“Dual BSD/GPL(BSD/GPL双重许可证)”、“Dual MPL/GPL(MPL/GPL双重许可证)”以及“Proprietary(专有)”。如果一个模块没有显示地标记为上述内核可识别的许可证,则会被假定是专有的,而内核装载这种模块就会被“污染”。

可在模块中包含的其他描述性定义包括:

// 描述模块作者
MODULE_AUTHOR(BobZhang<zhangbob@email.com>);
// 简短说明模块用途
MODULE_DESCRIPTION("This is a hello world demo module.");
// 代码修订号;有关版本字串的创建惯例,请参考<linux/module.h>中的注释
MODULE_VERSION(version);
// 模块的别名
MODULE_ALIAS("hello_world");
// 告诉用户空间模块所支持的设备
MODULE_DEVICE_TABLE(device_table);

上述MODULE_声明可出现在源文件中源代码函数以外的任何地方。但新近的内核编码习惯是将这些声明放在文件的最后。

模块参数

内核提供一种方式传递参数给模块,那就是在运行insmod或modprobe命令时,将要传递给模块的参数给出,而modeprobe还可以从他的配置文件(/etc/modprobe.conf)中读取参数值。这两个命令可在命令行接受几种参数类型的赋值。为了演示这种功能,我们假定对前面的hello模块做一些必要的增强。我们添加了两个参数:一个是整数值,其名称为howmany;另一个是字符串,名称为whom。在装载这个增强的模块时,将相whom问候howmany次。这样我们可用下面的命令来装载该模块:

 insmod hello.ko howmany= whom="Bob"

上面这条命令的效果会让hello模块打印10次“hello, Bob”。

更改后的hello模块如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h> static char *whom = "world";
static int howmany = ;
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO); // 声明模块的许可证书
MODULE_LICENSE("Dual BSD/GPL"); static __int hello_init(void)
{
int count = ;
// KERN_ALERT表示的是日志级别
for(; count < howmany; ++count)
printk(KERN_ALERT "hello, %s\n", whom);
return ;
}
static __exit void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world\n");
} // 注册模块初始化函数,在模块安装到内核时会被调用
module_init(hello_init);
// 注册模块的退出函数,在模块从内核移除时会被调用
module_exit(hello_exit);

内核支持的模块参数类型如下:

类型

描述

bool

invbool

布尔值(取true或false),关联的变量应该是int型。invbool类型反转其值,也就是说,true值变成false,而false变成true。

charp

字符指针值。内核会为用户提供的字符串分配内存,并相应设置指针。

int

long

short

uint

ulong

ushort

具有不同长度的基本整数类型。以u开头的类型用于无符号值。

  

 

模块装载器也支持数组参数,贼提供数组值时用逗号划分各数组成员。要声明数组参数,需要使用下面的宏:

 module_param_array(name, type, num, perm);

其中,name是数组的名称(也就是参数的名称),type是数组原书的类型,num是一个证书变量,而perm是常见的访问许可值。如果装载时设置数组参数,则num会被设置为用户提供的值的个数。模块装载器会拒绝接受超过数组大小的值。

module_param中的最后一个成员是访问许可值,我们应使用<linux/stat.h>存在的定义。这个值用来控制水能够访问sysfs中对模块参数的表述。如果perm被设置为0,就不会有对应的sysfs入口项;否则,模块参数会在/sys/module中出现,并设置为给定的访问许可。如果参数使用S_IRUGO,则任何人均可读取该参数,但不能修改;S_IRUGO|S_IWUSR允许root用户修改该参数。注意,如果一个参数通过sysfs而被修改,则如同模块修改了这个参数的值一样,但是内核不会以任何方式通知模块。大多数情况下,我们不应该让模块参数是可写的,除非我们打算检测这种修改并作为相应的动作。

在用户空间编写驱动

可以在用户空间编写驱动,欲知详情请google或者bing搜索。

当前进程

虽然内核模块不像应用程序那样顺序执行,然而内核执行的大多数操作还是和某个特定的进程相关。内核代码可以通过访问全局项current来获得当前进程。current在<asm.current.h>中定义,是一个指向struct task_struct的指针,而task_struct结构在<linux/sched.h>文件中定义。current指针指向当前正在运行的进程。在open、read等操作系统调用的执行过程中,当前进程指的是调用这些系统调用的进程。如果需要,内核代码可以通过current获得与当前进程相关的信息。

 printk(KERNE_INFO "The process is \"%s\" [pid %i]\n",
current->comm, current->pid);

存储在current->comm成员中的命令是当前进程所执行的程序文件的基本名称(base name),如果必要,会裁剪到15个字符以内。

未完待续~


一切伟大的思想和行动都有一个微不足道的开始。

Any great thoughts and actions has a small beginning.

Linux设备驱动程序 第三版 读书笔记(一)的更多相关文章

  1. effective java(第三版)---读书笔记

    第一章 引言 < Effective Java>这本书并不厚,而且并不适合初学者,适合有一定的工作经验的java攻城狮.这本书不是百科全书式的JAVA 手册,而是试图在讲述如何正确.高效地 ...

  2. javaScript高程第三版读书笔记

    看完<dom编程艺术>现在准备读进阶版的js高程了,由于篇幅较长,所以利用刚看完<dom编程艺术>学到的知识写了段JavaScript代码,来折叠各章的内容.并且应用到了< ...

  3. Linux内核分析第三章读书笔记

    第三章 进程管理 3.1 进程 进程就是处于执行期的程序 进程就是正在执行的程序代码的实时结果 线程:在进程中活动的对象.每个线程都拥有一个独立的程序计数器.进程栈和一组进程寄存器. 内核调度的对象是 ...

  4. 《ECMAScript6标准入门》第三版--读书笔记

    2015年6月,ECMAScript 6正式通过,成为国际标准.尽管在目前的工作中还没有使用ES6,但是每项新技术出来总是忍不住想尝尝鲜,想知道ES6能为前端开发带来哪些变化?对自己的工作有哪些方面可 ...

  5. JavaScript高级程序设计第三版-读书笔记(1-3章)

    这是我第一次用markdown,也是我第一次在网上记录我自己的学习过程. 第一章 JavaScript主要由以下三个不同的部分构成 ECMAScript   提供核心语言功能 DOM     提供访问 ...

  6. CSS权威指南-第三版--读书笔记

    第一章:CSS和文档 html是结构化语言,css是样式语言,html主要用来被强大的搜索引擎更好的索引,更好的让一个盲人通过语音浏览器来了解我们的网页,这也就是为什么说html是结构话语言,因为这是 ...

  7. 高性能mysql第三版读书笔记3

    innodb以前不支持高并发,在搞病房下就是悲剧,全部卡在mutex(缓冲池mutex)上,现在通过线程调度器控制线程怎么进入内核访问数据,参数为innodb_thread_concurrency,它 ...

  8. CLR.via.C#第三版 读书笔记

    第一章 CLR的执行模型 1.1将源代码编译成托管代码 决定将.NET Framework作为自己的开发平台之后,第一步是决定要生成什么类型的应用程序或组件.假定你已经完成了这些次要的细节:一切都已经 ...

  9. 9Andrew.S.Tanenbaum计算机网络第三版读书笔记-总体概览

随机推荐

  1. 解决mysql中文乱码问题?

    mysql是我们项目中非常常用的数据型数据库.但是因为我们需要在数据库保存中文字符,所以经常遇到数据库乱码情况.下面就来介绍一下如何彻底解决数据库中文乱码情况. 1.中文乱码 1.1.中文乱码 cre ...

  2. LeetCode258 各位相加

    题目链接:https://leetcode-cn.com/problems/add-digits/ 给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数. 示例: 输入: 38 输出: ...

  3. undefined 和null的区别

    undefined 和null的区别null是一个表示"无"的对象,转为数值时为0:undefined是一个表示"无"的原始值,转为数值时为NaN.undefi ...

  4. json字符串、json对象、数组之间的转换

    json字符串转化成json对象 // jquery的方法 var jsonObj = $.parseJSON(jsonStr) //js 的方法 var jsonObj = JSON.parse(j ...

  5. -bash: xhost: command not found

    参考自:http://blog.csdn.net/csdnones/article/details/51513163,感谢原作者解决了我的问题. 执行xhost +,报以下错误,原因是因未没有安装相关 ...

  6. rabbitmq 配置集群镜像

  7. luogu3426 [POI2005]SZA-Template 后缀树

    链接 bzoj不能auto https://www.luogu.org/problemnew/show/P3426 思路 这个要求的串一定是S的前缀和S的后缀. 转化一下 建立出来fail树(fail ...

  8. Learning-MySQL【2】:MySQL存储引擎及数据库的操作管理

    一.存储引擎 存储引擎实际上就是如何存储数据.如何为存储的数据建立索引和如何更新.查询数据.存储引擎也可以称为表类型. MySQL提供了插件式(pluggable)的存储引擎,存储引擎是基于表的.同一 ...

  9. Learning-MySQL【4】:表的操作管理和 MySQL 的约束控制

    一.表的操作 1.表的基本概念 数据库与表之间的关系:数据库是由各种数据表组成的,数据表是数据库中最重要的对象,用来存储和操作数据的逻辑结构. 表由列和行组成,列是表数据的描述,行是表数据的实例. 表 ...

  10. Codeforces Round #495 (Div. 2) D. Sonya and Matrix

    http://codeforces.com/contest/1004/problem/D 题意: 在n×m的方格中,选定一个点(x,y)作为中心点,该点的值为0,其余点的值为点到中心点的曼哈顿距离. ...