免费注册 查看新帖 |

Chinaunix

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

ldd3 read note Capter 14 上 [复制链接]

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

                    
   
   
2009.9.14

The Linux Device Model

这个抽象模型支持:
Power 管理和系统 shut down
电源管理需要知道系统的拓扑,必须USB HC不能在有设备连接的时候shut down. Linux现有设备模型(从2.6开始)可以提供遍历系统设备的方法.
和user sapce 交互
sysfs, 越来越多的设备使用sysfs来改变参数,而抛弃原来的dev 模式.
Hotpluggable devices
和user space交互 plug/unplugging消息. (uevent)
Device classes
设备分类.
Object lifecycles
各种设备生命周期/相顾关系的抽象管理
有图为例



Kobjects, Ksets, and Subsystems
struct kobject {
//最初只是引用计数,但是现在复杂多了.
Reference counting of objects:
  提供引用计数
Sysfs representation
   提供sysfs接口
Data structure glue
   提供'设备关系'管理的各种工具,数据结构
Hotplug event handling
    uevent
}
Kobject Basics

Embedding kobjects

kobject 就是要嵌入在其他的设备结构中,以使其和linux提供的设备模型关联起来.  比如cdev
struct cdev {
   struct kobject kobj;
   struct module *owner;
   struct file_operations *ops;
   struct list_head list;
   dev_t dev;
    unsigned int count;
};
将指向kobject的指针kp转换成cdev:
struct cdev *device = container_of(kp, struct cdev, kobj);
Kobject initialization
1. 将所有域清零.
2. 调用kobject_init , 引用计数被置成1
3. 至少要设置名子: int kobject_set_name(struct kobject *kobj, const char *format, ...);
4. 其他应该设置的域: ktype, kset,  parent.
Reference count manipulation
struct kobject *kobject_get(struct kobject *kobj); //必须检查返回值, 在销毁中的kobj就会返回NULL
void kobject_put(struct kobject *kobj);
对应模块的应用技术也得保持好.如cdev:
struct kobject *cdev_get(struct cdev *p)
{
   struct module *owner = p->owner;
   struct kobject *kobj;
   if (owner && !try_module_get(owner))
           return NULL;
    kobj = kobject_get(&p->kobj);
    if (!kobj)
         module_put(owner);
    return kobj;
}

Release functions and kobject types

因为, sysfs, 以及open和close, driver是不知道自己什么时候引用技术到0的, 故而, kobj提供了call back: release. 就下下面这样:
void my_object_release(struct kobject *kobj)
{
   struct my_object *mine = container_of(kobj, struct my_object, kobj);
   .....
  /* Perform any additional cleanup on this object, then... */
   kfree(mine);
}
release函数是必须有的, 调用release之前是不能释放kobj的..
这个relase 函数是存储在ktype中的:
struct kobj_type {
   void (*release)(struct kobject *);
   struct sysfs_ops *sysfs_ops;
   struct attribute **default_attrs;
};
kobj需要关连一个ktype, 有两种方式: 直接用kobj内的ktype指针, 但是如果kobj是属于一个kset的,那么, ktype将由kset来提供, 函数
struct kobj_type *get_ktype(struct kobject *kobj);
用于正确的获取ktype.

Kobject Hierarchies, Ksets, and Subsystems

体系中有两种kobj之间的连接关系: parent指针, 和kset.   parent的例子: usb设备的parent或许是一个hub.  parent指针用于指导设备在sysfs中的位置.
Ksets
kset用于代表一组相同type的kobj, 他自己也是kboject. kset就是提供给sysfs使用的,别无他用.(一个目录,kobject出现的时候都是目录,只有attr是文件). koject无须出现自sysfs中,但是kset管理的obj都通过kset出现在sysfs里.
一般在kobject创建的时候加入kset. 两个步骤:
1.) 填充kobject的kset指针
2) 调用 int kobject_add(struct kobject *kobj);
extern int kobject_register(struct kobject *kobj);是kobject_init and kobject_add的组合.
从kset删除: void kobject_del(struct kobject *kobj);
典型的kset和他聚合的kobject关系如下图:

1. 每个kobject都是嵌入在一个具体的type内的
2. 其parent不一定是那个kset容器, 但是这种情况很少.

kset 操作函数

void kset_init(struct kset *kset);
int kset_add(struct kset *kset);
int kset_register(struct kset *kset);
void kset_unregister(struct kset *kset);
这些函数一般是操作kset内的kobject的.
struct kset *kset_get(struct kset *kset);
void kset_put(struct kset *kset);
kobject_set_name(&my_set->kobj, "The name");
上面提到了, kset内的ktype指针指向共同的ktype. 这些被聚合的kobject的ktype指针保持为NULL.(一般情况) . kset 还有一个指向subsystem的指针.
kset 补充说明:
Subsystems
在/sys目录下的直接子目录一般就是subsystem了.  一个subsysem其实就是一个kset.  
struct subsystem {
   struct kset kset;
   struct rw_semaphore rwsem;
};
kset必须属于一个subsystem, kset内有指针直接找到所属susbsystem, 但是从subsys不能简单找到属于他的所有的kset.  subsys内的rwsem 保护kset的内部链表.
声明一个subsystem的方式:
decl_subsys(name, struct kobj_type *type, struct kset_hotplug_ops *hotplug_ops);
subsystem的操作函数大部分也四操作其内嵌的kset:
void subsystem_init(struct subsystem *subsys);
int subsystem_register(struct subsystem *subsys);
void subsystem_unregister(struct subsystem *subsys);
struct subsystem *subsys_get(struct subsystem *subsys)
void subsys_put(struct subsystem *subsys);

kset/subsystem的补充说明

注: 新的内核中, 采用下面的函数动态创建subsystem居多,而不是用上面提到的宏.
    kset_create_and_add
通过这种方式创建的kset还有: classes_init. devices_init, slab_sysfs_init, param_sysfs_init /*module*/
另外的几个目录, fs, kernel 就是一个kobject, 而非一个kset, 搜索方法:
bash#> grep  kobject_create_and_add . -r  | grep "NULL"
./arch/powerpc/platforms/pseries/power.c:    power_kobj = kobject_create_and_add("power", NULL);
./kernel/power/main.c:    power_kobj = kobject_create_and_add("power", NULL);
./kernel/ksysfs.c:    kernel_kobj = kobject_create_and_add("kernel", NULL);
./fs/namespace.c:    fs_kobj = kobject_create_and_add("fs", NULL);
./block/genhd.c:    block_depr = kobject_create_and_add("block", NULL);
./drivers/base/firmware.c:    firmware_kobj = kobject_create_and_add("firmware", NULL);
./drivers/base/core.c:    dev_kobj = kobject_create_and_add("dev", NULL);
./drivers/base/hypervisor.c:    hypervisor_kobj = kobject_create_and_add("hypervisor", NULL);
struct kset *kset_create_and_add(const char *name,
                 struct kset_uevent_ops *uevent_ops,
                 struct kobject *parent_kobj)
{
    struct kset *kset;
    int error;
    kset = kset_create(name, uevent_ops, parent_kobj); /*kset 内嵌的obj在 buses_init时, 父obj是NULL*/
                                                       /*同时, kset.kobj.kset是NULL, kset自己不属于kset*/
    if (!kset)
        return NULL;
    error = kset_register(kset);/*[color="#0000ff"]kset.kobj的其parent和kset都是NULL,所以创建的目录在sysfs_root下,见kobject_add_internal*/
    if (error) {
        kfree(kset);
        return NULL;
    }
    return kset;
}
在kobject_add_internal里有这样的策略: 如果一个kobj属于一个kset, 并且没有指定这个kobj的parent, 则这个kobj就位于kset.obj所对应的那个目录下.
static int kobject_add_internal(struct kobject *kobj)
{
    int error = 0;
    struct kobject *parent;
......
    parent = kobject_get(kobj->parent);
    /* join kset if set, use it as parent if we do not already have one */
    if (kobj->kset) {
        if (!parent)
            parent = kobject_get(&kobj->kset->kobj);
        kobj_kset_join(kobj);  /*挂入kset链表而已*/
        kobj->parent = parent;
    }
......
    error = create_dir(kobj);
}
Low-Level Sysfs Operations
* 每个kobject,kset 对应sysfs的一个目录
* kobject的attr对应目录内的一个文件
* 应该包含
* kobject_add 复杂将kobject加入sysfs,上面kobject加入kset的时候就调用了这个函数
*上面的补充说明已经说过了: 如果没有parent也没有kset,出现在sysfs根目录, 有parent就出现在parent内, 如果不指定parent, 一般指向所属的kset(的内嵌kobject)
Default Attributes
属性在ktype中指定.
struct kobj_type {
    void (*release)(struct kobject *);
    struct sysfs_ops *sysfs_ops;
    struct attribute **default_attrs;
};
struct attribute {
   char *name;
   struct module *owner;
    mode_t mode;  //S_IRUGO for read-only, |S_IWUSR
};
struct sysfs_ops { //提供属性的操作函数
   ssize_t (*show)(struct kobject *kobj, struct attribute *attr,  char *buffer); //输出buffer 是 page size,返回输出总字节数
   ssize_t (*store)(struct kobject *kobj, struct attribute *attr,   const char *buffer, size_t size);//返回已经解码的字节数
};
Nondefault Attributes
int sysfs_create_file(struct kobject *kobj, struct attribute *attr); //成功返回0
int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);
//逻辑删除(对应文件就不再出现在/sys目录了),已经打开的还可以继续用,就是有user正在访问的话,直到user close才能真正的删除
Binary Attributes
比如下载firmeware, 就要binary了.
struct bin_attribute {
   struct attribute attr;
   size_t size; //最大大小
    ssize_t (*read)(struct kobject *kobj, char *buffer,loff_t pos, size_t size);
    ssize_t (*write)(struct kobject *kobj, char *buffer, loff_t pos, size_t size);
};
只能动态注册,需要有办法确定何时数据发送完毕. 一次一个page的数据.
int sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr);
int sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);
Symbolic Links

int sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name); 给kobj创建一个属性, 一个名字为name,指向target的符号连接.
void sysfs_remove_link(struct kobject *kobj, char *name);
Hotplug Event Generation

就是从kernel调用user space 的/sbin/hotplug, 这个功能由设备模型中的uevent来完成.  事件产生的时机是kobject_add or kobject_del. 的时候.

Hotplug Operations
struct kset_hotplug_ops {
   int (*filter)(struct kset *kset, struct kobject *kobj); /*如果返回0, 对应事件不报给user space*/
   char *(*name)(struct kset *kset, struct kobject *kobj); /*产生传递给user space程序的唯一一个参数:子系统名称*/
   int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp, int num_envp, char *buffer, int buffer_size);
     //其他参数,通过环境变量传递,这里产生所有需要的环境变量 : NAME=value , 正常情况返回0, 否则事件abort
};
如果当前object 的kset内没有 hotplug操作,就顺着parent向上找,知道有个kset设置了hotplug操作.
就调用这个找到的hotplug函数. 比如硬盘, 至少有block设备, 分区,和request queue几个kobject.
但是请求队列是没人在意的, 故而可以如下写fileter:
static int block_hotplug_filter(struct kset *kset, struct kobject *kobj)
{
   struct kobj_type *ktype = get_ktype(kobj);
   return ((ktype = = &ktype_block) || (ktype = = &ktype_part));
}
Buses, Devices, and Drivers

尝试建立lddbus, 将scullp driver连接到这个bus.
Buses
bus可以连接到另一个bus,比如usb基本都是连接到PCI的, 而soc系统, usb一般连接到虚拟的platform bus.
*
*
struct bus_type { //bus本身属于子系统 /sys/bus
    char *name; //bus 名字
    struct subsystem subsys;  //bus本身是一个子系统(就是sysfs中的一个目录)
    struct kset drivers; //属于这个bus的driver   (新的内核中移入bus_type_private)
    struct kset devices; //属于这个bus的device  (新的内核中移入bus_type_private)
    int (*match)(struct device *dev, struct device_driver *drv);
    struct device *(*add)(struct device * parent, char * bus_id);
    int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
    /* Some fields omitted */
}
Bus registration
struct bus_type ldd_bus_type = {
   .name = "ldd",
   .match = ldd_match,
   .hotplug = ldd_hotplug,
};
ret = bus_register(&ldd_bus_type);
if (ret)
   return ret;
void bus_unregister(struct bus_type *bus); //删除bus前,driver和设备当然得都被正确的卸载掉.
Bus methods

int (*match)(struct device *device, struct device_driver *driver); //入果driver可以处理这个device,返回非零值.
int (*hotplug) (struct device *device, char **envp, int num_envp, char *buffer, int buffer_size);
//bus层的hotplug给uevent添加环境变量, 如
static int ldd_hotplug(struct device *dev, char **envp, int num_envp,char *buffer, int buffer_size)
{
   envp[0] = buffer;
   if (snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s",   Version) >= buffer_size)
        return -ENOMEM;
    envp[1] = NULL;
    return 0;
}
Iterating over devices and drivers
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *));
如果fn返回非0值,遍历终止,这个值被传递给bus_for_each_dev的返回值.   data传递给fn.
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *));
这两个函数都会对bus的rwsem加锁.
Bus attributes
struct bus_attribute { //和kobject类似,只是每个属性都有单独的show,store
   struct attribute attr;
   ssize_t (*show)(struct bus_type *bus, char *buf);
   ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};
BUS_ATTR(name, mode, show, store); 用于帮助定义属性,
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr); //所有属性必须手动调用注册函数.
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr); //反之依然
如lddbus的属性
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
    return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
.....
if (bus_create_file(&ldd_bus_type, &bus_attr_version))
   printk(KERN_NOTICE "Unable to create version attribute\n");
....
Devices

struct device {
   struct device *parent; //所连接到的设备/总线/hub
   struct kobject kobj;   //这个设备本身的kobject,device->kobj->parent 应该等于 &device->parent->kobj.
   char bus_id[BUS_ID_SIZE]; //设备唯一标识,比如PCI和usb的那个标准ID字符串
   struct bus_type *bus;  //连接到的bus
   struct device_driver *driver;  //谁管理这个设备
   void *driver_data;  //自己存的cookie
   void (*release)(struct device *dev); //实际上是从内嵌的kobj的release内调用的.
/* Several fields omitted */
};
Device registration
int device_register(struct device *dev);
void device_unregister(struct device *dev);
ldd bus 本身也是一个设备,看他的注册:
static void ldd_bus_release(struct device *dev)
{
    printk(KERN_DEBUG "lddbus release\n");
}
struct device ldd_bus = { //top level的bus, 所以parent和bus指针为空.
   .bus_id = "ldd0",
   .release = ldd_bus_release  
};
ret = device_register(&ldd_bus);
if (ret)
   printk(KERN_NOTICE "Unable to register ldd0\n");
注册完成后, 设备目录出现在/sys/devices/ldd0, 以后的设备也出现在这里.

Device attributes
struct device_attribute {
   struct attribute attr;
   ssize_t (*show)(struct device *dev, char *buf);
   ssize_t (*store)(struct device *dev, const char *buf, size_t count);
};
DEVICE_ATTR(name, mode, show, store);
int device_create_file(struct device *device, struct device_attribute *entry);
void device_remove_file(struct device *dev, struct device_attribute *attr);
Device structure embedding

一般, 设备也是作为内嵌对象使用的, 供第三级设备模型使用,如pci_dev, usb_dev.  ldd也可以创建自己的设备类型:
struct ldd_device {
   char *name;
   struct ldd_driver *driver;
   struct device dev;
};
#define to_ldd_device(dev) container_of(dev, struct ldd_device, dev);
int register_ldd_device(struct ldd_device *ldddev)
{
    ldddev->dev.bus = &ldd_bus_type;
    ldddev->dev.parent = &ldd_bus;
    ldddev->dev.release = ldd_dev_release;
    strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE);
    return device_register(&ldddev->dev);
}
EXPORT_SYMBOL(register_ldd_device);

Device Drivers

struct device_driver {
   char *name;  //driver 名称
   struct bus_type *bus; //驱动的设备所属bus, 含有此bus上driver的共有属性
   struct kobject kobj; //driver的kobj,
   struct list_head devices; //用这个driver的设备
   int (*probe)(struct device *dev); //探测设备是否存在
   int (*remove)(struct device *dev); //设备从系统移除时调用
   void (*shutdown) (struct device *dev); //系统shutdown时调用
   ....
};
int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);
struct driver_attribute {
   struct attribute attr;
   ssize_t (*show)(struct device_driver *drv, char *buf);
   ssize_t (*store)(struct device_driver *drv, const char *buf, size_t count);
};
DRIVER_ATTR(name, mode, show, store);
int driver_create_file(struct device_driver *drv, struct driver_attribute *attr);
void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr);
Driver structure embedding
driver上面也是还有包装的. 以ldd为例
struct ldd_driver {
    char *version;
    struct module *module;
    struct device_driver driver;
    struct driver_attribute version_attr;
};
#define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);
int register_ldd_driver(struct ldd_driver *driver)
{
   int ret;
   driver->driver.bus = &ldd_bus_type;
   ret = driver_register(&driver->driver);
   if (ret)
      return ret;
    driver->version_attr.attr.name = "version";
    driver->version_attr.attr.owner = driver->module;
    driver->version_attr.attr.mode = S_IRUGO;
    driver->version_attr.show = show_version;
    driver->version_attr.store = NULL;
    return driver_create_file(&driver->driver, &driver->version_attr);
}
sculld 创建的 ldd_driver:
static struct ldd_driver sculld_driver = {
    .version = "$Revision: 1.1 $",
     .module = THIS_MODULE,
     .driver = {
     .name = "sculld",
},
};
调用register_ldd_driver 后:
$ tree /sys/bus/ldd/drivers
/sys/bus/ldd/drivers
`-- sculld
|-- sculld0 -> ../../../../devices/ldd0/sculld0
|-- sculld1 -> ../../../../devices/ldd0/sculld1
|-- sculld2 -> ../../../../devices/ldd0/sculld2
|-- sculld3 -> ../../../../devices/ldd0/sculld3
`-- version
Classes
*/sys/class, 功能分类. 除了特殊的/sys/block. dirver是不用做专门的支持的,高层代码自动处理.
*如果driver 创建class, 则自己完全拥有他, 不用考虑其他模块的引用问题, 并且user interface很友好.

simple calss 接口
(提供设备号给user space)
struct class_simple *class_simple_create(struct module *owner, char *name); //用IS_ERR检查返回值
void class_simple_destroy(struct class_simple *cs);
struct class_device *class_simple_device_add(struct class_simple *cs,  dev_t devnum,
struct device *device, //如果不为NULL, 则符号连接到 /sys/device
const char *fmt, ...);
int class_simple_set_hotplug(struct class_simple *cs, int
(*hotplug)(struct class_device *dev, char **envp, int num_envp, char
*buffer, int buffer_size));
void class_simple_device_remove(dev_t dev);
The Full Class Interface
struct class {
   char *name; //唯一的名称,/sys/class
   struct class_attribute *class_attrs; //class属性
   struct class_device_attribute *class_dev_attrs;  //calss 对应设备的属性
   int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
   void (*release)(struct class_device *dev); //设备删除时调用
   void (*class_release)(struct class *class);  //calss 本身删除时被调用
/* Some fields omitted */
};
int class_register(struct class *cls);
void class_unregister(struct class *cls);
struct class_attribute {
    struct attribute attr;
    ssize_t (*show)(struct class *cls, char *buf);
    ssize_t (*store)(struct class *cls, const char *buf, size_t count);
};
CLASS_ATTR(name, mode, show, store);
int class_create_file(struct class *cls, const struct class_attribute *attr);
void class_remove_file(struct class *cls, const struct class_attribute *attr);

Class devices

struct class_device {
   struct kobject kobj;
   struct class *class;
   struct device *dev; //符号连接到 /sys/device, 如果非空
   void *class_data;
   char class_id[BUS_ID_SIZE];
};
int class_device_register(struct class_device *cd);
void class_device_unregister(struct class_device *cd);
int class_device_rename(struct class_device *cd, char *new_name);
struct class_device_attribute {
   struct attribute attr;
   ssize_t (*show)(struct class_device *cls, char *buf);
   ssize_t (*store)(struct class_device *cls, const char *buf,
   size_t count);
};
CLASS_DEVICE_ATTR(name, mode, show, store);
int class_device_create_file(struct class_device *cls, const struct class_device_attribute *attr);
void class_device_remove_file(struct class_device *cls, const struct class_device_attribute *attr);
Class interfaces
在class中有变化的时候, 得到一个机会处理设备.
struct class_interface {
   struct class *class;
   int (*add) (struct class_device *cd); //一般是给设备添加更多的属性
   void (*remove) (struct class_device *cd);
};
int class_interface_register(struct class_interface *intf);
void class_interface_unregister(struct class_interface *intf);
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/79526/showart_2121099.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP