查看: 7853|回复: 27

面对不断升级的内核,如何学习linux设备驱动 [复制链接]

一朝成名 (离线)
积分
36914
帖子
1865
发表于 2009-8-20 20:08:46 |显示全部楼层
关键词: linux , 内核 , 驱动 , 设备 , 学习
面对不断升级的内核,如何学习linux设备驱动   Post By:2009-8-10 21:19:15

                作者:刘洪涛,华清远见嵌入式学院金牌讲师。



面对不断升级的linux内核、GNU开发工具、linux环境下的各种图形库,很多linux应用程序开发人员和linux设备驱动开发人员即兴奋,又烦躁。兴奋的是新的软件软件、工具给我提供了更强大的功能,烦躁的是适应新软件的特性、搭建新环境是一项非常繁琐的事情。本文想从以下3个方面探讨一下“面对不断升级的内核,如何学习linux设备驱动”。
        内核发展的现状及其对技术人员的影响
        Linux目前主要维护2.4和2.6两个内核版本。在http://www.kernel.org/ 网站上已经可以下载到最新的2.6内核linux-2.6.31,及最新的2.4内核linux-2.4.37。稳定版本号基本上是1~3月更新一次,如:2.6.22至2.6.23。升级版本号每1~2周更新一次,如:2.6.23.1至2.6.23.2。
        由于高版本内核并不完全兼容低版本内核,所以内核升级对从事linux开发的技术人员造成了一定的影响,特别对于linux入门人员。
        内核的升级对应从事linux应用程序开发的人员来说影响较小,因为系统调用基本保持兼容。而影响比较大的是驱动开发人员。每次内核的更新都可以导致很多内核函数使用上的变化。其中有内核本身提供的函数,也有硬件平台代码提供的函数,后者变化的更加频繁。这一点让初学内核驱动的人很迷茫,因为当他们按照手里的经典著作,如:Alessandro的《linux设备驱动程序》,编写驱动时,发现并不能够成功的在你的linux平台上编译通过、或不能正常执行。你的朋友会告诉你,你用的内核和书里的不一致。那该怎么办呢?
        我想从两个方面去解释这个问题,一方面是如何写好linux设备驱动,另一方面是如何应对不断升级的内核。
        如何写好Linux设备驱动
        Linux设备驱动是linux内核的一部分,是用来封装硬件细节,为上层提供标准接口的一种方法。为了能够编写出质量比较高的驱动,要求工程师必须具备以下几个方面的知识:
        熟悉处理器的性能
        如:处理器的体系结构、汇编语言、工作模式、异常处理等此项对于初学者来说,重要程度:***。也就是说还不熟悉驱动编写方法的情况下,可
以先不把重心放在这一项上,因为可能因为它的枯燥、抽象而影响到你对设备驱动的兴趣。
        随着你不断的熟悉驱动的编写,你会很自然的意识到此项的重要性。
        掌握驱动目标的硬件工作原理及通讯协议
        如:串口控制器、显卡控制器、硬件编解码、存储卡控制器、I2C通讯、SPI通讯、USB通讯、SDIO通讯、I2S通讯、PCI通讯等
        此项的重要程度应该不用多说了,编写设备驱动的前提就是知道设备的操作方法。但不是说要把所有设备的操作方法都熟悉了以后才可以驱动,你只需要了解你要驱动的硬件就可以了。所有这一项对于初学者来说重要程度都是:*****。
        掌握硬件的控制方法
        如:中断、轮询、DMA 通常一个硬件控制器会有多种控制方法,你需要根据系统性能的需要合理的选择操作方法。
        此项对于初学者来说:重要程度:****。初学阶段以实现功能为目的。掌握的顺序应该是,轮询->中断->DMA。随着学习的深入,需要综合考虑系统的性能需求,采取合适的方法。
        良好的GNU C语言编程基础
        如:C语言的指针、结构体、内存操作、链表、队列、栈、C和汇编混合编程等。
        这些编程语法是编写设备驱动的基础。
        此项无论对于初学者还是熟手重要程度:*****。
        良好的linux操作系统概念
        如:多进程、多线程、进程调度、进程抢占、进程上下文、虚拟内存、原子操作、阻塞、睡眠、同步等概念及它们之间的关系。
        这些概念及方法在设备驱动的使用是linux设备驱动区别单片机编程的最大特点。只有理解了它们才会编写出高质量的驱动。
        此项对于初学者来说:重要程度:***。开始可以以实现功能为目的,逐步完善自己的驱动。
        掌握linux内核中设备驱动的编写接口
        如:字符设备的cdev、块设备的gendisk、网络设备的net_device,以及基于这些基本接口的framebuffer设备的fb_info、mtd设备的mtd_info、tty设备的tty_driver、usb设备的usb_driver、mmc设备的mmc_host等
        Linux内核为设备驱动编写者留下了标准的接口。驱动编写者无需精通内核的各个部分,只需要明确内核留给我们的接口,并实现此接口就可以了。内核流出的接口采用的是面向对象的思路,即把目标设备看成一个对象,通常利用一个结构体来描述这个对象。驱动工程师的任务就是实现这个对象。这个结构体中会包含设备的属性(用变量表示)和操作方法(用函数指针表示)。如:字符设备的cdev
        struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;  //操作方法结合,其它项都是属性
    struct list_head list;
    dev_t dev;
    unsigned int count;
};
        此项对于初学者来说:重要程度:****。开始阶段可以以模仿为主,即套用一些固定的模板。
        如何应对不断升级的内核
        内核升级对驱动的影响主要体现在,(1)驱动接口定义的变化(2)内核的一些功能函数的名称、参数、头文件、宏定义的变化(3)平台代码关于硬件操作方面封装的一些函数的变化(4)设备模型的影响。下面探讨一下,如何应对这几个方面的问题:
        驱动接口定义的变化
        如:2.4内核中字符设备驱动的注册接口是
        int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
        而2.6内核中已经不建议使用这种方法了,改为:
        int cdev_add(struct cdev *p, dev_t dev, unsigned count)
        又如:2.6.27内核中网卡接口的net_device结构成员和低版本的net_device结构成员也发生了一些变化。
        这种接口定义及注册方法带来的变化,发生的并不频繁。解决方案是:参考内核中的代码。这种接口定义及注册方法在内核中非常容易找到,如:字符设备驱动的注册方法及接口定义可以参照内核driver/char/目录下的很多实例。
        内核的一些功能函数的名称、参数、头文件、宏定义的变化
        如:中断注册函数的格式及参数在2.4内核、2.6内核低版本和高版本之间都存在差别
        在2.6.8中,中断注册函数的定义为:
        int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *),unsigned long irq_flags, const char * devname, void *dev_id)
        irq_flags的取值主要为下面的某一种或组合:
        SA_INTERRUPT、SA_SAMPLE_RANDOM、SA_SHIRQ
        在2.6.26中,中断注册函数的定义为:
        int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
typedef irqreturn_t (*irq_handler_t)(int, void *);
        irq_flags的取值主要为下面的某一种或组合:(功能和2.6.8的对应)
        IRQF_DISABLED、IRQF_SAMPLE_RANDOM、IRQF_SHARED
        当出现这些问题时,编译过程中,编译器会给我们比较明确的错误提示,根据这些提示你可以判断出是否是缺少头问题、是否是函数参数定义有误等。解决问题的最好办法还是到你的目标内核中找信息。此时找问题的方法可以借助于搜索,如:你可以在新的内核中搜索request_irq,看新内核中的驱动是如何使用它的。这种方法非常有效。
        平台代码关于硬件操作方面封装的一些函数的变化
        内核中,硬件平台相关的代码在内核更新过程中变化比较频繁。和我们的设备驱动也是息息相关。所以在针对一个新内核编写设备驱动前,一定要熟悉你的平台代码的结构。有时平台虽然提供了内核要求的接口函数,但使用起来功能却并不完善。下面还是先举个例子说明平台代码更新对设备驱动的影响。
        如:在linux-2.6.8内核中,调用set_irq_type(IRQ_EINT0,IRQT_FALLING);去设置S3C2410的IRQ_EINT0的中断触发信号类型,你会发现不会有什么效果。跟踪代码发现内核的set_irq_type函数需要平台提供一个针对硬件平台的实现函数
        static struct irqchip s3c_irqext_chip = {
    .mask       = s3c_irqext_mask,
    .unmask     = s3c_irqext_unmask,
    .ack        = s3c_irqext_ack,
    .type      = s3c_irqext_type
};
s3c_irqext_type就是linux内核需要的实现函数而s3c_irqext_type在2.6.8中的实现为:
static int s3c_irqext_type(unsigned int irq, unsigned int type)
{
    irqdbf("s3c_irqext_type: called for irq %d, type %d\n", irq, type);
    return 0;
}
        原来并没有实现。而在较高版本的内核,如2.6.26内核中,这个函数是实现了的。所以你一定要小心。当平台函数不好用时,一定要查查原因,或者直接操作硬件寄存器来达到目的。
        2.6内核设备模型对驱动的影响
        在2.6内核中写设备驱动和在2.4内核中有着很大的不同,就是在设备驱动中融入了比设备驱动本身结构还复杂,难以理解的设备模型。初学驱动时你可以不理会设备模型,但你会发现内核里的驱动代码基本上都是融入了设备模型的了。所以很多时候你不得不面对现实,还是要弄懂它,并且它也的注册方法也会随着内核的升级而发生变化。解决此类问题的最好方法还是参考目标内核驱动代码。
        总结:
        开始学习设备驱动时,选择一个当前比较流行的内核版本和硬件平台。不着急追赶最新潮流。这样你可以找到的网络资源会比较多,不至于有孤军奋战的感觉。我想这个过程应该不低于1年。当过了这个过程后,尝试将你编写过的驱动移植到各个目标平台上。上面的一些建议、和应对方法是本人的一些经验总结,仅供参考。
linux_Ultra (离线)
积分
39472
帖子
313
发表于 2009-8-22 18:36:47 |显示全部楼层
本帖最后由 linux_Ultra 于 2009-8-22 18:39 编辑

今天 ,刚好问老师关于 学linux c 编程的 问题:
我问: 关于 linux系统平台c编程  要学习:
c 基础+c 本质(c的反汇编和编译连接)+ apue + arm+linux驱动,
这5块知识点,里边都包含着很多的知识, 当你学了一个其中的一个概念的时候,到底要不要深入进去挖深他?比如学到apue进程这块,说到进程,他给了你相关函数和操作过程,你到底要不要去深入进去,挖掘它的内部原理,以便更能用好这些函数? 比如 说道 extern 这个关键字原理,和 side effet这个概念,你要不要去翻翻e文c手册,去系统的理解他们?

你猜他给了什么回答?~~~~~~~~~~~~
linux_Ultra (离线)
积分
39472
帖子
313
发表于 2009-8-22 18:40:11 |显示全部楼层
本帖最后由 linux_Ultra 于 2009-8-22 18:43 编辑

......
















...
一朝成名 (离线)
积分
36914
帖子
1865
发表于 2009-8-22 20:24:07 |显示全部楼层
对不起,没注明呵呵,我不是本文作者,是转帖的~
对于你的问题我来回答一下吧,个人意见呵呵
其实就是关于想挖多深的问题,还好,你学的这是linux(呵呵,很幸运啊win下就不能了),可以想挖多深挖多深,一直挖到程序跟cpu的硬件如何交互(kernel部分)。
第一方面是兴趣,你要是有兴趣,可以根据thread库函数,去学习linux系统下如何实现的线程(各种系统下实现thread的方式是不一样的,相关操作系统有这方面的讲解)。
第二要根据自己的需求和能力去侧重的学习,一个人精力是有限的,如果有能力就多挖深点~对于以后工作会有更深的理解,解决问题的能力也会增强。
最后就是要有一个循序渐进的过程,不能一口吃个胖子

ps:最近我也在研究os的实现的问题,兴趣所好~
网名招摇了点,人不招摇,大家都知道:)
geyingzhen (离线)
积分
69998
帖子
251
发表于 2009-8-25 12:23:23 |显示全部楼层
学习!!!!
linux_Ultra (离线)
积分
39472
帖子
313
发表于 2009-8-26 08:15:16 |显示全部楼层
对不起,没注明呵呵,我不是本文作者,是转帖的~
对于你的问题我来回答一下吧,个人意见呵呵
其实就是关于想挖多深的问题,还好,你学的这是linux(呵呵,很幸运啊win下就不能了),可以想挖多深挖多深,一直挖到程 ...
一朝成名 发表于 2009-8-22 20:24


他给我举了两个例子:
1.种地。
给你一片地,你是只挖一个坑种放一颗种子,然后停在那,施肥浇水等他长大呢?
还是全都挖好了坑,每个坑放颗种子,然后都要去养护她们?
2.老鹰抓小鸡。
老鹰抓小鸡的时候,飞的太高,看见东西,你俯冲下来。因为距离太长。小鸡找就跑掉了。如果飞的太低,又不能监视全局,看不到目标。
lelee007 (离线)
积分
29395
帖子
2683
发表于 2009-9-24 22:35:39 |显示全部楼层
我靠,成名天天都跟哪儿跟踪到的这些东西?

share一下

感觉你那的资源相当丰富
一朝成名 (离线)
积分
36914
帖子
1865
发表于 2009-9-24 22:51:42 |显示全部楼层
我靠,成名天天都跟哪儿跟踪到的这些东西?

share一下

感觉你那的资源相当丰富
lelee007 发表于 2009-9-24 22:35



我这点货都倒广广了
网名招摇了点,人不招摇,大家都知道:)
wlown (离线)
积分
612
帖子
27
发表于 2009-10-14 09:26:12 |显示全部楼层
学习是永恒的!
alpha321 (离线)
积分
4631
帖子
209
发表于 2009-11-18 08:46:47 |显示全部楼层
谢谢分享!
yangjiansen (离线)
积分
911
帖子
47
发表于 2009-11-30 11:40:26 |显示全部楼层
说的好深奥,还没有明白
foreverlee (离线)
积分
0
帖子
7
发表于 2010-6-9 21:29:50 |显示全部楼层
好东西啊
amingor168 (离线)
积分
19
帖子
29
发表于 2010-6-25 20:21:57 |显示全部楼层
眼皮越來越重喔!
alpha321 (离线)
积分
4631
帖子
209
发表于 2010-8-5 18:37:04 |显示全部楼层
好文,感谢分享!
mc02snx (离线)
积分
1
帖子
13
发表于 2010-10-5 22:56:37 |显示全部楼层
谢谢分享
renmingcan (离线)
积分
1
帖子
20
发表于 2010-10-10 12:08:21 |显示全部楼层
自己会的太少了 还需继续努力
z383775409 (离线)
积分
17
帖子
65
发表于 2010-11-1 22:45:07 |显示全部楼层
感谢楼主分享!
andy_han (离线)
积分
29
帖子
42
发表于 2011-5-16 21:01:06 |显示全部楼层
好文,感谢分享
hszx (离线)
积分
1820
帖子
517
发表于 2011-5-18 14:11:52 |显示全部楼层
freeboy898 (离线)
积分
637
帖子
107
发表于 2011-11-4 14:40:34 |显示全部楼层
好文章,学习了,谢谢楼主分享!
您需要登录后才可以发表评论 登录 | 立即注册

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备11013910号 | 京公网安备11010502021702
回顶部