字符设备与块设备驱动程序精选
platform模型驱动和字符设备模型驱动
platform模型驱动和字符设备模型驱动字符设备驱动模型:1、申请设备号:动态申请(alloc_chrdev_region()),动态申请(register_chrdev_region())struct cdev btn_cdev;//申请设备号if(major){//静态申请设备号dev_id = MKDEV(major, 0);register_chrdev_region(dev_id, 1, "button");}else{//动态申请设备号alloc_chardev_region(&dev_id, 0, 1, "button");major = MAJOR(dev_id);}在Linux中以主设备号用来标识与设备文件相连的驱动程序。
次编号被驱动程序用来辨别操作的是哪个设备。
cdev 结构体的 dev_t 成员定义了设备号,为 32 位,其中高 12 位为主设备号,低20 位为次设备号。
设备号的获得与生成:获得:主设备号:MAJOR(dev_t dev);次设备号:MINOR(dev_t dev);生成:MKDEV(int major,int minor);2、初始化设备:void cdev_init(struct cdev *, struct file_operations *);cdev_init()函数用于初始化cdev 的成员,并建立cdev 和file_operations 之间的连接。
3、注册设备int cdev_add(struct cdev *, dev_t, unsigned);cdev_add()函数向系统添加一个 cdev,完成字符设备的注册。
4、创建设备节点手动创建设备节点:mknod 的标准形式为:mknod DEVNAME {b | c} MAJOR MINOR1,DEVNAME是要创建的设备文件名,如果想将设备文件放在一个特定的文件夹下,就需要先用mkdir在dev目录下新建一个目录;2, b和c 分别表示块设备和字符设备:b表示系统从块设备中读取数据的时候,直接从内存的buffer中读取数据,而不经过磁盘;c表示字符设备文件与设备传送数据的时候是以字符的形式传送,一次传送一个字符,比如打印机、终端都是以字符的形式传送数据;3,MAJOR和MINOR分别表示主设备号和次设备号:为了管理设备,系统为每个设备分配一个编号,一个设备号由主设备号和次设备号组成。
设备驱动程序
Chapter 8Device Drivers(设备驱动程序)操作系统的目标之一是向用户掩盖系统硬件设备的特殊性。
例如,虚拟文件系统呈现给用户一个统一的文件系统视图,而和底层的物理设备无关。
本章描述Linux内核是如何管理系统中的物理设备的。
CPU不是系统中唯一的智能设备,每一个物理设备都由它自己的硬件控制器。
键盘、鼠标和串行口由SuperIO芯片控制,IDE磁盘由IDE控制器控制,SCSI磁盘由SCSI控制器控制,等等。
每一个硬件控制器都由自己的控制和状态寄存器(CSR),而且不同的设备有不同的寄存器。
一个Adaptec 2940 SCSI控制器的CSR和NCR 810 SCSI控制器的CSR完全不同。
CSR用于启动和停止设备,初始化设备和诊断它的问题。
管理这些硬件控制器的代码不是放在每一个应用程序里边,而是放在Linux内核。
这些处理或者管理硬件控制器的软件叫做设备驱动程序。
本质上,Linux内核的设备驱动程序是特权的、驻留在内存的、低级硬件控制例程的共享库。
正是Linux的设备驱动程序处理它们所管理的设备的特性。
UNIX的一个基本特点是它抽象了对设备的处理。
所有的硬件设备都象常规文件一样看待:它们可以使用和操作文件相同的、标准的系统调用来打开、关闭和读写。
系统中的每一个设备都用一个设备特殊文件代表。
例如系统中第一个IDE硬盘用/dev/had表示。
对于块(磁盘)和字符设备,这些设备特殊文件用mknod命令创建,并使用主(major)和次(minor)设备编号来描述设备。
网络设备也用设备特殊文件表达,但是它们由Linux在找到并初始化系统中的网络控制器的时候创建。
同一个设备驱动程序控制的所有设备都有一个共同的major设备编号。
次设备编号用于区分不同的设备以及它们的控制器。
例如,主IDE磁盘的不同分区都有一个不同的次设备编号。
所以,/dev/hda2,主IDE磁盘的第2个分区,其主设备号是3,而次设备号是2。
设备驱动程序简介
设备驱动程序简介1.设备驱动程序的作⽤从⼀个⾓度看,设备驱动程序的作⽤在于提供机制,⽽不是策略。
在编写驱动程序时,程序猿应该特别注意以下这个基本概念:编写訪问硬件的内核代码时,不要给⽤户强加不论什么特定策略。
由于不同的⽤户有不同的需求,驱动程序应该处理如何使硬件可⽤的问题。
⽽将如何使⽤硬件的问题留给上层应⽤程序。
从还有⼀个⾓度来看驱动程序。
它还能够看作是应⽤程序和实际设备之间的⼀个软件层。
总的来说,驱动程序设计主要还是综合考虑以下三个⽅⾯的因素:提供给⽤户尽量多的选项、编写驱动程序要占⽤的时间以及尽量保持程序简单⽽不⾄于错误丛⽣。
2.内核功能划分Unix系统⽀持多进程并发执⾏。
每⼀个进程都请求系统资源。
内核负责处理全部这些请求,依据内核完毕任务的不同,可将内核功能分为例如以下⼏部分:1.进程管理:负责创建和销魂进程。
并处理它们和外部世界之间的连接。
内核进程管理活动就是在单个或多个CPU上实现了多个进程的抽象。
2.内存管理:内存是计算机的主要资源之中的⼀个,⽤来管理内存的策略是决定系统系能的⼀个关键因素。
3.⽂件系统:内核在没有结构的硬件上构造结构化的⽂件系统。
⽽⽂件抽象在整个系统中⼴泛使⽤。
4.设备控制:差点⼉每个系统操作终于都会映射到物理设备上。
5.⽹络功能:⽹络功能也必须由操作系统来管理,系统负责在应⽤程序和⽹络接⼝之间传递数据包,并依据⽹络活动控制程序的运⾏。
全部的路由和地址解析问题都由内核处理。
可装载模块:Linux有⼀个⾮常好的特性:内核提供的特性可在执⾏时进⾏扩展。
可在执⾏时加⼊到内核的代码被称为“模块”。
Linux内核⽀持⼏种模块类型。
包含但不限于设备驱动程序。
每⼀个模块由⽬标代码组成,能够使⽤insmod程序将模块连接到正在执⾏的内核,也能够使⽤rmmod程序移除连接。
3.设备和模块的分类Linux系统将设备分成三个基本类型:字符设备、块设备、⽹络接⼝。
1.字符设备:字符设备驱动程序通常⾄少要实现open、close、read和write系统调⽤。
C语言设备驱动编程入门
C语言设备驱动编程入门C语言设备驱动编程是一项常见的技术,用于编写操作系统的设备驱动程序。
设备驱动程序是操作系统与硬件设备之间的桥梁,它负责将用户操作转化为硬件设备能够理解和执行的指令。
本文将介绍C语言设备驱动编程的基本概念和入门知识,帮助读者了解并入门这一重要的编程技术。
一、设备驱动程序概述设备驱动程序是操作系统的一部分,它与操作系统内核紧密结合,用于实现对硬件设备的控制和管理。
设备驱动程序通常由硬件设备制造商提供,或者由操作系统开发者开发。
它负责处理硬件设备与操作系统之间的通信,使得用户能够方便地操作硬件设备。
设备驱动程序可以分为字符设备驱动和块设备驱动两种类型。
字符设备驱动用于处理流式数据的设备,如键盘、鼠标等;块设备驱动用于处理以块为单位的数据的设备,如硬盘、U盘等。
不同类型的设备驱动程序在实现上有所不同,但都需要用C语言编写。
二、设备驱动程序的基本结构设备驱动程序的基本结构包括设备初始化、设备打开、设备关闭和设备读写等函数。
下面我们逐步介绍这些函数的作用和实现方法。
1. 设备初始化函数设备初始化函数负责对设备进行初始化,包括设备的寄存器配置、中断设置等。
在这个函数中,我们需要了解硬件设备的相关规格和特性,并根据需要进行适当的配置。
2. 设备打开函数设备打开函数在设备被用户程序打开时被调用,它负责向操作系统申请资源,并进行相应的设置,例如打开文件、分配内存等。
3. 设备关闭函数设备关闭函数在设备被用户程序关闭时被调用,它负责释放设备所占用的资源,如释放文件占用的内存、关闭文件等。
4. 设备读写函数设备读写函数是设备驱动程序的核心部分,它负责设备与用户程序之间的数据交换。
设备读函数用于从设备中读取数据,设备写函数用于向设备中写入数据。
三、设备驱动程序的编写步骤编写设备驱动程序需要经过以下几个步骤:1. 了解硬件设备在编写设备驱动程序之前,我们需要详细了解硬件设备的规格和特性,包括硬件寄存器的地址、中断向量等。
块、字符、网络设备
write等。
指令 用途
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
区特性的字符设备,访问它们时可前后移动访问位置。例如framebuffer就是这样的一个设
备,app可以用mmap或lseek访问抓取的整个图像。
块设备:
和字符设备类似,块设备也是通过/dev目录下的文件系统节点来访问。块设备(例如磁盘)
上能够容纳filesystem。在大多数的Unix系统中,进行I/O操作时块设备每次只能传输一个
用。字符终端(/dev/console)和串口(/dev/ttyS0以及类似设备)就是两个字符设备,
它们能很好的说明“流”这种抽象概念。字符设备可以通过FS节点来访问,比如/dev/tty1
和/dev/lp0等。这些设备文件和普通文件之间的唯一差别在于对普通文件的访问可以前后
移动访问位置,而大多数字符备是一个只能顺序访问的数据通道。然而,也存在具有数据
或多个完整的块,而每块包含512字节(或2的更高次幂字节的数据)。Linux可以让app像
字符设备一样地读写块设备,允许一次传递任意多字节的数据。因此,块设备和字符设备的
区别仅仅在于内核内部管理数据的方式,也就是内核及驱动程序之间的软件接口,而这些不
同对用户来讲是透明的。在内核中,和字符驱动程序相比,块驱动程序具有完全不同的接口。
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
网络驱动程序
网络驱动程序我们已经讨论了字符设备和块设备驱动程序,接着要讨论的是迷人的网络世界。
网络接口是Linux设备中的第三标准类,这一章就是讲述它们是如何与核心的其余部分交互的。
网络接口并不象字符和块设备那样存在于文件系统。
相反,它在核心层处理包的发送和接收,并不与进程中的某个打开的文件绑定在一起。
网络接口在文件系统中的角色就象被安装的块设备。
一个块设备在blk_dev数组和其它核心结构中注册它的特征,接着按照要求通过它的request_fn函数“发送”和“接收”块。
类似地,一个网络接口必须在特定的数据结构中注册自己,从而在与外部世界交换包时可以被调用。
安装的磁盘与包发送接口有几个重要的不同。
首先,磁盘以一个结点的形式存在于/dev 目录,而网络接口并不在文件系统中出现。
不过两者之间最大的不同在于:磁盘是被请求向核心发送一个缓冲区,而网络接口则是请求向核心推送进来的包。
Linux的网络子系统被设计成完全协议无关的。
这对网络协议(IP vs. IPX 或其它协议)和硬件协议(以太网vs.令牌环等)都是如此。
网络驱动程序和核心之间的交互一次处理一个网络包;这允许协议可以干净地对驱动程序隐`藏起来,而物理传输则可以对协议隐藏起来。
本章描述网络接口如何与核心的其它部分紧密合作,并给出一个基于内存的模块化的网络接口,称之为(你可能已经猜到了)snull。
为简化讨论,这个接口使用以太网硬件协议并传送IP包。
通过snull获得的知识可以很好地应用于IP以外的协议,从以太网移到其它硬件协议只要求你对使用的物理协议有所了解。
snull的另一个限制是它不能在Linux1.2中编译。
再说一遍,这样做只是为了保持代码简单,并避免在snull中加入一些另人厌倦的条件。
不过,本章将会提到与网络驱动程序相关的可移植性问题。
本章并不介绍IP的编号原则,网络协议,以及其它普通的网络概念。
这个主题与驱动程序作者无关,而且以不到几百页的篇幅想对网络技术有一个令人满意的概述是不可能的。
linux 驱动 面试题
linux 驱动面试题Linux驱动面试题1. 概述Linux驱动程序是连接硬件设备和操作系统之间的重要软件,其作用是向操作系统提供对硬件设备的控制和访问接口。
在Linux系统下,驱动程序的设计和实现是嵌入式系统开发中的重要环节。
本文将介绍一些常见的Linux驱动面试题,帮助读者进行备考和提升相关知识水平。
2. 设备模型与驱动框架Linux内核具有完善的设备模型和驱动框架,以支持各种硬件设备的驱动开发。
在面试中,面试官通常会询问与设备模型和驱动框架相关的问题,如:a) 请介绍Linux内核的设备模型以及其作用。
b) 请解释驱动框架中的Platform设备和Pins控制器是如何配合工作的。
3. 字符设备驱动字符设备驱动是Linux常见的一种驱动类型,用于向应用程序提供对字符设备的访问接口。
相关的面试题可能包括:a) 请解释字符设备驱动的基本工作原理。
b) 内核中的“注册字符设备驱动”的过程是怎样的?c) 请介绍字符设备驱动中的主要数据结构,并解释其作用。
4. 块设备驱动块设备驱动用于向操作系统提供对块设备(如硬盘)的访问接口。
在Linux面试中,可能会涉及以下问题:a) 请解释块设备驱动与字符设备驱动的区别。
b) 在Linux内核中,块设备驱动是如何处理块设备的请求的?c) 请介绍块设备驱动中的磁盘调度算法以及其作用。
5. 中断处理中断是处理外部事件的一种机制,驱动程序需要能够正确处理中断。
面试中可能会涉及以下问题:a) 请解释中断处理机制,并描述Linux内核中的中断处理流程。
b) 在驱动程序中,如何注册和处理中断?c) 请介绍Linux内核中的软中断和Tasklet。
6. 性能优化和调试性能优化和调试是驱动程序开发中重要的环节,也是面试中常见的问题之一。
相关问题可能包括:a) 请介绍一些常用的性能优化方法和工具,用于提高驱动程序的性能。
b) 在Linux内核中,如何进行驱动程序的调试和故障定位?c) 请解释内核中的“内核态”和“用户态”,以及二者之间的区别。
块设备IO流程
1. 概述系统能够随机访问固定大小数据片的设备称为块设备,这些数据片称作块。
另一种基本的设备类型是字符设备。
字符设备按照字节流的方式被有序访问,像串口和键盘都属于字符设备。
这两种类型的设备的根本区别在于它们是否可以被随机访问,换句话说,就是能否在访问设备时随意从一个位置跳到另一个位置。
字符设备仅仅需要控制一个位置--当前位置;而块设备访问的位置必须在介质的不同区间前后移动,同时块设备对执行性能的要求很高。
如何管理块设备和如何管理队块设备的请求,该部分在内核中被称为块I/O层。
2. 解剖一个块设备块设备中最小的可寻址单元式扇区。
扇区最常见大小事512字节。
软件都会用到自己的最小逻辑可寻址单元--块。
块石文件系统的一种抽象--只能基于块来访问文件系统。
虽然物理磁盘寻址是按照扇区级来进行的,但是内核执行的所有磁盘操作都是按照块进行的。
所以,块只能数倍于扇区的大小,但大小不能超过一个页面。
扇区:设备的最小寻址单元,亦称"硬扇区"或"设备块"块:文件系统的最小寻址单元,亦称"文件块"或"I/O块"3. 缓冲区和缓冲区头当一个块被调用内存时,它要存储在一个缓冲区中。
每个缓冲区与一个块对应,它相当于是磁盘块在内存中的表示。
所有这些信息都和文件系统的控制信息密切交融,文件系统的控制信息储存在超级快中,超级块是一种包含文件系统信息的数据结构。
由于内核在处理数据需要相关的控制信息,所以每个缓冲区都有一个对应的描述符。
该描述符用buffer_head结构体表示,被称为缓冲区头,在文件<linux/buffer_head.h>中定义。
结构体中h_count域表示缓冲区的使用技术。
在操作缓冲区头之前,应该增加缓冲区头的引用计数,确保该缓冲区托不会再被分配出去,当完成对缓冲区的操作后,就减少引用计数。
缓冲区头的目的在于描述磁盘块和物理内存缓冲区之间的映射关系。
块设备和字符设备详解
linux设备驱动分3类:字符设备驱动、块设备驱动、网络设备驱动。
废话少说:直接贴图展示:1、字符设备结构体描述:cdevstruct cdev{struct kobject kobj;/*内嵌的kobject对象*/strcut module *owner;/*所属模块*/struct file_operations *ops;/*文件操作结构体*/struct list_head list;dev_t dev;/*设备号,dev_t实质是一个32位整,12位为主设备号,20位为次设备号,提取主次设备号的方法:MAJOR(dev_t dev),MINOR(dev_t dev),生成dev_t的方法:MKDEV(int major,int minor)*/unsigned int count;};2、linux2.6内核提供了一组函数来操作cdev结构体void cdev_init(struct cdev *,struct file_operations *);/*初始化cdev的成员,并且建立cdev 与file_operation的连接*/struct cdev *cdev_alloc(void);/*动态申请一个cdev的内存空间*/void cdev_put(struct cdev *p);int cdev_add(struct cdev *,dev_t,unsigned);/*添加一个cdev,完成字符的注册*/void cdev_del(struct cdev *);/*删除一个cdev,完成字符的注销*/在调用cdev_add函数向系统注册字符设备之前,应该先调用register_chrdev_region()函数或是alloc_chrdev_region()函数向系统申请设备号;模型为:int register_chrdev_region(dev_t from,unsigned count,const char *name);int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char*name)在系统调用cdev_del函数从系统注销字符设备后,unregister_chrdev_region()应该释放之前申请的设备号,该函数原型为:unregister_chrdev_region(dev_t from,unsigned count)3、关于file_operations的内容请参考下篇分析下面代码是基于虚拟的globalmem设备进行字符设备的分析,具体在linux中设备驱动远要比这个复杂。
PCI驱动编程
目录一、字符设备和块设备 (2)二、设备驱动程序接口 (2)三、设备驱动程序模块 (3)四、设备驱动程序结构 (4)1.驱动程序的注册与注销 (4)2.设备的打开与释放 (4)3.设备的读写操作 (4)4.设备的控制操作 (5)5.设备的中断和轮询处理 (5)五、PCI驱动程序框架 (5)1.关键数据结构 (5)a. pci_driver (5)b. pci_dev (6)2.基本框架 (9)六、框架的具体实现之模块操作 (12)1.struct pci_device_id (12)2.初始化设备模块 (12)3.卸载设备模块: (15)4.中断处理: (16)七、框架的具体实现之设备文件操作 (16)1.设备文件操作接口 (16)2.打开设备模块 (17)3.释放设备模块 (17)4.设备数据读写和控制信息模块 (18)5.内存映射模块 (19)八、附录 (19)1.PCI设备私有数据结构 (19)2.PCI配置寄存器 (20)参考资料: (21)一、字符设备和块设备Linux抽象了对硬件的处理,所有的硬件设备都可以像普通文件一样来看待:它们可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作,而驱动程序的主要任务也就是要实现这些系统调用函数。
Linux系统中的所有硬件设备都使用一个特殊的设备文件来表示,例如,系统中的第一个IDE硬盘使用/dev/hda表示。
每个设备文件对应有两个设备号:一个是主设备号,标识该设备的种类,也标识了该设备所使用的驱动程序;另一个是次设备号,标识使用同一设备驱动程序的不同硬件设备。
设备文件的主设备号必须与设备驱动程序在登录该设备时申请的主设备号一致,否则用户进程将无法访问到设备驱动程序。
在Linux操作系统下有两类主要的设备文件:一类是字符设备,另一类则是块设备。
字符设备是以字节为单位逐个进行I/O操作的设备,在对字符设备发出读写请求时,实际的硬件I/O紧接着就发生了,一般来说字符设备中的缓存是可有可无的,而且也不支持随机访问。
块设备和字符设备详解
linux设备驱动分3类:字符设备驱动、块设备驱动、网络设备驱动。
废话少说:直接贴图展示:1、字符设备结构体描述:cdevstruct cdev{struct kobject kobj;/*内嵌的kobject对象*/strcut module *owner;/*所属模块*/struct file_operations *ops;/*文件操作结构体*/struct list_head list;dev_t dev;/*设备号,dev_t实质是一个32位整,12位为主设备号,20位为次设备号,提取主次设备号的方法:MAJOR(dev_t dev),MINOR(dev_t dev),生成dev_t的方法:MKDEV(int major,int minor)*/unsigned int count;};2、linux2.6内核提供了一组函数来操作cdev结构体void cdev_init(struct cdev *,struct file_operations *);/*初始化cdev的成员,并且建立cdev 与file_operation的连接*/struct cdev *cdev_alloc(void);/*动态申请一个cdev的内存空间*/void cdev_put(struct cdev *p);int cdev_add(struct cdev *,dev_t,unsigned);/*添加一个cdev,完成字符的注册*/void cdev_del(struct cdev *);/*删除一个cdev,完成字符的注销*/在调用cdev_add函数向系统注册字符设备之前,应该先调用register_chrdev_region()函数或是alloc_chrdev_region()函数向系统申请设备号;模型为:int register_chrdev_region(dev_t from,unsigned count,const char *name);int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char*name)在系统调用cdev_del函数从系统注销字符设备后,unregister_chrdev_region()应该释放之前申请的设备号,该函数原型为:unregister_chrdev_region(dev_t from,unsigned count)3、关于file_operations的内容请参考下篇分析下面代码是基于虚拟的globalmem设备进行字符设备的分析,具体在linux中设备驱动远要比这个复杂。
mtd介绍——精选推荐
mtd介绍MTD,Memory Technology Device即内存技术设备字符设备和块设备的区别在于前者只能被顺序读写,后者可以随机访问;同时,两者读写数据的基本单元不同。
字符设备,以字节为基本单位,在Linux中,字符设备实现的⽐较简单,不需要缓冲区即可直接读写,内核例程和⽤户态API⼀⼀对应,⽤户层的Read函数直接对应了内核中的Read例程,这种映射关系由字符设备的file_operations维护。
块设备,则以块为单位接受输⼊和返回输出。
对这种设备的读写是按块进⾏的,其接⼝相对于字符设备复杂,read、write API没有直接到块设备层,⽽是直接到⽂件系统层,然后再由⽂件系统层发起读写请求。
同时,由于块设备的IO性能与CPU相⽐很差,因此,块设备的数据流往往会引⼊⽂件系统的Cache机制。
MTD设备既⾮块设备也不是字符设备,但可以同时提供字符设备和块设备接⼝来操作它。
MTD总概述Linux中MTD的所有源码位于/drivers/mtd⼦⽬录下,MTD设备通常可分为四层这四层从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。
⼀、Flash硬件驱动层硬件驱动层负责在init时驱动Flash硬件并建⽴从具体设备到MTD原始设备映射关系tip: 映射关系通常包括分区信息、I/O映射及特定函数的映射drivers/mtd/chips : CFI/jedec接⼝通⽤驱动drivers/mtd/nand : nand通⽤驱动和部分底层驱动程序drivers/mtd/maps : nor flash映射关系相关函数drivers/mtd/devices: nor flash底层驱动⼆、MTD原始设备⽤于描述MTD原始设备的是mtd_info,它定义了⼤量的关于MTD的数据和操作函数。
mtdcore.c : MTD原始设备接⼝相关实现mtdpart.c : MTD分区接⼝相关实现三、MTD设备层基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。
八、linux设备分为哪几类。
⼋、linux设备分为哪⼏类。
1、linux设备Linux系统中的设备可以分为字符设备、块设备和⽹络设备这3类。
(1)字符设备 字符设备是能够像字节流⼀样被访问的设备,当对字符设备发出读写请求,相应的IO操作⽴即发⽣。
Linux系统中很多设备都是字符设备,如字符终端、串⼝、键盘、⿏标等。
在嵌⼊式Linux 开发中,接触最多的就是字符设备以及驱动。
(2)块设备 块设备是Linux系统中进⾏TO操作时必须以块为单位进⾏访问的设备,块设备能够安装⽂件系统。
块设备驱动会利⽤⼀块系统内存作为缓冲区,因此对块设备发出读写访问,并不⼀定⽴即产⽣硬件I/O操作。
Linux系统中常见的块设备有如硬盘、软驱等等。
(3)⽹络设备 ⽹络设备既可以是⽹卡这样的硬件设备,也可以是⼀个纯软件设备如回环设备。
⽹络设备由Linux的⽹络⼦系统驱动,负责数据包的发送和接收,⽽不是⾯向流设备,因此在Linux系统⽂件系统中⽹络设备没有节点。
对⽹络设备的访问是通过socket调⽤产⽣,⽽不是普通的⽂件操作如 open/closc和 read/write等。
2、驱动在linux中的地位 驱动是Linux系统中设备和⽤户之间的桥梁,Linux系统中,访问设备必须通过设备驱动进⾏操作,⽤户程序是不能直接操作设备的。
Linux系统中硬件、驱动和⽤户程序的关系如图2.2所⽰。
驱动程序运⾏与内核空间,⽤户程序只能通过内核提供的系统调⽤,由经VFS 以及驱动程序才能访问和操作硬件,硬件设备传递的数据也必须经过驱动、VFS和系统调⽤才能被⽤户程序接收。
所以说,设备驱动是应⽤程序访问系统设备以及进⾏数据传递的桥梁和通道。
基于linux2.6内核的字符设备驱动程序设计
, sr t t uc
o ne = TH I M ODULE; w r S
… … … … …
//获 取 字 符 设 备 号
2ce d v结构体
存 ln x2. 核 中 , 用 c e 结 构 体 描 iu 6内 使 dv 述 一 个 字 符 设 备 , d v结 构 体 定 义 如 下 : ce
.
_
的 结构 体 , 中 包 含设 备所 涉 及 到 的c e 其 d v、 私有数据及信号量等信息 。 下所 示: 如 / 设 备 结 构 体 /
sr t t uc XXX—d v t —e
— —
{, i e t l f t ) sz , of ;
_ —
f
s r t de c v; t uc c v de
s ie sz
— —
ቤተ መጻሕፍቲ ባይዱ
/ /从 设 备 中 同 步 读 取 数 据 t* ie(tutf e} h r u r (wrt) rc i .c a s s l e
—
}, i e t, l f t { ; sz of )
//该 设 备 其 他 的 私 有 数 据 和 信 号 量 的 信 息 的 定 义
… …
/ /向 设 备 发 送 数 据 u sg e n * o1 sr c i ,src n in d it(p l(tutfl ) e十 tu t
p l t bl sr c ) o l a e tu t :
—
}X —e X X d v; 模 块 加 载 和 卸 载 函数 的 形 式 如 下 : /+ 备 驱 动 模 块 加 载 函 数 / 设
・
计 算机技 术 ・
基 于 l u 26 i x . 内核 的字 符 设 备驱 动程 序设 计 n
字符设备驱动(1)驱动代码完整源码:charButtons.c
字符设备驱动(1)驱动代码完整源码:charButtons.c 内核版本:Linux3.0.8开发板:基于三星S5PV210处理器的Tiny210开发板驱动名称:charButtons.c驱动描述:按键触发中断,中断处理程序执⾏相应的简单LED点亮操作⽅案1注册字符设备使⽤新的接⼝实现(需要好⼏个函数来实现。
貌似更复杂)⽅案2注册字符设备使⽤⽼的接⼝实现(貌似⽼接⼝更简单)/*****************************************************************************简述:简单字符型驱动程序,⼿动静态分配设备号,⼿动创建设备节点******************************************************************************/#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/cdev.h>#include <linux/fs.h>#include <linux/wait.h>#include <linux/poll.h>#include <linux/sched.h>#include <linux/irq.h>#include <asm/irq.h>#include <linux/interrupt.h>#include <mach/map.h>#include <mach/gpio.h>#include <mach/regs-gpio.h>#include <plat/gpio-cfg.h>#include <linux/slab.h>#define DEVICE_NAME "buttons"struct button_desc {int gpio;int number;char *name;};struct led_desc {int gpio;int number;char *name;};static struct button_desc buttons[] = {{ S5PV210_GPH2(0), 0, "KEY0" },{ S5PV210_GPH2(1), 1, "KEY1" },{ S5PV210_GPH2(2), 2, "KEY2" },{ S5PV210_GPH2(3), 3, "KEY3" },{ S5PV210_GPH3(0), 4, "KEY4" },{ S5PV210_GPH3(1), 5, "KEY5" },{ S5PV210_GPH3(2), 6, "KEY6" },{ S5PV210_GPH3(3), 7, "KEY7" },};static struct led_desc leds[] = {{S5PV210_GPJ2(0),1,"LED1"},{S5PV210_GPJ2(1),2,"LED2"},{S5PV210_GPJ2(2),3,"LED3"},{S5PV210_GPJ2(3),4,"LED4"},};#define OK (0)#define ERROR (-1)struct gpio_chip *chip;struct cdev *gDev;struct file_operations *gFile;dev_t devNum;unsigned int subDevNum = 1;//要申请的次设备号个数int reg_major = 234;int reg_minor = 0;static irqreturn_t button_interrupt(int irq, void *dev_id){struct button_desc *bdata = (struct button_desc *)dev_id;int down;unsigned tmp;tmp = gpio_get_value(bdata->gpio);/* active low */down = !tmp;printk("KEY %d: %08x\n", bdata->number, down);if(bdata->number < 4){gpio_set_value(leds[bdata->number].gpio,0);printk("LED %d: On \n",leds[bdata->number].number);}else{gpio_set_value(leds[(bdata->number) - 4].gpio,1);printk("LED %d: OFF \n",leds[(bdata->number)-4].number); }return IRQ_HANDLED;}int butsOpen(struct inode *p, struct file *f){int irq;int i;int err = 0;printk(KERN_EMERG"butsOpen\r\n");for (i = 0; i < ARRAY_SIZE(buttons); i++) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);// irq = IRQ_EINT(16)+i =160+i "S5PV210_GPH2(i)"//irq = IRQ_EINT(24)+i =168+i "S5PV210_GPH3(i)"err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_BOTH,buttons[i].name, (void *)&buttons[i]);if (err)break;}for(i = 0; i<ARRAY_SIZE(leds);i++){if(!leds[i].gpio)continue;gpio_direction_output(leds[i].gpio,1);}if (err) {i--;for (; i >= 0; i--) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);disable_irq(irq);free_irq(irq, (void *)&buttons[i]);}return -EBUSY;}return0;}int charDrvInit(void){devNum = MKDEV(reg_major, reg_minor);printk(KERN_EMERG"devNum is %d\r\n", devNum);if(OK == register_chrdev_region(devNum, subDevNum, DEVICE_NAME)) {printk(KERN_EMERG"register_chrdev_region ok\r\n");}else{printk(KERN_EMERG"register_chrdev_region error\r\n");return ERROR;}/*if(OK == alloc_chrdev_region(&devNum, subDevNum, subDevNum,"test")) {printk(KERN_EMERG"register_chrdev_region ok\r\n");}else{printk(KERN_EMERG"register_chrdev_region error\r\n");return ERROR;}*/gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);gFile->open = butsOpen;//注册设备函数到file_operations结构体gFile//gDev->owner = THIS_MODULE;gFile->owner = THIS_MODULE;cdev_init(gDev, gFile);//在cdev结构体中添加指针指向file_operations结构体gFilecdev_add(gDev, devNum, 3);//建⽴设备号与cdev结构体联系printk(KERN_EMERG"button driver initial done...\r\n");return0;}void __exit charDrvExit(void){int i,irq;cdev_del(gDev);unregister_chrdev_region(devNum, subDevNum);for (i = 0; i < ARRAY_SIZE(buttons); i++) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);disable_irq(irq);free_irq(irq, (void *)&buttons[i]);}return;}module_init(charDrvInit);//执⾏insmod时会执⾏此⾏代码并调⽤charDrvInit,驱动开始module_exit(charDrvExit);//执⾏rmmod时,结束MODULE_LICENSE("GPL");代码实现⽅案1/*****************************************************************************简述:简单字符型驱动程序,⼿动静态分配设备号,⼿动创建设备节点******************************************************************************/#include <linux/module.h>#include <linux/fs.h>#include <mach/gpio.h>#include <linux/irq.h>#include <linux/kdev_t.h>#include <linux/interrupt.h>#include <linux/init.h>#define DEVICE_NAME "leds"struct button_desc {int gpio;int number;char *name;};struct led_desc {int gpio;int number;char *name;};static struct button_desc buttons[] = {{ S5PV210_GPH2(0), 0, "KEY0" },{ S5PV210_GPH2(1), 1, "KEY1" },{ S5PV210_GPH2(2), 2, "KEY2" },{ S5PV210_GPH2(3), 3, "KEY3" },{ S5PV210_GPH3(0), 4, "KEY4" },{ S5PV210_GPH3(1), 5, "KEY5" },{ S5PV210_GPH3(2), 6, "KEY6" },{ S5PV210_GPH3(3), 7, "KEY7" },};static struct led_desc leds[] = {{S5PV210_GPJ2(0),1,"LED1"},{S5PV210_GPJ2(1),2,"LED2"},{S5PV210_GPJ2(2),3,"LED3"},{S5PV210_GPJ2(3),4,"LED4"},};#define OK (0)#define ERROR (-1)dev_t devNum;unsigned int subDevNum = 1;//要申请的次设备号个数int reg_major = 234;int reg_minor = 0;static irqreturn_t button_interrupt(int irq, void *dev_id){struct button_desc *bdata = (struct button_desc *)dev_id;int down;unsigned tmp;tmp = gpio_get_value(bdata->gpio);/* active low */down = !tmp;printk("KEY %d: %08x\n", bdata->number, down);if(bdata->number < 4){gpio_set_value(leds[bdata->number].gpio,0);printk("LED %d: On \n",leds[bdata->number].number);}else{gpio_set_value(leds[(bdata->number) - 4].gpio,1);printk("LED %d: OFF \n",leds[(bdata->number)-4].number); }return IRQ_HANDLED;}int butsOpen(struct inode *p, struct file *f){int irq;int i;int err = 0;printk(KERN_EMERG"butsOpen\r\n");for (i = 0; i < ARRAY_SIZE(buttons); i++) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);// irq = IRQ_EINT(16)+i =160+i "S5PV210_GPH2(i)"//irq = IRQ_EINT(24)+i =168+i "S5PV210_GPH3(i)"err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_FALLING, buttons[i].name, (void *)&buttons[i]);if (err)break;}for(i = 0; i<ARRAY_SIZE(leds);i++){if(!leds[i].gpio)continue;gpio_direction_output(leds[i].gpio,1);}if (err) {i--;for (; i >= 0; i--) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);disable_irq(irq);free_irq(irq, (void *)&buttons[i]);}return -EBUSY;}return0;}static const struct file_operations gFile ={.owner = THIS_MODULE,.open = butsOpen,};int charDrvInit(void){devNum = MKDEV(reg_major, reg_minor);printk(KERN_EMERG"devNum is %d\r\n", devNum);if(OK == register_chrdev(reg_major, DEVICE_NAME, &gFile)){printk(KERN_EMERG "register_chrdev_region ok\r\n");}else{printk(KERN_EMERG"register_chrdev_region error\r\n");return ERROR;}printk(KERN_EMERG "button driver initial done...\r\n");return0;}void __exit charDrvExit(void){int i,irq;unregister_chrdev(reg_major, DEVICE_NAME);for (i = 0; i < ARRAY_SIZE(buttons); i++) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);disable_irq(irq);free_irq(irq, (void *)&buttons[i]);}return;}module_init(charDrvInit);//执⾏insmod时会执⾏此⾏代码并调⽤charDrvInit,驱动开始module_exit(charDrvExit);//执⾏rmmod时,结束MODULE_LICENSE("GPL");MODULE_AUTHOR("LiuB");代码实现⽅案2函数修饰符__init,本质上是⼀个宏定义,在内核源代码中定义:#define __init __section(.init.text) __cold notrace作⽤就是,将被它修饰的函数放⼊.init.text段中去。
块设备文件和字符设备文件的本质区别是什么
块设备文件和字符设备文件的本质区别是什么块设备文件和字符设备文件的本质区别是什么作者:愤怒的葡萄设备文件分为BlockDevice Driver和Character Device Drive两类。
Character Device Drive又被称为字符设备或裸设备rawdevices; Block Device Driver通常成为块设备。
而Block Device Driver是以固定大小长度来传送转移资料;Character Device Driver是以不定长度的字元传送资料。
且所连接的Devices也有所不同,BlockDevice大致是可以随机存取(Random Access)资料的设备,如硬碟机或光碟机;而Character Device刚好相反,依循先後顺序存取资料的设备,如印表机、终端机等皆是。
/dev/dsk对应的为块设备,文件系统的操作用到它,如mount。
/dev/rdsk对应的为字符设备(裸设备,rdsk的r即为 raw),fscknewfs等会涉及到。
一般我们的操作系统和各种软件都是以块方式读写硬盘,这里的块是逻辑块,创建文件系统时可以选择,windows里叫簇。
可看newfs ormkfs的manual。
oracle是比较常见的字符方式读写硬盘。
字符设备还是块设备的定义属于操作系统的设备访问层,与实际物理设备的特性无必然联系。
设备访问层下面是驱动程序,所以只要驱动程序提供的方式,都可以。
也就是说驱动程序支持stream方式,那么就可以用这种方式访问,驱动程序如果还支持block方式,那么你想用哪种方式访问都可以,典型的比如硬盘式的裸设备,两种都支持块设备(block device):是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或从设备中一次性读出放入到缓冲区,如磁盘和文件系统等字符设备(Characterdevice):这是一个顺序的数据流设备,对这种设备的读写是按字符进行的,而且这些字符是连续地形成一个数据流。
计算机操作系统课后答案第9章习题解答
第9章习题解答一、填空1.MS-DOS操作系统由BOOT、IO.SYS、MSDOS.SYS以及 所组成。
2.MS-DOS的一个进程,由程序(包括代码、数据和堆栈)、程序段前缀以及环境块三部分组成。
3.MS-DOS向用户提供了两种控制作业运行的方式,一种是批处理方式,一种是命令处理方式。
4.MS-DOS存储管理规定,从地址0开始每16个字节为一个“节”,它是进行存储分配的单位。
5.MS-DOS在每个内存分区的前面都开辟一个16个字节的区域,在它里面存放该分区的尺寸和使用信息。
这个区域被称为是一个内存分区所对应的内存控制块。
6.MS-DOS有4个存储区域,它们是:常规内存区、上位内存区、高端内存区和扩充内存区。
7.“簇”是MS-DOS进行磁盘存储空间分配的单位,它所含扇区数必须是2的整数次方。
8.当一个目录表里仅包含“.”和“..”时,意味该目录表为空。
9.在MS-DOS里,用文件名打开文件,随后就通过句柄来访问该文件了。
10.在MS-DOS里,把字符设备视为设备文件。
二、选择1.下面对DOS的说法中,B 是正确的。
A.内、外部命令都常驻内存B.内部命令常驻内存,外部命令非常驻内存C.内、外部命令都非常驻内存D.内部命令非常驻内存,外部命令常驻内存2.DOS进程的程序,在内存里 D 存放在一起。
A.总是和程序段前缀以及环境块B.和谁都不C.总是和进程的环境块D.总是和程序段前缀3.MS-DOS启动时能够自动执行的批处理文件名是: C 。
A.CONFIG.SYS B.MSDOS.SYSC.AUTOEXEC.BAT D.4.下面所列的内存分配算法, D 不是MS-DOS采用的。
A.最佳适应法B.最先适应法C.最后适应法D.最坏适应法5.在MS-DOS里,从1024K到1088K的存储区域被称为 D 区。
A.上位内存B.扩展内存C.扩充内存D.高端内存6.MS-DOS的存储管理是对A的管理。
A.常规内存B.常规内存和上位内存C.常规内存和扩展内存D.常规内存和扩充内存7.在下面给出的MS-DOS常用扩展名中,B 不表示一个可执行文件。
嵌入式第10章 字符设备和驱动程序设计-陈文智.
在<linux/kdev_t.h>头文件中给出了这些宏的定义
#define MINORBITS #define MINORMASK #define MAJOR(dev) #define MINOR(dev) #define MKDEV(ma,mi) 20 ((1U << MINORBITS) - 1) ((unsigned int) ((dev) >> MINORBITS)) ((unsigned int) ((dev) & MINORMASK)) (((ma) << MINORBITS) | (mi))
register_chrdev_region函数和alloc_chrdev_region函数用于 分配设备号,它们的区别是后者是以动态的方式分配的。 unregister_chrdev_region函数则用于释放设备号。
关键数据结构
多数情况下,基本的驱动程序操作都会涉及到内核提 供的三个关键数据结构,分别是file_operations、file和 inode,它们都在<linux/fs.h>头文件中定义。 ◦ struct file_operations ◦ struct file ◦ struct inode
对字符设备的访问是通过文件系统内的设 备文件进行的,或者称为设备节点。它们 通常位于/dev目录。 表示字符设备的设备文件可以通过“ls -l” 命令输出的第一列中的“c”来识别,而 块设备则用“b”标识。 主设备号用来标识该设备的种类,也标识 了该设备所使用的驱动程序;次设备号由 内核使用,标识使用同一设备驱动程序的 不同硬件设备。
在2.6内核中,可以容纳大量的设备,而不像先前的内核版本最 多只能使用255个主设备号和255个次设备号。