Linux设备驱动程序 第三版 读书笔记(一)
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设备驱动程序 第三版 读书笔记(一)的更多相关文章
- effective java(第三版)---读书笔记
第一章 引言 < Effective Java>这本书并不厚,而且并不适合初学者,适合有一定的工作经验的java攻城狮.这本书不是百科全书式的JAVA 手册,而是试图在讲述如何正确.高效地 ...
- javaScript高程第三版读书笔记
看完<dom编程艺术>现在准备读进阶版的js高程了,由于篇幅较长,所以利用刚看完<dom编程艺术>学到的知识写了段JavaScript代码,来折叠各章的内容.并且应用到了< ...
- Linux内核分析第三章读书笔记
第三章 进程管理 3.1 进程 进程就是处于执行期的程序 进程就是正在执行的程序代码的实时结果 线程:在进程中活动的对象.每个线程都拥有一个独立的程序计数器.进程栈和一组进程寄存器. 内核调度的对象是 ...
- 《ECMAScript6标准入门》第三版--读书笔记
2015年6月,ECMAScript 6正式通过,成为国际标准.尽管在目前的工作中还没有使用ES6,但是每项新技术出来总是忍不住想尝尝鲜,想知道ES6能为前端开发带来哪些变化?对自己的工作有哪些方面可 ...
- JavaScript高级程序设计第三版-读书笔记(1-3章)
这是我第一次用markdown,也是我第一次在网上记录我自己的学习过程. 第一章 JavaScript主要由以下三个不同的部分构成 ECMAScript 提供核心语言功能 DOM 提供访问 ...
- CSS权威指南-第三版--读书笔记
第一章:CSS和文档 html是结构化语言,css是样式语言,html主要用来被强大的搜索引擎更好的索引,更好的让一个盲人通过语音浏览器来了解我们的网页,这也就是为什么说html是结构话语言,因为这是 ...
- 高性能mysql第三版读书笔记3
innodb以前不支持高并发,在搞病房下就是悲剧,全部卡在mutex(缓冲池mutex)上,现在通过线程调度器控制线程怎么进入内核访问数据,参数为innodb_thread_concurrency,它 ...
- CLR.via.C#第三版 读书笔记
第一章 CLR的执行模型 1.1将源代码编译成托管代码 决定将.NET Framework作为自己的开发平台之后,第一步是决定要生成什么类型的应用程序或组件.假定你已经完成了这些次要的细节:一切都已经 ...
- 9Andrew.S.Tanenbaum计算机网络第三版读书笔记-总体概览
随机推荐
- jquery easyui datagrid 将值作为img显示图片时报404 undefined
原因:datagrid 在请求到数据先进行头部数据和样式的渲染,之后数据 obj = {} value = undefined index = 0 进行一次渲染, 在没有formater情况将数据 ...
- 基于ROS和python,通过TCP通信协议,完成键盘无线控制移动机器人运动
一.所需工具包 1.ROS键盘包:teleop_twist_keyboard 2.TCP通讯包:socket $ cd ~/catkin_ws/src $ git clone https://gith ...
- json字符串、json对象、数组之间的转换
json字符串转化成json对象 // jquery的方法 var jsonObj = $.parseJSON(jsonStr) //js 的方法 var jsonObj = JSON.parse(j ...
- vim中^M的研究
vim打开文件时在行尾显示^M,这样的情况时不时会遇到,下面稍微深入了解下这个问题: 原理呢,其实很简单:Windows换行风格(也叫dos风格)的文本以Unix风格解析就会出现这个情况: 首先重现这 ...
- java.lang.IllegalArgumentException: An invalid domain [.test.com] was specified for this cookie解决方法
当项目中使用单点登录功能时,通常会使用cookie进行信息的保存,这样就可以在多个子域名上存取用户信息. 比如有三个domain分别为test.com,cml.test.com,b.test.com这 ...
- jQuery页面替换+php代码实现搜索后分页
HTML代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <ti ...
- sublime汉化
1.打开sublime使用快捷键 shift+ctrl+p调出package control; 2.键入Package Control:install package 会弹出一个输入框,然后再搜索lo ...
- angular --- s3core移动端项目(三)
angular.module('myApp') .directive('listActive',functon(){ return { restrict:'A', scope:{ listActive ...
- C# Cache 设定过期时间的方法
1. 设定绝对过期时间 /// <summary> /// 设定绝对的过期时间 /// </summary> /// <param name="CacheKey ...
- URL URI
URL 是统一资源定位符,对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址.互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理 ...