linux混混之牢骚:
同事小李要移民到美国,领导问他:“你对你的工资不满意吗?”
小李说:“满意。”
“对你的住房不满意?”
“满意”
“那是上网环境不满意?”
“也满意”
“对医疗,孩子上学都不满意?”
“都满意!”
“既然你都满意为什么还要移民?”
“因为那里允许有不满意!”
linux version:2.6.39
什么是sys文件系统:
Sysfs文件系统是一个类似于proc文件系统的特殊文件系统,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息。
其实,就是 在用户态可以通过对sys文件系统的访问,来看内核态的一些驱动或者设备等。
去/sys看一看,
localhost:/sys#ls
/sys/ block/ bus/ class/ devices/ firmware/ kernel/ module/ power/
Block目录:包含所有的块设备,进入到block目录下,会发现下面全是link文件,link到sys/device/目录下的一些设备。
Devices目录:包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构
Bus目录:包含系统中所有的总线类型
Drivers目录:包括内核中所有已注册的设备驱动程序
Class目录:系统中的设备类型(如网卡设备,声卡设备等)。去class目录中看一下,随便进到一个文件夹下,会发现该文件夹下的文件其实是连接文件,link到/sys/device/.../../...下的一个设备文件。 可以说明,其实class目录并不会新建什么设备,只是将已经注册的设备,在class目录下重新归类,放在一起。
但是,你可能根本没有去关心过sysfs的挂载过程,她是这样被挂载的。
mount -t sysfs sysfs /sys
但是sys文件是根据什么依据来创建其内容呢?他的信息来源是什么呢?
下面来分析sys的信息来源。
Linus设备底层模型
Kobject
应该说每个Kobject结构都对应一个 目录。for example:/sys/bus/pci/drivers/serial/ 路径, serial这个目录就是由一个kobject 结构体 来表示的。由此可见,Kobject是用来表示 直接对应着一个 设备,或设备驱动 的目录。Kobject包含了 这个目录的一些信息,如:目录名,父目录,设备名称等等一些信息。当然,如果Kobject用来表示一个目录,那么他所包含的信息是差不多了,但是Kobject表示的目录是用来描述某一个设备/设备驱动 的。所以仅仅Kobject这个结构体还不能完全的描述这个设备/设备驱动,再所以,Kobject这个结构体不会单独使用,一般都会包含在另一个结构体中,用网络上的话说就是包含在一个容器中。这个容器可以是:device结构体,device_drive结构体。现在层次就很明显了,device/device_drive来表示一个设备/设备驱动,当然包含了这个设备/设备驱动的信息,并且还包含了这个驱动所对应的目录的信息,Kobject结构。
当然device/device_drive在另外一层的东西了,后面再分析。我们在这里就先分析Kobject结构。
- struct kobject {
- const char *name;
- struct list_head entry;
- 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;
- unsigned int uevent_suppress:1;
- };
注意:在kenerl中,如kref,前面讲到的 page_reference变量。 都用来表示被引用。 所以 以后看变量的时候要注意看 ref或reference,来表示被引用。
相关函数
void kobject_init(struct kobject * kobj);kobject初始化函数。
int kobject_set_name(struct kobject *kobj, const char *format, ...);设置指定kobject的名称。
struct kobject *kobject_get(struct kobject *kobj);将kobj 对象的引用计数加1,同时返回该对象的指针。
void kobject_put(struct kobject * kobj); 将kobj对象的引用计数减1,如果引用计数降为0,则调用kobject release()释放该kobject对象。
int kobject_add(struct kobject * kobj);将kobj对象加入Linux设备层次。挂接该kobject对象到kset的list链中,增加父目录各级kobject的引用计数,在其parent指向的目录下创建文件节点,并启动该类型内核对象的hotplug函数。
int kobject_register(struct kobject * kobj);kobject注册函数。通过调用kobject init()初始化kobj,再调用kobject_add()完成该内核对象的注册。
void kobject_del(struct kobject * kobj);从Linux设备层次(hierarchy)中删除kobj对象。
void kobject_unregister(struct kobject * kobj);kobject注销函数。与kobject register()相反,它首先调用kobject del从设备层次中删除该对象,再调用kobject put()减少该对象的引用计数,如果引用计数降为0,则释放kobject对象。
kobject下的结构体描述:
struct kobj_type
{
void (*release)(struct kobject *);
struct sysfs_ops * sysfs_ops;
struct attribute ** default_attrs;
};
Kobj type数据结构包含三个域:一个release方法用于释放kobject占用的资源;一个sysfs ops指针指向sysfs操作表和一个sysfs文件系统缺省属性列表。
Sysfs操作表包括两个函数store()和show()。当用户态读取属性时,show()函数被调用,该函数编码指定属性值存入buffer中返回给用户态;而store()函数用于存储用户态传入的属性值。
attribute struct attribute
{
char * name;
struct module * owner;
mode_t mode;
};
attribute属性。它以文件的形式输出到sysfs的目录当中。在kobject对应的目录下面。文件 名就是name。文件读写的方法对应于kobj type中的sysfs ops。
Kset
像刚才所说,每个Kobject结构都对应一个 目录。for example:/sys/bus/pci/drivers/serial/ 路径, /serial/这个目录由一个kobject 结构体 来表示的。但是/serial/的上一级目录/drivers/如何表示呢?那么就出现了Kset这个结构体。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- struct kset {
- struct list_head list;
- spinlock_t list_lock;
- struct kobject kobj;
- struct kset_uevent_ops *uevent_ops;
- };
subsystem
在以前的版本中,还有subsystem结构,但 是在现在的版本中都已经去掉了,用Kset来代替
- struct subsystem {
- struct kset kset;
-
- struct rw semaphore rwsem;
-
- };
由上面声明可以看出,完全可以让Kset来代替subsystem结构。
总结:
1,在sys下,表示一个目录使用的结构体是 Kobject,但是在linux的内核中,有硬件的设备 和 软件的驱动,在sys下都需要用一个目录来表示。 单纯的一个Kobject结构无法表示完全,增加了容器,来封装Kobject。 即下面要将的:device和drive_device结构。
2, 最底层驱动目录的上一层目录,从sys角度上来说,他依然是个目录,所以他也有Kobjec这个变量。但是从他的意义上讲,他将 一些有公共特性Kobjec 的 device/driver_device结构组织到了一起,所以除了有Kobject这个变量外,他又添加了一些变量,组成了Kset这个结构来表示这一级的目录。但是仅仅是用Kset来表示了这一级的目录,和1,一样,仅仅表示一个目录是不够的,在linux内核中,需要他在软件上有个映射。所以,也将Kset进行了封装,形成了 bus_type这个结构。
3, 从1 ,2,的解释可以看出,应为kobject在Kset的目录下,那么 device/device_driver 就在 bus_type结构下。所以,linux驱动模型中,驱动和设备都是挂在总线下面的。
4, 如上所述,Kset的意义:表示一个目录(由结构体下的Kobject来完成),并且这个目录下的所有目录有共同的特性(所以说,Kset表示的目录下,不一定非要是Kobject街头的,也可以是Kset结构的。即:Kset嵌套Kset)。所以使用Kset来代替了以前的 subsystem结构。
贴两张图来形象了解一下:
1, Kset和Kobject的连接图(from linux那些事之我是sys)
2,整个sys目录的结构体表示图:(from ULK--当然,在这里subsystem结构要换成Kset了,但我个人认为,以前的subsystem结构上会更清晰,不是吗?)
(但这边有个问题。。。Kobject通过下面的attribute来建立目录下的文件,但我看到目录下有好几个文件,难道是根据一个attribute来建立好几个文件?疑惑ing,好像attribute是个指针,还能当数组首地址?bus_add_attrs函数中如是说)
设备模型的上层容器
刚才讲了Kset和Kobject结构体,都是用来表示 sys下的目录结构的。下面来讲驱动中封装这些结构的容器。
总线bus
bus_type结构: 刚才上面已经将的够多的了,闲话少说,直接上code。
- 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 (*resume)(struct device *dev);
-
- const struct dev_pm_ops *pm;
-
- struct subsys_private *p; 见下面:
- };
- <p>struct subsys_private {
- struct kset subsys;
- struct kset *devices_kset;</p><p> struct kset *drivers_kset;
- struct klist klist_devices;
- struct klist klist_drivers;
- struct blocking_notifier_head bus_notifier;
- unsigned int drivers_autoprobe:1;
- struct bus_type *bus;</p><p> struct list_head class_interfaces;
- struct kset glue_dirs;
- struct mutex class_mutex;
- struct class *class;
- };</p><p>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- </p>
这个结构体用来描述比如:/sys/bus/pci pci总线,/sys/bus/platform platform总线等。
另外:从这个结构体分析下来,整个bus的目录结构都很清楚了eg:
1,可以找到总线下的设备目录: bus_type bus ---> subsys_private p---->Kest devices_kset
2,可以找到总线下的设备驱动目录: bus_type bus ---> subsys_private p---->Kest driver_kset
另外,找到的也只是目录,因为找到的仅仅是Kset结构。
于此看来,这个subsys_private p这个变量挺有用的。 哥预言,此子日后必成大器。当然,条件是 日后!!哥邪恶的笑了。。。。。
设备device
首先明白,device这个结构并不是直接挂在bus下的,可以到/sys/bus/platform/device下随便看一下,发现里面的都是link文件,link到/sys/device/下。所以真正的device结构体的在/sys/device下的。
- struct device {
- struct device *parent;
-
- struct device_private *p;
-
- struct kobject kobj;
- const char *init_name;
- struct device_type *type;
-
- struct mutex mutex;
-
-
-
- struct bus_type *bus; //他所在的总线的类型
- struct device_driver *driver;
-
- void *platform_data;
-
- struct dev_pm_info power;
- struct dev_power_domain *pwr_domain;
-
- #ifdef CONFIG_NUMA
- int numa_node;
- #endif
- u64 *dma_mask;
- u64 coherent_dma_mask;
-
-
-
-
-
- struct device_dma_parameters *dma_parms;
-
- struct list_head dma_pools;
-
- struct dma_coherent_mem *dma_mem;
-
-
- struct dev_archdata archdata;
-
- struct device_node *of_node;
-
- dev_t devt;
-
- spinlock_t devres_lock;
- struct list_head devres_head;
-
- struct klist_node knode_class;
- struct class *class;
- const struct attribute_group **groups;
-
- void (*release)(struct device *dev);
- };
设备driver
- struct device_driver {
- const char *name;
- struct bus_type *bus;
-
- struct module *owner;
- const char *mod_name;
-
- bool suppress_bind_attrs;
-
- const struct of_device_id *of_match_table;
-
- 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);
- const struct attribute_group **groups;
-
- const struct dev_pm_ops *pm;
-
- struct driver_private *p;
- };
无语。。。。。。。。。。。。。
终于经过了一大段偷工减料之后,能开始分析代码了
设备模型的注册等操作:
总线的操作:
用户可以自己注册一个总线,然后将自己喜欢的设备和驱动挂载到下面。但是linux 2.6中,有个默认的总线,platform总线。我们就分析一下这个总线。
小记:随手在Source insight里敲了个 platform_bus_init,结果的真的有这个函数,再看一下谁调用他了吧? 竟然是drive_init。啊。。终于找到组织了,在start_kernel的最后一步后调用这个drive_init了。
- int __init platform_bus_init(void)
- {
- int error;
-
- early_platform_cleanup();
-
- error = device_register(&platform_bus);
- if (error)
- return error;
- error = bus_register(&platform_bus_type);
- if (error)
- device_unregister(&platform_bus);
- return error;
- }
-
-
-
-
-
-
-
-
- int bus_register(struct bus_type *bus)
- {
- int retval;
- struct subsys_private *priv;
-
- priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->bus = bus;
- bus->p = priv;
-
- BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
-
- retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
- if (retval)
- goto out;
-
- priv->subsys.kobj.kset = bus_kset;
- priv->subsys.kobj.ktype = &bus_ktype;
- priv->drivers_autoprobe = 1;
-
- retval = kset_register(&priv->subsys);
- if (retval)
- goto out;
-
- retval = bus_create_file(bus, &bus_attr_uevent);
- if (retval)
- goto bus_uevent_fail;
-
- priv->devices_kset = kset_create_and_add("devices", NULL,
- &priv->subsys.kobj);
- if (!priv->devices_kset) {
- retval = -ENOMEM;
- goto bus_devices_fail;
- }
-
- priv->drivers_kset = kset_create_and_add("drivers", NULL,
- &priv->subsys.kobj);
- if (!priv->drivers_kset) {
- retval = -ENOMEM;
- goto bus_drivers_fail;
- }
-
- klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
- klist_init(&priv->klist_drivers, NULL, NULL);
-
- retval = add_probe_files(bus);
- if (retval)
- goto bus_probe_files_fail;
-
- retval = bus_add_attrs(bus);
- if (retval)
- goto bus_attrs_fail;
-
- pr_debug("bus: '%s': registered\n", bus->name);
- return 0;
-
- bus_attrs_fail:
- remove_probe_files(bus);
- bus_probe_files_fail:
- kset_unregister(bus->p->drivers_kset);
- bus_drivers_fail:
- kset_unregister(bus->p->devices_kset);
- bus_devices_fail:
- bus_remove_file(bus, &bus_attr_uevent);
- bus_uevent_fail:
- kset_unregister(&bus->p->subsys);
- out:
- kfree(bus->p);
- bus->p = NULL;
- return retval;
- }
bus_register-->kset_register
-
-
-
-
- int kset_register(struct kset *k)
- {
- int err;
-
- if (!k)
- return -EINVAL;
-
- kset_init(k);
- err = kobject_add_internal(&k->kobj);
- if (err)
- return err;
- kobject_uevent(&k->kobj, KOBJ_ADD);
- return 0;
- }
- <p>static int kobject_add_internal(struct kobject *kobj)
- {
- int error = 0;
- struct kobject *parent;</p><p> if (!kobj)
- return -ENOENT;</p><p> if (!kobj->name || !kobj->name[0]) {
- WARN(1, "kobject: (%p): attempted to be registered with empty "
- "name!\n", kobj);
- return -EINVAL;
- }</p><p> parent = kobject_get(kobj->parent);</p><p>
- if (kobj->kset) {
- if (!parent)
- parent = kobject_get(&kobj->kset->kobj);
- obj_kset_join(kobj);
- kobj->parent = parent;
- }</p><p> pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
- kobject_name(kobj), kobj, __func__,
- parent ? kobject_name(parent) : "<NULL>",
- kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");</p><p> error = create_dir(kobj);
- if (error) {
- kobj_kset_leave(kobj);
- kobject_put(parent);
- kobj->parent = NULL;</p><p>
- if (error == -EEXIST)
- printk(KERN_ERR "%s failed for %s with "
- "-EEXIST, don't try to register things with "
- "the same name in the same directory.\n",
- __func__, kobject_name(kobj));
- else
- printk(KERN_ERR "%s failed for %s (%d)\n",
- __func__, kobject_name(kobj), error);
- dump_stack();
- } else
- kobj->state_in_sysfs = 1;</p><p> return error;
- }</p>
到此,bus_register解释完成。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int device_register(struct device *dev)
- {
- device_initialize(dev);
- return device_add(dev);
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- void device_initialize(struct device *dev)
- {
- dev->kobj.kset = devices_kset;
- kobject_init(&dev->kobj, &device_ktype);
- INIT_LIST_HEAD(&dev->dma_pools);
- mutex_init(&dev->mutex);
- lockdep_set_novalidate_class(&dev->mutex);
- spin_lock_init(&dev->devres_lock);
- INIT_LIST_HEAD(&dev->devres_head);
- device_pm_init(dev);
- set_dev_node(dev, -1);
- }
dev_initialize,不解释。
这里有个疑问:在bus_register的时候,有条语句:priv->subsys.kobj.kset = bus_kset;。在dev_initialize的时候也有条dev->kobj.kset = devices_kset;语句。 刚才以为是上级目录的kset结构。但是如此看来好像不是很对,因为dev的上级目录是不定的,可能在/sys/device/platform下,也可能在其他。但是都赋值成devices_kset显然不对。 那么有可能在一个标志。所有的bus的subsys.kobj.kset 这个变量都是bus_kset, 所有dev->kobj.kset的变量都是devices_kset。具体为什么?
天空中深沉的传来一句话:1+1=几?
我说:2
啪,一道雷劈死我了。答曰:你知道的太多了。 为了留条命,就不解释了。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int device_add(struct device *dev)
- {
- struct device *parent = NULL;
- struct class_interface *class_intf;
- int error = -EINVAL;
-
- dev = get_device(dev);
- if (!dev)
- goto done;
-
- if (!dev->p) {
- error = device_private_init(dev);
- if (error)
- goto done;
- }
-
-
-
-
-
-
- if (dev->init_name) {
- dev_set_name(dev, "%s", dev->init_name);
- dev->init_name = NULL;
- }
-
- if (!dev_name(dev)) {
- error = -EINVAL;
- goto name_error;
- }
-
- pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
-
- parent = get_device(dev->parent);
- setup_parent(dev, parent);
-
-
- if (parent)
- set_dev_node(dev, dev_to_node(parent));
-
-
-
- error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
- if (error)
- goto Error;
-
-
- if (platform_notify)
- platform_notify(dev);
-
- error = device_create_file(dev, &uevent_attr);
- if (error)
- goto attrError;
-
- if (MAJOR(dev->devt)) {
- error = device_create_file(dev, &devt_attr);
- if (error)
- goto ueventattrError;
-
- error = device_create_sys_dev_entry(dev);
- if (error)
- goto devtattrError;
-
- devtmpfs_create_node(dev);
- }
-
- error = device_add_class_symlinks(dev);
- if (error)
- goto SymlinkError;
- error = device_add_attrs(dev);
- if (error)
- goto AttrsError;
- error = bus_add_device(dev);
- if (error)
- goto BusError;
- error = dpm_sysfs_add(dev);
- if (error)
- goto DPMError;
- device_pm_add(dev);
-
-
-
-
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_ADD_DEVICE, dev);
-
- kobject_uevent(&dev->kobj, KOBJ_ADD);
- bus_probe_device(dev);
- if (parent)
- klist_add_tail(&dev->p->knode_parent,
- &parent->p->klist_children);
-
- if (dev->class) {
- mutex_lock(&dev->class->p->class_mutex);
-
- klist_add_tail(&dev->knode_class,
- &dev->class->p->klist_devices);
-
-
- list_for_each_entry(class_intf,
- &dev->class->p->class_interfaces, node)
- if (class_intf->add_dev)
- class_intf->add_dev(dev, class_intf);
- mutex_unlock(&dev->class->p->class_mutex);
- }
- done:
- put_device(dev);
- return error;
- DPMError:
- bus_remove_device(dev);
- BusError:
- device_remove_attrs(dev);
- AttrsError:
- device_remove_class_symlinks(dev);
- SymlinkError:
- if (MAJOR(dev->devt))
- devtmpfs_delete_node(dev);
- if (MAJOR(dev->devt))
- device_remove_sys_dev_entry(dev);
- devtattrError:
- if (MAJOR(dev->devt))
- device_remove_file(dev, &devt_attr);
- ueventattrError:
- device_remove_file(dev, &uevent_attr);
- attrError:
- kobject_uevent(&dev->kobj, KOBJ_REMOVE);
- kobject_del(&dev->kobj);
- Error:
- cleanup_device_parent(dev);
- if (parent)
- put_device(parent);
- name_error:
- kfree(dev->p);
- dev->p = NULL;
- goto done;
- }