linux device driver3 读书笔记(一)

一:机制与策略(转)

  http://www.51hei.com/bbs/dpj-29441-1.html

  机制mechanism,策略policy。如果你看过《linux device drivers》,里面给出了大概的介绍。机制提供了干什么(do what),策略提供如何做(how to do)。驱动程序完成机制的功能,把策略的实现留给用户的应用程序。

通常在机制中,驱动程序要完成打开,关闭,读写,控制等功能。这些都是设备使用时最基本的操作。而策略中就要实现一些高级的数据处理或界面功能。通过例子来说明会更好些。

以RTC(实时时钟设备)为例。假设RTC共有8个REG,分别是2个控制寄存器、年寄存器、月寄存器、日寄存器、时寄存器、分寄存器、秒寄存器。RTC的启动和关闭分别通过设定控制寄存器的某一个位来完成。

1.    驱动程序提供open,release,write,read函数。Open和release不多说了,具体介绍一下read和write的功能。Read中通过IO端口操作可以读入这8个寄存器的值,Write中通过IO端口操作可以写入这8个寄存器的值。仅仅这样驱动程序已经为应用程序提供了所有完成对RTC设备访问的接口。这样或许对应用程序操作RTC不是很方便,因此在发布驱动程序的时候,我们提供了针对RTC的用户函数库。比如setDate,setTime等,这些函数无非就是把驱动程序中的read和write组合调用了一下。但是采用这种策略,用户写应用程序时就会更方便,对底层的细节就不必知道的太多。

2.    另一个可以选择的机制就是驱动程序中引入ioctl函数,驱动程序把setDate,setTime操作的功能在ioctl函数中实现。Ioctl通过IO端口操作访问8个寄存器就可以完成。将来驱动程序发布时,不用提供用户函数库。只要告诉开发人员IOCTL函数的操作码即可,使用不同的ioctl调用就可以完成setDate,setTime功能。

通过上面两种情况可以看出,机制不同提供的策略选择不同,机制可以把策略中的一些功能集成。但是如何选择正确的机制和策略组合呢?

针对上面2中机制提供,分析各自优缺点:

第一种方案中,由于没有提供Ioctl函数,驱动程序必然会占用内存小。而且对于RTC设备,我们不会经常去setDate,setTime,因此这种小概率使用的函数放到用户库中实现更好。

第二中方案,不用提供用户函数库,发布方便。但是ioctl函数会常驻内存中,尽管他可能一直得不到调用!这中方式更符合面向对象的编程思想。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

二:用户空间和内核空间

一个模块在内核空间运行, 而应用程序在用户空间运行. 这个概念是操作系统理论的基础,

驱动模块试涉及到内核态以及用户态,当应用程序发出一个systerm call或者被硬件的中断挂起,内核在上下文中执行systerm call(系统调用),内核和应用程序的memory map也不同,所以

内核的内存空间以及应用程序的内存空间不能互相访问,要用到copy_to_user 以及copy_from_user函数;

内核程序与应用程序最大的不同是内核是并发的,而应用程序是按顺序执行的;所以应用程序不用担心上线文切换,而内核必须要做好上下文切换;

2.6内核以后,内核是抢占式的,这必须要求内核代码是可重入的

内核的栈是非常小的,内核共用4k的栈,所以当需要大的结构体时,是使用堆内存;kzalloc

驱动模块的代码中的函数以及数据结构跟内核的版本紧密相连,所以编译模块的时候要选用正确的内核版本,安装模块的时候也要选择正确的内核版本;

如果

2.6 预备知识

#include <linux/module.h>      包含大量的模块加载卸载的头文件 如module_init 、module_exit等函数
#include <linux/init.h>        指定清理的初始化函数,init的初始化函数

MODULE_LICENSE("GPL");      内核认识的特定许可有, "GPL"( 适用 GNU 通用公共许可的任何版本 ),

MODULE_AUTHOR ( 声明谁编写了模块 ),

MODULE_DESCRIPION( 一个人可读的关于模块做什么的声明 ),

MODULE_VERSION ( 一个代码修订版本号

MODULE_ALIAS ( 模块为人所知的另一个名子 )

以及 MODULE_DEVICE_TABLE ( 来告知用户空间, 模块支持那些设备 ).

2.7 初始化中的错误处理

这点很重要,比如你在insmodu 一个模块的时候加载失败,如果这个驱动模块的代码没有进行相关错误处理,在加载会出现更多问题

错误恢复是最好使用goto语句;

 1 int __init my_init_function(void)
 2 {
 3 int err;
 4 err = register_this(ptr1, "skull"); /* registration takes a pointer and a name */
 5 if (err)
 6 goto fail_this;
 7 err = register_that(ptr2, "skull");
 8 if (err)
 9 goto fail_that;
10 err = register_those(ptr3, "skull");
11 if (err)
12 goto fail_those;
13 return 0; /* success */
14 fail_those:
15 unregister_that(ptr2, "skull");
16 fail_that:
17 unregister_this(ptr1, "skull");
18 fail_this:
19 return err; /* propagate the error */
20 }

或者在失败的时候直接执行你的清理函数即可,但是这样需要更多的上下文切换,消耗更过的内存空间,寄存器等;或者可以下下面一个函数;

检查每种操作的状态,在清理,但是这样做的坏处是需要定义很多歌变量,还是用goto比较好;

 1 struct something *item1;
 2 struct somethingelse *item2;
 3 int stuff_ok;
 4 void my_cleanup(void)
 5 {
 6 if (item1)
 7 release_thing(item1);
 8 if (item2)
 9 release_thing2(item2);
10 if (stuff_ok)
11 unregister_stuff();
12 return;
13 }
 1 int __init my_init(void)
 2 {
 3 int err = -ENOMEM;
 4 item1 = allocate_thing(arguments);
 5 item2 = allocate_thing2(arguments2);
 6 if (!item2 || !item2)
 7 goto fail;
 8 err = register_stuff(item1, item2);
 9 if (!err)
10 stuff_ok = 1;
11 else
12 goto fail;
13 return 0; /* success */
14 fail:
15 my_cleanup();
16 return err;
17 }
时间: 2024-10-24 18:13:14

linux device driver3 读书笔记(一)的相关文章

Linux内核架构读书笔记 - 2.5.3 处理优先级

1 优先级的内核表示 内核使用 0 - 139 表示内部优先级,值越低,优先级越高.0 -99 实时进程使用 nice 值 [-20,19]映射到范围100 - 139,如下图 内核定义了一系列宏来辅助优先级之间的转换 sched.h 1 /* 2 * Priority of a process goes from 0..MAX_PRIO-1, valid RT 3 * priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL/SCHED_BATCH 4 *

Linux内核架构读书笔记 - 2.5.2 数据结构

调度系统各个组建关系如下 激活调度器两种方法:进程睡眠或其他原因放弃CPU,周期性检测 上述两个组件统称为通用调度器或核心调度器. 调度器用于判断接下来运行那个进程,内核支持不同的调度策略( 完全公平调度 实时调度 无事可做的空闲调度进程) 调度器被调用时候 需要执行体系相关的进程上下文切换 每个进程属于某个调度器类,各个调度器负责管理所属进程,通用调度器不涉及进程管理,都由调度器来 下面分别讲述: task_struct 成员 sched.h 1 struct task_struct { 2

Linux内核架构读书笔记 - 2.5.4 核心调度器

什么是核心调度器? 参考前面的博文http://www.cnblogs.com/songbingyu/p/3696414.html 1 周期性调度器 作用: 管理内核中与整个系统和各个进程的调度相关的统计量 负责当前调度类的周期性调度方法 kernel/sched.c 1 /* 2 * This function gets called by the timer code, with HZ frequency. 3 * We call it with interrupts disabled. 4

《Linux程序设计》&mdash;&mdash;读书笔记(2)

Linux环境: 无论操作系统何时启动一个新程序,参数argc和argv都会被设置并传递给main(即使main函数未声明参数,此时只是不能使用这些参数).这些参数通常由另一个程序提供,一般是shell,它要求操作系统启动该新程序.shell会接受用户输入的命令行,将命令行分解成单词,然后把这些单词放入argv数组. 命令行参数在向程序传递信息方面很有用,很多工具程序使用命令行参数来改变程序的行为或设置选项(这一点我们很熟悉,但是可能很少会联想到正是argc.argv参数在这里发挥作用). 尽管

《Linux程序设计》&mdash;&mdash;读书笔记

UNIX是一套计算机操作系统应遵循的特定规范(定义了所有必需的操作系统函数的名称.接口和行为),完全符合该规范的操作系统才允许使用"UNIX"的商标,否则就是"类UNIX操作系统". 许多类UNIX系统都是商业性质的,如IBM的AIX,HP的HP-UX和Sun的Solaris:也有一些免费的,如FreeBSD和Linux. Linux是一个可以自由发布的类UNIX内核实现.发展之路:UNIX->Minix->Linux. Linux程序: Linux应用

linux第二次读书笔记

<Linux内核设计与实现>读书笔记 第五章 系统调用 第五章系统调用 系统调用是用户进程与内核进行交互的接口.为了保护系统稳定可靠,避免应用程序恣意忘形. 5.1与内核通信 系统调用在用户空间进程和硬件设备间添加了一个中间层, 作用:为用户空间提供了一种硬件的抽象接口:保证了系统的稳定和安全,避免应用程序不正确使用硬件,窃取其他进程的资源,或做出危害系统的行为:为了实现多任务和虚拟内存. Linux提供的系统调用比大部分操作系统少得多. 5.2 API.POSIX.和C库 一个API定义了一

Linux device tree 简要笔记

第一.DTS简介     在嵌入式设备上,可能有不同的主板---它们之间差异表现在主板资源不尽相同,比如I2C.SPI.GPIO等接口定义有差别,或者是Timer不同,等等.于是这就产生了BSP的一个说法.所谓BSP,即是是板级支持包,英文全名为:Board Support Package.是介于主板硬件和操纵系统之间的一层.每一个主板,都有自己对应的BSP文件.在kernel/arch/arm/mach-* 目录下,放置着不同主板的BSP文件,比如展讯的某一个项目的BSP文件为: 1 kern

linux私房菜-读书笔记

第零章:计算机概论 计算机:接受用户输入指令和数据,经过中央处理器的数据和逻辑单元运算处理器处理后,以产生或存储成有用的信息. 计算机硬件的五大单元:输入单元.输出单元.CPU内部控制单元.算术逻辑单元和内存.CPU作为一个具有特定功能的芯片,里面含有微指令集,一条微指令集对应一个汇编语句.CPU内分为算术逻辑单元与控制单元,算术逻辑单元主要负责程序运算与逻辑判断,控制单元主要协调各组件与各单元间的工作.基本上数据都是流经内存然后再转出去. CPU:内部已经含有一些小指令,软件都是要经过CPU内

《构建高可用LINUX服务器》读书笔记

一.基础 显示物理CPU个数的命令如下所示:cat /proc/cpuinfo | grep "physical id" | sort | uniq | wc -l 显示每个物理CPU中core的个数(即核数)的命令如下所示:cat /proc/cpuinfo | grep "cpu cores" |uniq 显示逻辑CPU的个数的命令如下所示:cat /proc/cpuinfo | grep "processor" | wc -l 查看硬盘的I