当前位置:文档之家› linux驱动原理-LED驱动分析

linux驱动原理-LED驱动分析

第五章:Linux驱动介绍5.1 驱动原理:LINUX提供标准接口函数给底层,底层驱动按照LINUX编程规则进行驱动编写。

操作系统是通过各种驱动程序来驾驭硬件设备的,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。

设备驱动程序是内核的一部分,硬件驱动程序是操作系统最基本的组成部分,在Linux内核源程序中也占有60%以上。

因此,熟悉驱动的编写是很重要的.Linux内核中采用可加载的模块化设计(LKMs,Loadable Kernel Modules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其他的代码可以编译到内核中,或者编译为内核的模块文件(在需要时动态加载)。

5.2 内核模块的主要相关命令◆lsmod:列出当前系统中加载的模块,其中左边第一列是模块名,第二列是该模块大小,第三列则是使用该模块的对象数目。

◆rmmod:是用于将当前模块卸载。

◆insmod和modprobe是用于加载当前模块,但insmod不会自动解决依存关系,即如果要加载的模块引用了当前内核符号表中不存在的符号,则无法加载,也不会去查在其他尚未加载的模块中是否定义了该符号;modprobe可以根据模块间依存关系以及/etc/modules.conf文件中的内容自动加载其他有依赖关系的模块。

5.3 设备分类Linux系统的设备分为三类:字符设备--(包含一个混杂设备)、块设备和网络设备。

字符设备通常指像普通文件或字节流一样,以字节为单位顺序读写的设备,如并口设备、虚拟控制台等。

字符设备可以通过设备文件节点访问,它与普通文件之间的区别在于普通文件可以被随机访问(可以前后移动访问指针),而大多数字符设备只能提供顺序访问,因为对它们的访问不会被系统所缓存。

但也有例外,例如帧缓存(framebuffer)是一个可以被随机访问的字符设备。

块设备通常指一些需要以块为单位随机读写的设备,如IDE硬盘、SCSI硬盘、光驱等。

块设备也是通过文件节点来访问,它不仅可以提供随机访问,而且可以容纳文件系统(例如硬盘、闪存等)。

Linux可以使用户态程序像访问字符设备一样每次进行任意字节的操作,只是在内核态内部中的管理方式和内核提供的驱动接口上不同。

$ ls –l /devcrw-rw---- 1 root uucp 4, 64 08-30 22:58 ttyS0 /*串口设备, c表示字符设备*/ brw-r----- 1 root disk 8, 0 08-11 23:03 sda /*硬盘设备,b表示块设备*/ 5.4 设备驱动程序工作原理模块在调用insmod命令时被加载,此时的入口点是init_module()函数,通常在该函数中完成设备的注册。

同样,模块在调用rmmod命令时被卸载,此时的入口点是cleanup_module()函数,在该函数中完成设备的卸载。

在设备完成注册加载之后,用户的应用程序就可以对该设备进行一定的操作,如open()、read()、write()等,而驱动程序就是用于实现这些操作,在用户应用程序调用相应入口函数时执行相关的操作。

5.5 应用程序、库、内核、驱动程序的关系:4层软件关系如下:1.应用程序通过open函数打开设备文件;2.库根据open函数执行swi中断,引起异常进入内核;3.内核根据异常相关参数(应用程序传递的)找到相应驱动程序,并返回一文件句柄给库;4.库根据文件句柄,触发库提供的write或ioclt函数(函数相关参数由应用程序提供)执行swi触发异常后进入内核;5.内核根据传递的相关参数调用驱动程序相关函数进行相关操作,如点亮led等。

5.6 LINUX驱动程序开发步骤:1.查看原理图、数据手册,了解设备的操作方法;2.在内核中找到相近的驱动程序,以它为模块进行开发,有时候需要从零开始;3.实现驱动程序的初始化:比如向内核注册这个驱动程序,这样应用程序传入文件名时,内核才能找到相应的驱动程序;4.设计所要实现的操作,比如open、close、read、write等函数;5.实现中断服务(中断并不是每个设备驱动所必须的)。

6.编译该驱动程序到内核中,或者用insmod命令加载;7.测试驱动程序。

5.7 驱动程序的加载和卸载:可以将驱动程序静态编译进内核中,也可以将它作为模块在使用时再加载。

在配置内核时,如果某个配置项设为m,就表示它将会被编译成一个模块。

在linux2.6内核中,模块的扩展名为.ko,可以使用insmod 命令加载,使用rmmod命令卸载,使用lsmod命令查看内核中已经加载了哪些模块。

当使用insmod加载模块时,模块的初始化函数被调用,它用来向内核注册驱动程序;当使用rmmod卸载模块时,模块的清除函数被调用。

在驱动代码中,这两个函数要么取固定的名字:init_module和5.8关键概念5.8.1 不可剥夺型(non-preemptive kernel):(分时操作系统内核)要求每个任务主动放弃CPU的使用权。

优点:响应中断快;几乎无须使用信号量保护共享数据,运行中的任务占有CPU,而不必担心被别的任务抢占。

缺点:响应时间,高优先级的任务已经进入就绪态,但还不能运行,要等,直到当前运行着的任务释放CPU。

5.8.2 可剥夺型内核(preemptive kernel):(实时操作系统内核)最高优先级的任务一旦就绪,总能得到CPU的使用权。

当一个运行着的任务使一个比它优先级高的任务进入就绪态时,当前任务的CPU使用权就被剥夺了,或者说被挂起了,更高优先级的任务立刻得到了CPU的使用权。

如果是中断服务子程序使一个高优先级的任务进入就绪态,中断完成时,中断了的任务被挂起,优先级高的任务开始运行。

可剥夺型内核使得任务级响应时间得以最优化。

5.8.3 可重入函数:可以被一个以上的任务调用,而不必担心数据被破坏。

可重入函数任何时候都可以被中断,一段时间以后又可以运行,而相应的数据不会丢失。

可重入函数或者只使用局部变量,即变量保存在CPU寄存器中或堆栈中;或者使用全局变量,则要对全局变量予以保护。

(一个函数被多个任务调用时,每个任务都有自己独立的栈函数存放该函数运行的中间变量)。

5.8.4 资源:任何被任务所占用的实体都可称为资源。

资源可以是输入/输出设备,也可以是一个变量,一个结构或一个数组。

5.8.5 共享资源:可以被一个以上使用的资源叫做共享资源。

为了防止数据被破坏,每个任务在与共享资源打交道时,必须独占该资源。

这叫做互斥(mutual exclusion)。

5.8.6 代码的临界段也称为临界区,指处理时不可分割的代码。

一旦这部分代码开始执行,则不允许任何中断打入。

为确保临界段代码的执行不被中断,在进入临界段之前必须关中断,而临界段代码执行完后,要立即开中断。

5.8.7 实时系统的特点如果逻辑和时序出现偏差,将会引起严重后果。

有两种类型的实时系统:1.软实时系统:系统的宗旨是使各个任务尽快的运行,而不要求限定某一任务在多长时间内完成;如uc/os操作系统。

2.硬实时系统:各个任务不仅须执行无误,而且要做到准时。

像VxWorks通过硬件完成(例如通过定时器等方式),优先级高任务先执行,且每个任务执行时间可以指定。

大多数实时系统是两者的结合。

如uc/os中可以通过中断发生来改变任务运行状态(任务切换)就是一种硬件机制来完成。

5.8.8 死锁:也称为抱死(deadlock或deadly embrace),指两个任务无限期的相互等待对方控制着的资源。

最简单的防止死锁的方法,让每个任务都:(1)先得到全部需要的资源,再做下一步的工作;(2)用同样的顺序申请多个资源;(3)释放资源时,使用相反的顺序(按啥顺序得到,就按啥顺序释放,和中断进栈、出栈类似)。

内核通过定义等待超时来化解死锁-当等待时间超过了某一确定值,而信号量还是无效状态时,就会返回某种形式的出现超时错误的代码。

这个出错代码告知该任务,不是得到了资源使用权,而使系统错误。

5.8.9 Linux将进程状态描述为如下五种:TASK_RUNNING:可运行状态。

处于该状态的进程能被调度执行而成为当前进程。

TASK_INTERRUPTIBLE:可中断的睡眠状态。

处于该状态的进程在所需资源有效时被唤醒,也能通过信号或定时中断唤醒。

TASK_UNINTERRUPTIBLE:不可中断的睡眠状态。

处于该状态的进程仅当所需资源有效时被唤醒。

TASK_ZOMBIE:僵尸状态。

表示进程结束且已释放资源,但其task_struct(任务结构体)仍未释放。

相当于进程已经结束,但在内核的任务表里面还有相应的结构资源。

TASK_STOPPED:暂停状态。

处于该状态的进程通过其他进程的信号才能被唤醒。

如果处于暂停状态的进程被其它进程唤醒,但资源没有到位,那么就进入睡眠状态。

5.8.10 linux内核的三种调度方法:1.SCHED_OTHER 分时调度策略;(各任务时间片平均分配,linux2.4以前的内核)2.SCHED_FIFO实时调度策略,先到先服务;(各任务谁优先级高,先运行谁,linux改进版本,要付费的)3.SCHED_RR实时调度策略,时间片轮转(同优先级就可以按时间分片,不是同优先级按实时调度)(如linux2.6版本,半实时操作系统)。

实时进程将得到优先调用,实时进程根据实时优先级决定调度权值,分时进程则通过nice(优先级)和counter(个数,时间片)值决定权值,nice越小(优先级越高),counter越大,被调度的概率越大,也就是曾经使用了cpu最少的进程将会得到优先调度。

SHCED_RR和SCHED_FIFO的不同:当采用SHCED_RR策略的进程的时间片用完,系统将重新分配时间片,并置于就绪队列尾。

放在队列尾保证了所有具有相同优先级的RR任务的调度公平。

SCHED_FIFO一旦占用cpu则一直运行。

一直运行直到有更高优先级任务到达或自己放弃。

如果有相同优先级的实时进程(根据优先级计算的调度权值是一样的)已经准备好,FIFO时必须等待该进程主动放弃后才可以运行这个优先级相同的任务。

而RR可以让每个任务都执行一段时间。

相同点:1)RR和FIFO都只用于实时任务。

2)创建时优先级大于0(1-99)。

3)按照可抢占优先级调度算法进行。

4)就绪态的实时任务立即抢占非实时任务。

(SCHED_FIFO是实时,一个任务在执行,相同优先级其它任务不能执行;SHCED_RR是实时,同优先级协商式,一个任务在执行,相同优先级可以同时以时间片方式执行)。

5.8.11 所有任务都采用linux分时调度策略时:1.创建任务指定采用分时调度策略,并指定优先级nice值(-20~19)。

相关主题