Linux 驱动开发方法论

来源:互联网 发布:网络协议第七章答案 编辑:程序博客网 时间:2024/04/30 20:48

有一种感动,叫泪流满面,有一种机制,叫模块机制。显然,这种模块机制给那些Linux 的发烧友们带来了方便,因为模块机制意味着人们可以把庞大的Linux内核划分为许许多多个小的模块。对于编写设备驱动程序的开发者来说,从此以后他们可以编写设备驱动程序却不需要把她编译进内核,不用reboot机器,她只是一个模块,当你需要她的时候,你可以把她抱入怀中(insmod),当你不再需要 她的时候,你可以把她一脚踢开(rmmod)。

  于是,忽如一夜春风来,内核处处是模块。让我们从一个伟大的例子去认识模块。这就是传说的"Hello World!",这个梦幻般的名字我们看过无数次了,每一次她出现在眼前,就意味着我们开始接触一种新的计算机语言了。(某程序员对书法十分感兴趣,退休 后决定在这方面有所建树。于是花重金购买了上等的文房四宝。一日,饭后突生雅兴,一番磨墨拟纸,并点上了上好的檀香,颇有王羲之风范,又具颜真卿气势,定 神片刻,泼墨挥毫,郑重地写下一行字:hello world)

  请看下面这段代码,她就是Linux下的一个最简单的模块。当你安装这个模块的时候,她会用她特有的语言向你表白:“Hello,world!”,而后来你卸载了这个模块,你无情抛弃了她,她很伤心,她很绝望,但她没有抱怨,她只是淡淡地说,“Goodbye,cruel world!”(再见,残酷的世界!)

你需要使用module_init()和module_exit(),你可以称它们为函 数,不过实际上它们是一些宏,你可以不用去知道她们背后的故事,只需要知道,在Linux Kernel 2.6的世界里,你写的任何一个模块都需要使用它们来初始化或退出,或者说注册以及后来的注销。

  当你用module_init()为一个模块注册了之后,在你使用insmod这个命令去 安装的时候,module_init()注册的函数将会被执行。而当你用rmmod这个命令去卸载一个模块的时候,module_exit()注册的函数 将会被执行。module_init()被称为驱动程序的初始化入口(driverinitialization entry point)。

  怎么样演示以上代码的运行呢?没错,你需要一个Makefile。

在lwn.net上可以找到这个例子,你可以把以上两个文件放在你的某个目录下,然后执行 make,也许你不一定能成功,因为Linux Kernel 2.6要求你编译模块之前,必须先在内核源代码目录下执行make,换之,你必须先配置过内核,执行过make,然后才能make你自己的模块。原因就不细说了,你按着要求的这么去做就行了。在内核顶层目录make过之后,你就可以在你当前放置Makefile的目录下执行make了。make之后你就应该看到一个叫做hello.ko的文件生成了,恭喜你,这就是你将要测试的模块。

  执行命令,#rmmod hello.ko 同时在另一个窗口,用命令tail -f /var/log/messages察看日志文件,你会看到Hello world被打印了出来。再执行命令,#rmmod hello.ko此时,在另一窗口你会看到Goodbye,cruel world!被打印了出来。

到这里,我该恭喜你,因为你已经能够编写Linux内核模块了。这种感觉很美妙,不是吗?你可以嘲笑秦皇汉武略输文采唐宗宋祖稍逊风骚,还可以嘲笑一代天骄成吉思汗只识弯弓射大雕了。是的,阿娇姐姐告诉我们,只要我喜欢,还有什么不可以。

  日后我们会看到,2.6内核中,每个模块都是以module_init开始,以module_exit结束。对大多数人来说没有必要知道这是为什么,记住 就可以了,对大多数人来说,这就像是1+1为什么等于2一样,就像是两点之间最短的是直线,不需要证明,如果一定要证明两点之间直线最短,可以扔一块骨头 在B点,让一条狗从A点出发,你会发现狗走的是直线,是的,狗都知道,咱还能不知道吗?对于驱动开发来说,设备模型的理解是根本,毫不夸张得说,理解了设备模型,再去看那些五花八门的驱动程序,你会发现自己站在了另一个高度,从而有了一种俯视的感觉,就像凤姐俯视知音和故事会,韩峰同志俯视女下属。

  顾名而思义就知道设备模型是关于设备的模型,既不是任小强们的房模,也不是张导的炮模。对咱们写驱动的和不写驱动的人来说,设备的概念就是总线和与 其相连的各种设备了。电脑城的IT工作者都会知道设备是通过总线连到计算机上的,而且还需要对应的驱动才能用,可是总线是如何发现设备的,设备又是如何和 驱动对应起来的,它们经过怎样的艰辛才找到命里注定的那个他,它们的关系如何,白头偕老型的还是朝三暮四型的,这些问题就不是他们关心的了,而是咱们需要关心的。在房市股市千锤百炼的咱们还能够惊喜的发现,这些疑问的中心思想中心词汇就是总线、设备和驱动,没错,它们就是咱们这里要聊的Linux设备模型的名角。

  总线、设备、驱动,也就是bus、device、driver,既然是名角,在内核里都会有它们自己专属的结构,在include/linux/device.h里定义。

有没有发现它们的共性是什么?对,不是很傻很天真,而是很长很复杂。不过不妨把它们看成艺术品,既然是艺术,当然不会让你那么容易的就看懂了,不然怎么称大师称名家。这么想想咱们就会比较的宽慰了,阿Q是鲁迅对咱们80后最大的贡献。

  我知道进入了21世纪,最缺的就是耐性,房价股价都让咱们没有耐性,内核的代码也让人没有耐性。不过做为最没有耐性的一代人,还是要平心静气的扫一 下上面的结构,我们会发现,structbus_type中有成员struct kset drivers 和struct kset devices,同时struct device中有两个成员struct bus_type * bus和struct device_driver *driver,struct device_driver中有两个成员structbus_type * bus和struct klist klist_devices。先不说什么是klist、kset,光从成员的名字看,它们就是一个完美的三角关系。我们每个人心中是不是都有两个她?一个 梦中的她,一个现实中的她。

  凭一个男人的直觉,我们可以知道,struct device中的bus表示这个设备连到哪个总线上,driver表示这个备的驱动是什么,struct device_driver中的bus表示这个驱动属于哪个总线,klist_devices表示这个驱动都持哪些设备,因为这里device是复数, 又是list,更因为一个驱动可以支持多个设备,而一个设备只能绑定个驱动。当然,struct bus_type中的drivers和devices分别表示了这个总线拥有哪些设备和哪些驱动。

  单凭直觉,张钰红不了。我们还需要看看什么是klist、kset。还有上面device和driver结构里出现的kobject结构是什么?作 为一个五星红旗下长大的孩子,我可以肯定的告诉你,kobject和kset都是Linux设备模型中最基本的元素,总线、设备、驱动是西 瓜,kobjcet、klist是种瓜的人,没有幕后种瓜人的汗水不会有清爽解渴的西瓜,我们不能光知道西瓜的的甜,还要知道种瓜人的辛苦。kobject和kset不会在意自己的得失,它们存在的意义在于把总线、设备和驱动这样的对象连接到设备模型上。种瓜的人也不会在意自己的汗水,在意的只是能不能送出甜蜜的西瓜。

  一般来说应该这么理解,整个Linux的设备模型是一个OO的体系结构,总线、设备和驱动都是其中鲜活存在的对象,kobject是它们的基类,所 实现的只是一些公共的接口,kset是同种类型kobject对象的集合,也可以说是对象的容器。只是因为C里不可能会有C++里类的class继承、组合等的概念,只有通过kobject嵌入到对象结构里来实现。这样,内核使用kobject将各个对象连接起来组成了一个分层的结构体系,就好像马列主义 将我们13亿人也连接成了一个分层的社会体系一样。kobject结构里包含了parent成员,指向了另一个kobject结构,也就是这个分层结构的上一层结点。而kset是通过链表来实现的,这样就可以明白,struct bus_type结构中的成员drivers和devices表示了一条总线拥有两条链表,一条是设备链表,一条是驱动链表。我们知道了总线对应的数据结 构,就可以找到这条总线关联了多少设备,又有哪些驱动来支持这类设备。

  那么klist呢?其实它就包含了一个链表和一个自旋锁,我们暂且把它看成链表也无妨,本来在2.6.11内核里,struct device_driver结构的devices成员就是一个链表类型。这么一说,咱们上面的直觉都是正确的,如果买股票,摸彩票时直觉都这么管用,就不 会有咱们这被压扁的一代了。

  现在的人都知道,三角关系很难处。那么总线、设备和驱动之间是如何和谐共处那?先说说总线中的那两条链表是怎么形成的。内核要求每次出现一个设备就 要向总线汇报,或者说注册,每次出现一个驱动,也要向总线汇报,或者说注册。比如系统初始化的时候,会扫描连接了哪些设备,并为每一个设备建立起一个 struct device的变量,每一次有一个驱动程序,就要准备一个structdevice_driver结构的变量。把这些变量统统加入相应的链表,device 插入devices 链表,driver插入drivers链表。这样通过总线就能找到每一个设备,每一个驱动。然而,假如计算机里只有设备却没有对应的驱动,那么设备无法工 作。反过来,倘若只有驱动却没有设备,驱动也起不了任何作用。在他们遇见彼此之前,双方都如同路埂的野草,一个飘啊飘一个摇啊摇,谁也不知道未来在哪里,只能在生命的风里飘摇。于是总线上的两张表里就慢慢的就挂上了那许多孤单的灵魂。

  devices开始多了,drivers开始多了,他们像是来自两个 世界,devices们彼此取暖,drivers们一起狂欢,但他们有一点是相同的,都只是在等待属于自己的那个另一半。

  现在,总线上的两条链表已经有了,这个三角关系三个边已经有了两个,剩下的那个那?链表里的设备和驱动又是如何联系那?先有设备还是先有驱动?很久 很久以前,在那激情燃烧的岁月里,先有的是设备,每一个要用的设备在计算机启动之前就已经插好了,插放在它应该在的位置上,然后计算机启动,然后操作系统 开始初始化,总线开始扫描设备,每找到一个设备,就为其申请一个struct device结构,并且挂入总线中的devices链表中来,然后每一个驱动程序开始初始化,开始注册其struct device_driver结构,然后它去总线的devices链表中去寻找(遍历),去寻找每一个还没有绑定驱动的设备,即struct device中的struct device_driver指针仍为空的设备,然后它会去观察这种设备的特征,看是否是他所支持的设备,如果是,那么调用一个叫做 device_bind_driver的函数,然后他们就结为了秦晋之好。换句话说,把struct device中的struct device_driverdriver指向这个驱动,而struct device_driver driver把struct device加入他的那张struct klistklist_devices链表中来。就这样,bus、device和driver,这三者之间或者说他们中的两两之间,就给联系上了。知道其中之一,就 能找到另外两个。一荣俱荣,一损俱损。

  但现在情况变了,在这红莲绽放的日子里,在这樱花伤逝的日子里,出现了一种新的名词,叫热插拔。设备可以在计算机启动以后在插入或者拔出计算机了。 因此,很难再说是先有设备还是先有驱动了。因为都有可能。设备可以在任何时刻出现,而驱动也可以在任何时刻被加载,所以,出现的情况就是,每当一个struct device诞生,它就会去bus的drivers链表中寻找自己的另一半,反之,每当一个一structdevice_driver诞生,它就去bus的devices链表中寻找它的那些设备。如果找到了合适的,那么OK,和之前那种情况一下,调用 device_bind_driver绑定好。如果找不到,没有关系,等待吧,等到昙花再开,等到风景看透,心中相信,这世界上总有一个人是你所等的,只是还没有遇到而已。

  设备模型拍得再玄幻,它也只是个模型,必须得落实在具体的子系统,否则就只能抱着个最佳技术奖空遗恨。既然前面已经以USB子系统的实现分析示例了分析内核源码应该如何入手,那么这里就仍然以USB子系统为例,看看设备模型是如何软着陆的。

内核中USB子系统的结构

  我们已经知道了USB子系统的代码都位于drivers/usb目录下面,也认识了一个很重要的目录——core子目录。现在,我们再来看一个很重要的模块——usbcore。你可以使用“lsmod”命令看一下,在显示的结果里能够找到有一个模块叫做usbcore。

找到了usbcore那一行吗?core就是核心,基本上你要在你的电脑里用USB设备,那么两个模块是必须的:一个是usbcore,这就是核心 模块;另一个是主机控制器的驱动程序,比如这里usbcore那一行我们看到的ehci_hcd和uhci_hcd,你的USB设备要工作,合适的USB 主机控制器模块也是必不可少的。

  usbcore负责实现一些核心的功能,为别的设备驱动程序提供服务,提供一个用于访问和控制USB硬件的口,而不用去考虑系统当前存在哪种主机控制器。至于core、主机控制器和USB驱动三者之间的关系,如下图所示。

USB 驱动和主机控制器就像core的两个保镖,协议里也说了,主机控制器的驱动(HCD)必须位于USB软件的最下一层。HCD提供主机控制器硬件的抽象,隐 藏硬件的细节,在主机控制器之下是物理的USB及所有与之连接的USB设备。而HCD只有一个客户,对一个人负责,就是usbcore。usbcore将 用户的请求映射到相关的HCD,用户不能直接访问HCD。

  core为咱们完成了大部分的工作,因此咱们写USB驱动的时候,只能调用core的接口,core会将咱们的请求发送给相应的HCD。

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 皮肤又红又黑怎么办 打仙桃晃晃老输怎么办 微信支付忘了摇怎么办 苹果手机微信摇一摇摇不了怎么办 打麻将牌背的时候怎么办 打麻将背的时候怎么办 同城游台球初使化失败怎么办 qq密码改忘了怎么办啊 qq头像显示不出来怎么办 qq的重要数据被清理怎么办 把qq数据删除了怎么办 手机qq头像显示不同怎么办 手机qq图片显示不出来怎么办 微信群聊右上角双人头像没了怎么办 支付宝好友没了怎么办 手机qq好友头像没了怎么办 微信语音变成听筒模式怎么办 微信语音为听筒模式怎么办 微信设置成听筒模式怎么办 微信验证看不全怎么办 qq看视频无法放大怎么办 苹果6功能键坏了怎么办 qq显示不了头像和图片怎么办 关于学校随便更换老师家长怎么办 qq群昵称改不了怎么办 电脑qq群视频进不去怎么办 微信头像没保存怎么办 qq群信息不响怎么办 微信封面换不了怎么办 微信头像没有了怎么办 科二超过5次怎么办 蘑菇街个人中心打不开怎么办 蘑菇街的个人中心打不开怎么办 qq群500人满了怎么办 微信零钱发不出来怎么办 微信零钱发不出去怎么办 微信零钱发不了红包怎么办 当亲戚孤立你该怎么办 陌陌好友找不到了怎么办 换了手机号陌陌号找不到怎么办 微信解析包出现问题怎么办