随笔分类 - 《Linux device drivers》 读书笔记
开始学习linux驱动部分,借助《Linux device drivers》这本强大的书籍。
由于看的是英文版,就想一边看,一边翻译着玩一下,同时鞭策自己好好学习(其实就是把google翻译好的内容修改一下....)
也加上一下自己的理解的.
摘要:在第五章并发与竞态中,介绍了一个当进程需要的资源被其他进程占用,而不得不等待该资源的情形。这里,要说的则是即便进程得到资源之后,由于资源本身的一些问题而不能预期的完成功能,必须休眠一段时间,直到它的要求得到满足。两种情形的区别是显而易见的,不过在使用上,实在是有点相似。其实最近学的一些东西都是这样,信号量,自旋锁....不管他们的内在区别的话,使用时无外乎就是初始化,然后在一个地方申请,另一个地方反馈。先对休眠有一个基础的认识先。就是说,进程确实是在运行,需要的东西也有给它了,但是,给它的东西却不满足它的要求,于是乎它还是做不了事。怎么办呢,就只有让它等了,当前进程也就休眠了。进入休眠的时候会
阅读全文
摘要:第三章介绍了一个简单的字符设备驱动程序,它是一块内存当做设备的。当时的scull还很简单(只是介绍了openreleasereadwrite四个函数),这一章将进入讲述字符设备驱动程序的一些高级操作。ioctl函数,从函数名来看的话,iocontrol,看上去是用来操作IO的。更通俗的讲,对于硬件控制的代码应该置于此处。对于当前的scull来说,最常用的对硬件的操作也就是修改当前的量子大小(quantun)和当前的数组大小(qset)了。在用户空间调用的ioctl函数原型如下:1 int ioctl(int fd, unsigned long cmd, ...); 三个点的参数看上去有点新奇.
阅读全文
摘要:上一回主要介绍了使用信号量好处理竞态问题。有时候,驱动会碰见这种问题,就是一个线程在运行时,需要等待另外一个线程完成某个动作才能继续。这种情况跟竞态还是有点区别的,说不太清楚。当然这种情形可以用信号量来达到效果,但是这种方法有很多的缺点(对于这种特殊的情况来说)。于是内核就提供了一个全新的接口来专门处理这种情况。Completion(定义在<linux/completion.h>中),正如它的名字,它的作用在于让一个线程告诉另一个线程它完成了某项工作。跟信号量得使用一样,它可以通俗的理解为三个主要部分:初始化,申请和释放。初始化的代码:1 DECLARE_COMPLETION(my
阅读全文
摘要:第三章介绍了一个以内存为设备的字符设备驱动。需要注意的是,第三章介绍的只是这个驱动的最基本的四个函数,open release read和write 有了这四个函数,这个驱动也就可以使用了。不过,从书本附带的源码中得到的scull工程的代码可远远不止这四个函数,它的很多代码时为了后面章节的扩展。同时,由于内核版本跟作者所使用的内核版本不一致,在编译这个scull的时候会出现很多的问题,http://www.linuxidc.com/Linux/2011-06/37818.htm这篇文章介绍了在编译是可能遇到的问题以及解决方法,必须得感谢这篇文章的作者,我终于成功地把scull编译出来了。 第.
阅读全文
摘要:在设备驱动简介时,就提到过要编写特定硬件的设备,首先要相当的了解这个设备。scull的设备就是一片内存,了解scull的设备就得知道scull的内存使用了。 如图所示: 事实上,可以把scull的内存直接理解为一个3维数组了(类似)。第一维的单元是Scull_device结构,Scull_device结构的代码如下:1 struct scull_qset {2 void **data;3 struct scull_qset *next;4 }; data指向一个指针数组,这个里面的指针指向Quantum,Quantum就处在第二维了,而Quantum中的一个个字节,就是第三维了(这个是我个人.
阅读全文
摘要:也许我们可以这么理解,在上一篇介绍的三个数据结构中。file和inode代表着我们要操作的目标,而file_operation提供了对目标的操作。更重要的是,file_operation中的函数都是固定的,每一个函数在什么时候被调用都是内核规定的(事件机制??)。我们要做的事情就是把我们的代码写到对应的位置就是了。这样,就要了解每一个函数的具体作用,以及它究竟在何时会被内核调用了。 open函数的原型如下:1 int (*open)(struct inode *inode, struct file *filp); 其实,file_operation中的大部分函数的参数都会有inode 和fi.
阅读全文
摘要:上一篇讲到了注册设备编号,显然,注册设备编号只是编写驱动代码的第一个步骤。为了完成以后的一些步骤,先介绍三个重要的内核数据结构。 file_operation囊括了所有设备操作到设备号的链接。在linux中驱动以文件的方式存在,而file_operation则是对文件(这里是是字符设备)的所有操作的集合。由于file_operation的成员太多,而当前scull项目仅仅用到了其中的几个。 scull项目的file_operation初始化如下:1 struct file_operations scull_fops = {2 .owner = THIS_MODULE,3 .llseek = .
阅读全文
摘要:进入第三章了,这一章的目标是写一个完成的字符设备驱动。书中以一个scull项目为例,开始介绍字符设备驱动。值得一提的是,scull不依赖于特定的硬件设备,其实对于scull来说,它的设施就是一片内存空间。作者让内存来充当这个驱动的硬件设备。就这一章来说,内存模拟了四个“设备”:scull0 to scull3。这四个设备是全局静态的。虽然他们是由内存组成的,我们把他们当做实际的设备就好了,因为它们用起来跟实际的设备其实也没有什么区别。 对于像我这种初学者来说,很多概念都是新的,值得仔细研究。 主设备号和次设备号。 Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/de.
阅读全文
摘要:有些模块在加载的时候可能需要根据不同的参数来产生不同的功能。这些参数的值可以直接由insmod或者modprobe命令在加载的时候指定。当然,你的代码在使用模块参数前,必须定义这些参数,并让这些参数可用。一个使用了模块参数的设备驱动模块源码如下,这个代码是在原来最基础的hello world的基础上修改的: 1 #include <linux/init.h> 2 #include <linux/module.h> 3 MODULE_LICENSE("Dual BSD/GPL"); 4 static char *whom = "world&q
阅读全文
摘要:上一篇主要介绍了一个最最简单的设备启动模块Hello world的编译,并将其加载到内核的一些步骤。现在对hello world的代码做一个详细的解析,并提出几个编写设备驱动程序代码时要注意的问题。 如上一篇文章所看到的源码一样,设备驱动的初始化函数一般定义如下:1 static int __init initialization_function(void)2 {3 /* Initialization code here */4 }5 module_init(initialization_function); 每一个设备驱动的初始化函数都应该像上面那样子定义。首先,这个初始化函数应该定义为静
阅读全文
摘要:上一篇主要是介绍了设备驱动的定义,作用,以及编写设备驱动的原则等等,总之都是一些文字叙述。现在终于要开始和代码打交道了,很是激动啊。以下开始编写一个最最简单的设备驱动模块,经典的helloworld。下面所叙述的步骤都经过我的亲身体验。首先,自然是需要一个linux的源码包了,可以去http://www.kernel.org/下载,注意选择源码包的版本。我使用的是2.6.39.3。最好把源码包放到用户主目录下去操作。关于编译源码需要的工具,环境什么的,这里就不赘述了,有很多资料可以参考。在源码的drivers/char目录下建立一个文件夹examples,这个目录用于存放我们的设备驱动模块的源
阅读全文
摘要:以Linux为首的免费操作系统的诸多优点之一是它们的内部是开放可见的。操作系统曾今是一个黑暗且神秘的境界,它的代码只有少数程序员知晓,然而现在,它已经能够被任何具备必要技能(当然是指计算机知识)的人来检查,理解,甚至是修改。Linux让操作系统更加的民主化。Linux依然是一个巨大而且复杂的代码机构,然而,想成为内核hacker的人需要一个入口点,这个入口点能够帮助他们进入代码的内部而不被代码的复杂性压倒。通常情况下,设备驱动就是这个入口点。 设备驱动在Linux内核里扮演者一个特殊的角色。他们是特殊的“黑箱子“,使一个特定的硬件响应一个定义良好的内部编程接口;它们完全隐藏了设备工作的细节。.
阅读全文