Linux内核代码笔记5----I/O体系结构和设备驱动模型

1、I/O体系结构
    信息在连接计算机CPU、RAM和I/O设备之间流动的数据通路称为 总线。一台计算机包含几种不同的总线,通过 连接在一起。CPU和I/O设备之间的数据通路称为 I/O总线。80x86使用16位地址总线对I/O设备寻址,使用8、16、32位数据总线传输数据。每个连接到I/O总线上的设备都有自己的I/O地址集,称为 I/O端口(I/O port)。在IBM PC体系中,I/O地址空间一共提供了65536个8位I/O端口。
     I/O端口可以被映射到物理地址空间。因此CPU和I/O设备之间的通信就可以直接使用内存操作的指令(mov、and、or等)了(和DMA结合)。
    为了防止盲目向某一I/O端口写入数据导致系统崩溃,内核使用资源(resource)来记录分配给每个硬件设备的I/O端口。一个resource表示I/O端口地址的一个范围。
struct resource {
 const char *name;
 unsigned long start, end;
 unsigned long flags;
 struct resource *parent, *sibling, *child;
};
    任何设备驱动程序都可以使用下面函数:
request_resource()    //把一个给定范围分配给一个I/O设备
allocate_resource()
release_resource()    //释放之前分配给I/O设备的给定范围

2、设备驱动程序模型
    Linux提供了数据结构和函数,为系统中所有的总线、设备及驱动程序提供了一个统一的抽象描述,这个框架称为 设备驱动程序模型

sysfs文件系统
    /sys文件系统与/proc文件系统类似,是一种允许用户态程序访问内核内部数据结构的文件系统,主要是为了展现设备驱动程序模型组件间的层次关系。
    sysfs中普通文件的主要作用是表示驱动程序和设备的属性。比如/sys/block/sda/dev中含有第一个IDE主磁盘的主设备号和次设备号。

kobject
    设备驱动程序模型的核心数据结构是 kobject,每个kobject对应于sysfs文件系统中的一个目录。kobject被嵌入到一个更大的对象“容器”中。 容器描述设备驱动程序模型中的组件。容器的典型例子有总线、设备及驱动程序的描述符。
    kobject数据结构声明:
struct kobject { 
 char * k_name;                                    //指向容器名称字符串 
 char name[KOBJ_NAME_LEN];          //容器名字
 struct kref kref;                                   //容器的引用计数器
 struct list_head entry;                        //用于kobject所插入链表的指针
 struct kobject * parent;                     //
 struct kset * kset;                               //同类型kobject结构的集合,即:相关的kobject包含在同类型的容器中
 struct kobj_type * ktype;                   //指向kobject的类型描述符,即包含kobject的容器类型
 struct dentry * dentry; 
};

struct kobj_type {
 void (*release)(struct kobject *);
 struct sysfs_ops * sysfs_ops;                //指向sysfs操作表
 struct attribute ** default_attrs;         //sysfs文件系统的缺省属性链表
};

struct kset {
 struct subsystem * subsys;                            //指向subsystem描述符
 struct kobj_type * ktype;                              
 struct list_head list;                                      //包含在kset中的kobject链表的首部
 struct kobject kobj;                                     
 struct kset_hotplug_ops * hotplug_ops;    
};
    让kobject、kset、subsystem出现在sysfs子树中,就必须首先注册它们。Linux提供了一系列api注册、反注册对象、创建文件等,比如kobject_register、kset_register、sysfs_create_file()等。

设备驱动程序模型的组件
  • 设备(device)
  • 驱动程序(device_driver)
  • 总线(bus_type)
  • 类(class)

3、设备文件
    类Unix系统都是基于文件概念的。I/O设备当作设备文件(device file)特殊文件来处理。根据设备驱动程序的特性,设备文件可以分为块设备和字符设备。能够随机访问固定大小数据块(chunks)的设备称为 块设备,比如硬盘、软盘、闪存等。以字符流方式有序访问的设备称为 字符设备,比如串口和键盘。
    设备文件通常与硬件设备,或硬件设备的某一物理或逻辑分区相对应。设备文件通常由类型(字符or块)+主设备号+次设备号组成。通常把主设备号(12位,标识设备类型)和次设备号(20位,标志相同主设备号组中一个特定设备)组成一个32位的dev_t变量。MAJOR宏和MINOR宏可以分别提取主设备号和次设备号。
#define MINORBITS 20 
#define MINORMASK ((1U << MINORBITS) - 1) 

#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) 
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) 
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
    驱动程序在注册时,可以只指定设备号的分配范围,无需指定精确的值,内核会动态分配一个合适的设备号范围给驱动程序。通过udev工具,可以动态创建设备文件。
    隐藏设备文件与普通文件之间的差别:VFS。
    有些I/O设备没有对应的设备文件,比如网卡。类Unix系统为每个网卡分配一个不同的符号名,比如eth0,这个名字并没有对应的设备文件,也没有对应的索引节点。因此网络通信不是基于标准的文件系统调用,而是socket()、bing()、listen()、accept()等系统调用。

4、设备驱动程序

注册设备驱动程序
    在设备文件上发出的每个系统调用都由内核转化为相应设备驱动程序对应函数的调用,因此驱动程序需要注册,即:分配一个新的device_driver描述符,插入到设备驱动程序模型的数据结构中。
    以一个通用的PCI设备为例。设备驱动程序首先分配一个pci_driver类型的描述符,PCI内核层使用该描述符来处理设备。->->-> 初始化描述符一些字段 ->->-> 调用pci_register_driver()初始化pci_driver内嵌的device_driver ->->-> driver_register()将驱动程序插入设备驱动程序模型的数据结构中。
    注册设备驱动程序时,内核会寻找可能由该驱动程序处理但还未获得支持的硬件设备。

初始化设备驱动程序
    初始化设备驱动程序意味着系统资源的分配。( 是否在open时进行?
    引用计数器记录当前访问设备文件的进程数。open() ->->-> 若计数器为0,分配资源并激活硬件设备上的中断和DMA ->->-> 增加引用计数 ->->-> release()减少引用计数 ->->-> 若为0,禁止I/O控制器上的中断和DMA,释放资源。

监控I/O操作
     监控I/O操作结束的两种技术:轮询模式(polling mode)和中断模式(interrupt mode)。中断模式只有在I/O控制器能够通过IRQ线发出I/O操作结束信号的情况下才能使用。

访问I/O共享存储器
    根据设备和总线类型,I/O共享存储器可以被映射到不同的物理地址范围。内核程序作用于线性地址,因此I/O共享存储器单元必须大于PAGE_OFFSET的地址。i386架构下PAGE_OFFSET为0xC0000000,即第4个GB。
    设备驱动程序必须把I/O共享存储器单元的物理地址转换成内核空间的线性地址( how?)。

直接内存访问(DMA)
    PC都包含一个辅助的DMA电路,用来控制在RAM和I/O设备之间数据的传送。DMA一旦被CPU激活,就可自行传送数据;当数据传送完成后,DMA发出一个中断请求。DMA设置时间很长,所以在传送量少的数据时直接诶使用CPU效率更高。根据数据传送方式不同,分为同步DMA(由进程触发,比如声卡)和异步DMA(由硬件设备触发)。
    异步DMA例子如网卡。

     总线地址:除CPU之外的硬件设备驱动数据总线时所用的存储器地址。
    处理DMA缓冲区两种方式:一致性DMA映射和流式DMA映射。( 后续分析)    

5、字符设备驱动程序
    字符设备驱动程序描述结构体:cdev:
struct cdev { 
 struct kobject kobj;                        //
 struct module *owner;                   //指向实现驱动程序模块
 struct file_operations *ops;           //指向设备驱动程序文件操作表的指针
 struct list_head list;                       //与字符设备文件对应的索引节点链表的头
 dev_t dev;                                      //主设备号和次设备号
 unsigned int count;                     //设备号范围
};


reference
1、深入理解Linux内核-第3版
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值