字符设备的内核抽象
2017-12-29 10:03
260 查看
Linux内核中处处体现面向对象的设计思想,为了统一形形色色的设备,Linux系统将设备分为三类:字符设备、块设备、网络设备。并将其分别抽象为struct cdev,struct block_device,struct net_devce三个对象,具体的设备都可以包含着三种对象从而继承和三种对象属性和操作,并通过各自的对象添加到相应的驱动模型中,从而进行统一的管理和操作。
顾名思义,字符设备驱动程序管理的是核心对象是字符设备。从字符设备的设计框架角度出发,内核为字符设备抽象出了一个具体的数据结构struct cdev,定义如下:
各成员描述如下:
struct kobject kobj
内嵌的内核对象,通过它将设备统一加入到“Linux设备驱动模型”中管理(如对象的引用计数、电源管理、热插拔、生命周期、与用户通信等)。
struct module *owner
字符设备驱动程序所在的内核模块对象的指针。
const struct file_operations *ops
文件操作,是字符设备驱动中非常重要的数据结构,在应用程序通过文件系统(VFS)呼叫到设备设备驱动程序中实现的文件操作类函数过程中,ops起着桥梁纽带作用,VFS与文件系统及设备文件之间的接口是file_operations结构体成员函数,这个结构体包含了对文件进行打开、关闭、读写、控制等一系列成员函数。
struct list_head list
用于将系统中的字符设备形成链表(这是个内核链表的一个链接因子,可以再内核很多结构体中看到这种结构的身影)。
dev_t dev
字符设备的设备号,有主设备和次设备号构成。
unsigned int count
属于同一主设备好的次设备号的个数,用于表示设备驱动程序控制的实际同类设备的数量。
以下是具体设备与cdev的关系:
我们知道了具体的字符设备可以通过cdev来管理字符设备,那么我们如何能够在驱动中通过cdev来找到我们具体的设备呢?C语言中没有面向对象语言的继承的语法,但是我们可以通过结构体的包含来实现继承,例如一个字符设备adc_dev 结构体如下(这个一个能够进行ad采集设备):
可以看到,这个设备结构体中包含着操作此设备的内存信息、中断信息等,其中就嵌入了cdev结构体,然后通过字符设备的驱动框架就可以将此设备注册进Linux系统。
一般在驱动中我们很容易就能知道cdev指针的值,那么我们可以通过cdev来找到设备结构体adc_dev,内核提供了如下的宏接口:
此接口的具体实现在这不在赘述,详见内核链表相关文章。
现在我们来使用此接口获得我们的设备结构体:
int adc_open(struct inode *inode,struct file *filep)
{
struct adc_dev adc_devp;
adc_devp = container_of(inode->i_cdev, struct cdev, cdev);
…
return 0;
}
非常简单是不是,其中container_of接口的ptr是内嵌的cdev的内存地址,type为数据类型此处为struct cdev,member为内嵌的成员名此处为cdev。
由于Linux系统要管理成百上千中设备,所以这种抽象非常重要且有必要,它提取并抽象了设备的共性,为上层提供统一接口,使得管理和操作设备变的容易。
顾名思义,字符设备驱动程序管理的是核心对象是字符设备。从字符设备的设计框架角度出发,内核为字符设备抽象出了一个具体的数据结构struct cdev,定义如下:
#include <linux/cdev.h> struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; };
各成员描述如下:
struct kobject kobj
内嵌的内核对象,通过它将设备统一加入到“Linux设备驱动模型”中管理(如对象的引用计数、电源管理、热插拔、生命周期、与用户通信等)。
struct module *owner
字符设备驱动程序所在的内核模块对象的指针。
const struct file_operations *ops
文件操作,是字符设备驱动中非常重要的数据结构,在应用程序通过文件系统(VFS)呼叫到设备设备驱动程序中实现的文件操作类函数过程中,ops起着桥梁纽带作用,VFS与文件系统及设备文件之间的接口是file_operations结构体成员函数,这个结构体包含了对文件进行打开、关闭、读写、控制等一系列成员函数。
struct list_head list
用于将系统中的字符设备形成链表(这是个内核链表的一个链接因子,可以再内核很多结构体中看到这种结构的身影)。
dev_t dev
字符设备的设备号,有主设备和次设备号构成。
unsigned int count
属于同一主设备好的次设备号的个数,用于表示设备驱动程序控制的实际同类设备的数量。
以下是具体设备与cdev的关系:
我们知道了具体的字符设备可以通过cdev来管理字符设备,那么我们如何能够在驱动中通过cdev来找到我们具体的设备呢?C语言中没有面向对象语言的继承的语法,但是我们可以通过结构体的包含来实现继承,例如一个字符设备adc_dev 结构体如下(这个一个能够进行ad采集设备):
struct adc_dev { unsigned int __iomem *adccon; unsigned int __iomem *adcdat; unsigned int __iomem *clrint; unsigned int __iomem *adcmux; unsigned int adcval; struct completion completion; atomic_t available; unsigned int irq; struct cdev cdev; };
可以看到,这个设备结构体中包含着操作此设备的内存信息、中断信息等,其中就嵌入了cdev结构体,然后通过字符设备的驱动框架就可以将此设备注册进Linux系统。
一般在驱动中我们很容易就能知道cdev指针的值,那么我们可以通过cdev来找到设备结构体adc_dev,内核提供了如下的宏接口:
#define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) * __mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); })
此接口的具体实现在这不在赘述,详见内核链表相关文章。
现在我们来使用此接口获得我们的设备结构体:
int adc_open(struct inode *inode,struct file *filep)
{
struct adc_dev adc_devp;
adc_devp = container_of(inode->i_cdev, struct cdev, cdev);
…
return 0;
}
非常简单是不是,其中container_of接口的ptr是内嵌的cdev的内存地址,type为数据类型此处为struct cdev,member为内嵌的成员名此处为cdev。
由于Linux系统要管理成百上千中设备,所以这种抽象非常重要且有必要,它提取并抽象了设备的共性,为上层提供统一接口,使得管理和操作设备变的容易。
相关文章推荐
- 字符设备的内核抽象
- 字符设备的内核抽象
- 字符设备的内核抽象
- Linux内核大讲堂 (二) 传说中的字符设备(4)
- 字符设备驱动内核框架小结(一)
- linux驱动开发之字符设备--内核和用户空间数据的交换(read write)
- LED写成字符设备并加入内核的方法
- 我的内核学习笔记1:字符设备完善
- 字符设备驱动内核框架小结
- tony之linux driver_LDD3_scull字符设备驱动编译在新内核编译问题
- 高级字符设备驱动-内核等待队列笔记
- android内核字符驱动设备实战之----------应用框架层aidl服务编程篇
- 2.6内核字符设备驱动程序解析
- Linux内核大讲堂 (二) 传说中的字符设备(3)
- 字符设备驱动在内核源码中的分析
- 内核字符设备驱动框架
- Linux 内核--总线设备驱动模型(字符/块/网络设备 && platform设备)
- linux内核之字符设备驱动图解
- Linux 内核模块编程的第一个字符设备驱动
- 编写字符设备驱动实现内核态与用户态通信