linux驱动学习笔记1
1、概述1.1、机制与策略分离思想 机制和策略是Unix设计背后的一个最好观念。大部分编程问题其实可以划分为2部分:提供什么能力(机制)和如何使用这些能力(策略)。如果这两个方面由程序的不同部分来表达、或者由不同程序来共同表达,软件包是非常容易开发和适应特殊的需求的。 在驱动相关的地方,机制和策略的理论同样适用。在编写驱动时,程序员应当特别注重这个基础概念:编写内核代码来存取硬件,但是,不能强加特别策略给用户,因为不同的用户有不同的需求。驱动应当做到使硬件可用,并将所有如何使用硬件的细节留给驱动程序内部。1.2
驱动程序的角色 驱动程序在Linux内扮演着特殊的角色。它们是截然不同的黑盒子,使硬件特殊的部分响应定义好的内部编程接口。它们完全隐藏了设备工作的细节。用户的活动通过一套标准化的调用来进行,这些调用与特别的驱动是独立的;设备驱动的角色就是将这些标准调用映射到实际硬件中和设备相关的操作上。设备驱动编程接口是模块化的,支持运行时扩展由内核提供的某种特性的能力,即支持动态加载和卸载。1.3、设备分类方法以Linux的方式看待设备可区分为3种基本设备类型:字符设备、块设备、网络设备。字符设备: 它是一种可以看作字节流来存取的设备;一个字符驱动负责实现这种行为,这样的驱动常常至少实现open、close、read、write系统调用。字符设备通过文件系统节点来存取,与普通文件之间唯一不同就是,你常常可以在普通文件中移来移去,但是,大部分字符设备仅仅是数据通道,你只能顺序存取。但是,也存在例外的情况,存在一些字符设备像数据区一样,可以在里面移来移去,如framegrabber。块设备:如同字符设备,块设备通过位于/dev目录的文件系统节点来存取。一个块设备应该是可以驻有一个文件系统的。在大部分Unix系统,块设备只能处理传送512字节整数倍的I/O操作。而Linux却相反,允许读写任意长度的字节,如字符设备一样。结果就是,块与字符设备的区别仅在内核内部管理数据的方式上,及其内核/驱动的软件接口上的不同。网络设备:任何网络事物都通过一个接口来进行,就是说,一个能够与其他主机交换数据的设备。通常,一个接口是一个硬件设备,也可以是一个软件设备。网络设备常常设计成处理报文的发送和接收。一个网络驱动对单个连接一无所知。它只处理报文。既然不是一个面向流的设备,一个网络接口就不能像/dev/tty1那么容易映射到文件系统的一个结点上。Unix提供的对接口的存取方式仍然是分配一个名字给他们,如eth0,但是这个名字在文件系统中并没有对应的入口。内核与网络设备驱动间通讯,是调用与报文传递相关的函数,与字符设备、块设备完全不同,不用read/write。1.4、内核与应用程序区别
不同于大部分中小应用程序只处理单一,每个模块注册自己服务将来的请求,初始化函数立刻终止。每个内核模块都是基于事件驱动。另外一个不同是退出函数。内核模块的退出函数比须小心释放由初始化函数建立的东西,否则泄露会持续到重启,而普通程序可以在释放资源时懒惰。 应用程序存在于虚拟内存中,有一个非常大的堆栈区,相反,内核有一个非常小的堆栈;他可能小到一个4096字节。你的函数必须与这个内核空间调用链公用这个堆栈,因此,声明一个巨大的自动变量从来就不是一个好主意,如果需要大的结构,你应该在调用时间内动态分配。1.5内核的并发内核编程与传统应用程序编程方式上很大不同是并发问题。linux内核代码包括驱动代码必须是可重入的,他必须能同时在多个上下文中运行。驱动程序员的一个通常错误就是假定并发不是问题,这种假设总不成立。1.6
当前进程虽然内核不如普通应用程序一样顺序执行,但是,内核执行的大部分动作都是由某个进程执行的调用。在模块内部可以通过引用全局指针current来访问当前进程,它是一个struct task_struct结构。为了支持SMP,current不能采用全局指针来实现,而是采用了一种依赖体系结构的机制,将current隐藏在内核堆栈内,并保持对别的内核子系统的隐藏。