免费注册 查看新帖 |

Chinaunix

广告
  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2201 | 回复: 1
打印 上一主题 下一主题

Linux设备模型 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-05-12 22:27 |只看该作者 |倒序浏览

Linux设备模型
一、底层数据结构:kobject,kset
2.6内核引入了sysfs文件系统,与proc, devfs, devpty同类别,属于虚拟的文件系统。目的是展示设备驱动模型中各组件的层次关系,第一层目录:block, device, bus, drivers, class, power, firmware.
block 块设备;devices 系统所有的设备并根据设备挂接的总线类型组织成层次结构;bus 系统所有的总线类型;drivers 内核中所有已经注册的设备驱动程序;class 系统中的设备类型(如网卡 设备、声卡
设备、输入设备等)。
在/sys/bus下和/sys/bus/下也会有设备文件,但那只是符号链接,它指向/sys/devices/下的真实设备。此即为Linux设备模型。

2   kobject是构成Linux2.6设备模型的核心结构,每个在内核中注册的kobject对象都对应于sysfs文件系统的一个目录。
kobject_add()用于将kobject对象加入linux设备层次,挂载该对象到kset的list链表中,增加父目录各级kobject的引用计数,在其parent指向的目录下创建文件节点,并启动该类型内核对象的hotplug函数。
结合面向对象的思维。这个kobject属于最基础的结构,也就是最高抽象层(有点像java中的Cobject类)。任何一个设备模型如总线,设备,驱动都属于一个kobject 。在实现上这种派生关系就是在结构体中
包含一个kobject的变量。类似于C++中的基类,它嵌入于更大的对象的对象中--所谓的容器--用来描述设备模型的组件。如bus,devices, drivers 都是典型的容器。这些容器就是通过kobject连接起来了,形
成了一个树状结构。这个树状结构就与/sys向对应。
这个在层次上处理最顶层的kobject结构提供了所有模型需要的最基本的功能:
1 引用计数  用于内核维护其存在与消亡
2 sysfs表示  每个sys/下的对象对应着一个kobject。
3 热拔插事件处理。 处理设备的热拔插事件。
Kobjects 在内核中对应有一套申请,初始化,添加,注册,计数操作,释放等函数
struct kobject {
const char *name;
struct list_head entry;//用于连接到同类kobjects的链表
struct kobject *parent;//用于实现层次,指向其父对象
struct kset *kset;//用于实现层次,所属的集合
struct kobj_type *ktype;//指向对象的类型
struct sysfs_dirent *sd;
struct kref  kref;//计数
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
};//kernel 2.6.29.1/include/linux/kobject.h
3  kobject通常通过kset组织成层次化的结构,包含在kset中的所有kobject被组织成一个双向循环链表, kset是具有相同类型的kobject的集合。与kobject相似,linux提供了一系统函数来操作kset。
Kset 在概念上是一个集合或者叫容器。实现了对象的层次。所有属于一个ksets的对象(kobject)的parent都指向该ksets的kobj.同时这个对象都连接到kset 的list表上。

kset最重要的是建立上层(sub-system)和下层的(kobject)的关联性。kobject 也会利用它了分辨自已是属于那一個类型,然後在/sys 下建立正确的目录位置。而kset 的优先权比较高,kobject会利用自已的
*kset 找到自已所属的kset,並把*ktype 指定成該kset下的ktype,除非沒有定义kset,才会用ktype來建立关系。Kobject通过kset组织成层次化的结构,kset是具有相同类型的kobject的集合,在内核中用
kset数据结构表示,定义为:
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;//自身的kobjects
struct kset_uevent_ops *uevent_ops;
};//kernel 2.6.29.1/include/linux/kobject.h
最后 属于同一个集合的对象可以拥有共同的属性:ktype 。
struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};//kernel 2.6.29.1/include/linux/kobject.h
所谓的属性更具体一点说就是一些键值对。并且在sysfs_ops中的show函数被文件系统调用来显示sys/下面对应入口各属性的值。
如此 ,kobjects与ksets实现层次树的底层骨架。
进一步地,通过封装这些底层结构来实现上层的设备驱动模型。
内核设备驱动模型层次划分三个方面:总线,设备,驱动。
4  subsystem是一系列kset的集合,描述某一类设备子系统,如block_subsys表示所有的块设备。所有挂接到同一subsystem的kset共享同一个rwsem信号量,用于同步访问kset中的链表。内核也提供了一
系列操作subsystem的函数。
subsystem与kset的区别就是多了一个信号量,所以在后来的代码中,subsystem已经完全被kset取缔了。linux kernel 2.6.29.1里就没有。
二、linux设备模型层次关系:bus_type,device,device_driver
1  系统中的任一设备在设备模型中都有一个device对象描述。通常不单独使用,而是包含在一个更大的结构体中。
系统中每个驱动程序都有一个device_driver对象描述。
系统中总线由struc bus_type描述。每个bus_type对象都对应/sys/bus目录下的一个子目录,如PCI对应/sys/bus/pci,每个这样的目录下都存在2个子目录:devices/, drivers/,分别对应于bus_type结构体
中的devices和drivers域。其中devices子目录描述连接在该总线上的所有设备,而drivers则描述与该总线关联的所有驱动程序。
struct class 描述某一类设备,所有的class对象都属于class_subsys子系统,对应于/sys/class目录。每个class对象包括一个class_device链表,每个class_device对象表示一个逻辑设备,并通过struct
class_device中的dev成员(一个指向struct device的指针)关联一个物理设备。这样,一个逻辑设备总对应于一个物理设备,但一个物理设备却可能对应多个逻辑设备。
2 基本关系简要的概括如下:
驱动核心可以注册多种类型的总线。
每种总线下面可以挂载许多设备。(通过kset devices)
每种总线下可以用很多设备驱动。(通过包含一个kset drivers)}
每个驱动可以处理一组设备。
这种基本关系的建立源于实际系统中各种总线,设备,驱动结构的抽象。
下面看看三者数据结构的定义。
首先是总线,bus_type.
struct bus_type {
const char  *name;
struct bus_attribute  *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
int (*match)(struct device *dev, struct device_driver *drv);//设备驱动匹配函数
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);//热拔插事件
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*suspend_late)(struct device *dev, pm_message_t state);
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
struct dev_pm_ops *pm;
struct bus_type_private *p;
};//kernel 2.6.29.1/include/linux/device.h
下面是设备device的定义:
struct device {
struct klist  klist_children;
struct klist_node knode_parent; /* node in sibling list */
struct klist_node knode_driver;
struct klist_node knode_bus;
struct device *parent;//父设备,一般一个bus也对应一个设备
struct kobject  kobj;//代表自身
char  bus_id[BUS_ID_SIZE]; /* position on parent bus */
unsigned  uevent_suppress:1;
const char *init_name; /* initial name of the device */
struct device_type *type;
struct semaphore sem; /* semaphore to synchronize calls to its driver. */
struct bus_type *bus;  /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this device */
void  *driver_data; /* data private to the driver */
void  *platform_data; /* Platform specific data, device  core doesn't touch it */
dev_t  devt; /* dev_t, creates the sysfs "dev" */
spinlock_t  devres_lock;
struct class *class;
void (*release)(struct device *dev);
......
};//kernel 2.6.29.1/include/linux/device.h
下面是设备驱动定义:
struct device_driver {
const char  *name;
struct bus_type  *bus;
struct module  *owner;
const char   *mod_name; /* used for built-in modules */
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
struct attribute_group **groups;
struct dev_pm_ops *pm;
struct driver_private *p;
};
下面通过PCI驱动中设备模型的实例来看看细节。源码来自linux kernel 2.6.29.1
三、PCI设备驱动模型实例及设备,设备驱动注册源码的简单分析。围绕总线、设备、驱动程序三方面。
先看pci总线类型定义:
struct bus_type pci_bus_type = { //bus_type类型前面已定义,此时定义了一个对象(实例)pci_bus_type,后面__pci_register_driver()里用到
.name  = "pci",
.match  = pci_bus_match,
.uevent  = pci_uevent,
.probe  = pci_device_probe,
.remove  = pci_device_remove,
.shutdown = pci_device_shutdown,
.dev_attrs  = pci_dev_attrs,
.pm  = PCI_PM_OPS_PTR,
};//kernel 2.6.29.1/drivers/pci/pci-driver.c
然后是pci设备和驱动。pci设备和pci驱动没有直接使用device和device_driver,而是将二者封装起来,加上pci特定信息构成pci_dev和pci_driver。当然,意义是一样的。
struct pci_dev {   //没有直接用device 类型来定义一个PCI的设备对象,而是封装了它
struct list_head bus_list; /* node in per-bus list */
struct pci_bus *bus;  /* bus this device is on */  //所属pci总线
struct pci_bus *subordinate; /* bus this device bridges to */
void  *sysdata; /* hook for sys-specific extension */
struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */
struct pci_slot *slot;  /* Physical slot this device is in */
unsigned int devfn;  /* encoded device & function index */
unsigned short vendor;
unsigned short device;
unsigned short subsystem_vendor;
unsigned short subsystem_device;
unsigned int class;  /* 3 bytes: (base,sub,prog-if) */
u8  revision; /* PCI revision, low byte of class word */
u8  hdr_type; /* PCI header type (`multi' flag masked out) */
u8  pcie_type; /* PCI-E device/port type */
u8  rom_base_reg; /* which config register controls the ROM */
u8  pin;    /* which interrupt pin this device uses */
struct pci_driver *driver; /* which driver has allocated this device */  //所属的pci驱动
struct device dev;  /* Generic device interface */ //设备自身
/* ... */  
}; //kernel 2.6.29.1/include/linux/pci.h
当一个PCI 设备被发现, PCI 核心在内存中创建一个 struct pci_dev 类型的新变量。这个 PCI 设备的总线特定的成员被 PCI 核心初始化( devfn, vendor, device, 和其他成员), 并且 struct device 变量的 parent
变量被设置为 PCI 总线设备(注意总线也不仅有一个bus_type 结构,还对应一个设备device) bus 变量被设置指向 pci_bus_type 结构. 接下来 name 和 bus_id 变量被设置, 根据读自 PCI 设备的 name 和 ID.
在 PCI 设备结构被初始化之后, pci设备被注册到驱动核心, 调用 device_register(&dev->dev); 在device_register函数中,kobject被注册到驱动核心,pci设备被添加到pci总线的设备列表中,热拔插事件产生,
同时kobject被添加到parent的链表中,sysfs入口也被添加。
PCI设备的发现是通过特定代码探测PCI空间来实现的。PCI设备由内核自动生成的。这样在注册pci驱动的时候PCI设备已经注册,其属性如ID的信息都已经是被初始化好了。
struct pci_driver { //__pci_register_driver()里会初始化
struct list_head node;
char *name;
const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */  /* 驱动支持的设备ID列表 */
int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int  (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */
int  (*suspend_late) (struct pci_dev *dev, pm_message_t state);
int  (*resume_early) (struct pci_dev *dev);
int  (*resume) (struct pci_dev *dev);                 /* Device woken up */
void (*shutdown) (struct pci_dev *dev);
struct pci_error_handlers *err_handler;
struct device_driver driver; //设备驱动
struct pci_dynids dynids;
};//kernel 2.6.29.1/include/linux/pci.h
这里列出了pci_bus,pci_dev,pci_driver的定义。它们的关系与bus,device,driver一样。pci_bus直接是一个bus_type结构初始化的实体。
pci_dev由内核探测,并且注册到驱动核心。pci设备的初始化和注册分两个方面,一是pci设备信息如ID,资源等,二是pci_dev.dev的注册。调用register_device(struct  device * dev)来完成。
pci_driver一般由模块定义并且在模块初始化函数中向内核注册。也要两个方面,一是pci_driver中特定于PCI的方法,支持的ID列表等的初始化;二是内嵌的device_driver的注册,使用register_driver(struct
device_driver * drv)。
这就有点像面向对象中子类与父类的关系,子类构造函数的调用隐含父类构造函数的调用。
没有register_device(dev)和register_driver(drv)的注册,驱动核心就不知道设备和驱动的存在,sysfs也没有相关的入口。
对总线上的每个设备dev,调用__driver_attach(dev,drv);最终调用
driver_probe_device(drv, dev);
driver_register(drv);-->bus_add_driver(drv);-->driver_attach(drv);
-->__driver_attach(dev,drv);-->driver_probe_device(drv, dev);
//kernel 2.6.29.1/drivers/pci/pci-driver.c
static int __init pci_driver_init(void)
{
return bus_register(&pci_bus_type);
}
int __pci_register_driver(struct pci_driver *drv, struct module *owner, const char *mod_name)
{
int error;
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type; //前面定义的bus_type对象
drv->driver.owner = owner;
drv->driver.mod_name = mod_name;
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
/* register with core */
error = driver_register(&drv->driver); //注册
if (error)
  return error;
error = pci_create_newid_file(drv);
if (error)
  driver_unregister(&drv->driver);
return error;
}
EXPORT_SYMBOL(__pci_register_driver);
参考文献:
http://blog.mcuol.com/User/lvembededsys/Article/6820_1.htm
  “ Linux设备模型 学习总结 ”
《Linux设备驱动开发详解》(宋宝华)
http://blog.csdn.net/fudan_abc/category/332148.aspx
“Linux那些事儿 之 我是SysfsLinux内核中设备模型sysfs分析”
推荐博文:
浅析linux 2.6.23驱动注册函数driver_register()   
http://blog.chinaunix.net/u1/38994/showart_407974.html
Linux下PCI设备驱动程序开发   
http://www.ibm.com/developerworks/cn/linux/l-pci/index.html


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/96613/showart_1926309.html

论坛徽章:
0
2 [报告]
发表于 2012-08-07 11:25 |只看该作者
不错,参考文献也去看下!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP