在Linux操作体系下有3类首要的设备文件类型:块设备、字符设备和网络设备。这种分类办法能够将操控输入/输出设备的驱动程序与其他操作体系软件别离开来。
字符设备与块设备的首要区别是:在对字符设备宣布读/写恳求时,实践的硬件I/O一般紧接着产生。块设备则不然,它运用一块体系内存作为缓冲区,若用户进程对设备的恳求能满意用户的要求,就回来恳求的数据;不然,就调用恳求函数来进行实践的I/O操作。块设备首要是针对磁盘等慢速设备设计的,避免消耗过多的CPU时刻用来等候。网络设备能够经过BSD套接口拜访数据。
每个设备文件都有其文件特点(c/b),表明是字符设备仍是块设备。别的每个文件都有2个设备号,第一个是主设备号,标识驱动程序;第二个是从设备号,标识运用同一个设备驱动程序的、不同的硬件设备。设备文件的主设备号有必要与设备驱动程序在挂号时恳求的主设备号共同,不然用户进程将无法拜访驱动程序。
体系调用时操作体系内核与应用程序之间的接口,设备驱动程序是操作体系内核与机器硬件之间的接口。设备驱动程序是内核的一部分,它完结以下功用:
*对设备初始化和开释
*把数据从内核传送到硬件和从硬件读取数据
*读取应用程序传送给设备文件的数据和回送应用程序恳求的数据
*检测和处理设备呈现的过错
MTD(Memory Technology Device)设备是闪存芯片、小型闪存卡、记忆棒之类的设备,它们在嵌入式设备中的运用正在不断添加。MTD驱动程序是在Linux下专门为嵌入式环境开发的新的一类驱动程序。相关于惯例块设备驱动程序,运用MTD驱动程序的长处在于他们能更好的支撑、办理给予闪存设备,有依据扇区的擦除和读/写操作的更好的接口。
驱动程序结构
Linux的设备驱动程序能够分为3个首要组成部分:
1. 主动装备和初始化子程序,担任监测所要驱动的硬件设备是否存在和能否正常作业。假如该设备正常,则对这个设备及其相关的设备驱动程序需求的软件状况进行初始化。这部分驱动程序仅在初始化时被调用一次。
2. 服务于I/O恳求的子程序,又称为驱动程序的上半部分。调用这部分程序是由于体系调用的成果。这部分程序在执行时,体系仍以为是与进行调用的进程归于同一个进程,仅仅由用户态变成了中心态,具有进行此体系调用的用户程序的运转环境,因此能够在其间调用sleep()等与进行运转环境有关的函数。
3. 中止服务子程序,又称为驱动程序的下半部分。在Linux体系中,并不是直接从中止向量表中调用设备驱动程序的中止服务子程序,而是由Linux体系来接纳硬件中止,再由体系调用中止服务子程序。中止能够在任何一个进程运转时产生,因此在中止服务程序被调用时,不能依赖于任何进程的状况,也就不能调用任何与进程运转环境有关的函数。由于设备驱动程序一般支撑同一类型的若干设备,所以一般在体系调用中止服务子程序时,都带有一个或多个参数,以仅有标识恳求服务的设备。
在体系内部,I/O设备的存/取经过一组固定的进口点来进行,这组进口点是由每个设备的驱动程序供给的。详细到Linux体系,设备驱动程序所供给的这组进口点由一个文件操作结构来向体系进行阐明。file_operation结构界说于linux/fs.h文件中。
struct file_operation{
int (*lseek)(struct inode *inode, struct file *filp, off_t off, int pos);
int (*read)(struct inode *inode, struct file *filp, char *buf, int count);
int (*write)(struct inode *inode, struct file *filp, const char *buf, int count);
int (*readdir)(struct inode *inode, struct file *filp, struct dirent *dirent, int count);
int (*select)(struct inode *inode, struct file *filp, int sel_type, select_table *wait);
int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned int arg);
int (*mmap)(void);
int (*open)(struct inode *inode, struct file *filp);
int (*release)(struct inode *inode, struct file *filp);
int (*fasync)(struct inode *inode, struct file *filp);
};
file_operation结构中的成员简直全部是函数指针,所以实质上便是函数跳转表。每个进程对设备的操作都会依据major、minor设备号,转化成对file_operation结构的拜访。
常用的操作包含以下几种:
*lseek, 移动文件指针的方位,只能用于能够随机存取的设备。
*read, 进行读操作,参数buf为寄存读取成果的缓冲区,count为所要读取的数据长度。回来值为负表明读取操作产生过错;不然,回来实践读取的字节数。关于字符型,要求读取的字节数和回来的实践读取字节数都有必要是inode-i_blksize的倍数。
*write, 进行写操作,与read相似
*readdir, 获得下一个目录进口点,只需与文件体系相关的设备程序才运用。
*select, 进行挑选操作。假如驱动程序没有供给select进口,select操作会以为设备现已预备好进行任何I/O操作。
*ioctl, 进行读、写以外的其他操作,参数cmd为自界说的指令
*mmap, 用于把设备的内容映射到地址空间,一般只需块设备驱动程序运用
*open, 翻开设备预备进行I/O操作。回来0表明翻开成功,回来负数表明失利。假如驱动程序没有供给open进口,则只需/dev/driver文件存在就以为翻开成功。
*release, 即close操作。
在用户自己的驱动程序中,首要要依据驱动程序的功用,完结file_operation结构中函数完成。不需求的函数接口能够直接在 file_operation结构中初始化为NULL。file_operation变量会在驱动程序初始化时注册到体系内部。当操作体系对设备操作时,会调用驱动程序注册的file_operation结构中的函数指针。
Linux对中止的处理
在Linux体系里,对中止的处理是归于体系中心部分,因此假如设别与体系之间以中止方法进行数据交换,就有必要把该设备的驱动程序作为体系中心的一部分。设备驱动程序经过调用request_irq函数来恳求中止,经过free_irq来开释中止。它们被界说为:
#include
int request_irq(unsigned int irq,
void (*handler)(int irq, void dev_id, struct pt_regs *regs),
unsigned long flags,
const char *device,
void *dev_id);
void free_irq(unsigned int irq, void *dev_id);
参数irq表明所要恳求的硬件中止号;handler为向体系挂号的中止处理子程序,中止产生时由体系来调用,调用时所带参数irq为中止号;dev_id为恳求时告知体系的设备标识;regs为中止产生时的寄存器内容;device为设备名,将会呈现在/proc/interrupts文件里;flag是恳求时的选项,它决议中止处理程序的一些特性,其间最重要的是中止处理程序是快速处理程序仍是慢速处理程序。快速处理程序运转时,一切中止都被屏蔽,而慢速处理程序运转时,除了正在处理的中止外,其他中止都没有被屏蔽。在Linux体系中,中止能够被不同的中止处理程序同享。
作为体系中心的一部分,设备驱动程序在恳求和开释内存时不是调用malloc和free,而代之以调用kmalloc和kfree,它们被界说为:
#include
void *kmalloc(unsigned int len, int priority);
void kfree(void *obj);
参数len为期望恳求的字节数;obj为要开释的内存指针;priority为分配内存操作的优先级,即在没有满足闲暇内存时怎么操作,一般用GFP_KERNEL