您的位置:首页 > 运维架构 > Linux

Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)

2011-12-10 23:58 453 查看
可能把驱动模型放在第一章讲会会有点难度,但是只要能跨过这道坎,后面就会轻松很多,驱动模型是整个linux设备驱动的基石。大部分人把驱动模型叫做设备模型,但是我查了linux的帮助文档,就是在下载源码路径下的Documentation目录中找到driver-model这个目录,里面包含的文件就是我在本章中所要讲述的东西,也就是我所说的驱动模型。因此本文都会用驱动模型这个术语(如果各位觉得这种叫法是错误的,请在评论中指出,并给出理由,本人非常诚恳的接受各位善意的批评与指正)。驱动模型的核心结构就是我们通常所说的bus、device、device_driver。即总线、设备、设备驱动。首先分析linux内核要有层次的概念,linux从设计上来说是一层套一层的,那么在这一层之下,还有一层由kobject、kobj_type、kset这三者组成,也可以认为其属于驱动模型的范围内,我们可以看到内核对它的描述是:generic
kernel object infrastructure。就是通用内核对象基础的意思。我们暂且叫它内核对象层吧。在驱动模型的上层我们可以封装各种子模块子系统,这个以后再做讲解。

我们首先来看看内核对象层是什么东西,都有些什么功能。在这个分析的过程中请多一点耐心,在这中间需要的仅仅是耐心而已。

首先给出内核对象层各成员的原型:

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;

};

struct kset {

struct list_head list;

spinlock_t list_lock;

struct kobject kobj;

struct kset_uevent_ops *uevent_ops;

};

struct kobj_type {

void (*release)(struct kobject *kobj);

struct sysfs_ops *sysfs_ops;

struct attribute **default_attrs;

};

首先从各结构体的成员我们发现这没有三角恋的关系,kobject中包含有kobj_type和kset及自身,kset中包含有kobject,而kobj_type则不包含上面两者,只要是在道上混的兄弟,一眼就可以看出kobject在这场恋爱关系中是占据绝对地位的。

针对这三者,linux内核提供了一些操作这些成员的方法。(位于kobject.c)。

我们挑几个名角讲一讲:

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)

{

char *err_str;

if (!kobj) {

err_str = "invalid kobject pointer!";

goto error;

}

if (!ktype) {

err_str = "must have a ktype to be initialized properly!/n";

goto error;

}

if (kobj->state_initialized) {

/* do not error out as sometimes we can recover */

printk(KERN_ERR "kobject (%p): tried to init an initialized "

"object, something is seriously wrong./n", kobj);

dump_stack();

}

kobject_init_internal(kobj);

kobj->ktype = ktype;

return;

error:

printk(KERN_ERR "kobject (%p): %s/n", kobj, err_str);

dump_stack();

}

我们避重就轻的看一下(前面初始化和合法条件判断在实际的运行当中是很重要的,但是对我们分析来说只要能抓主线,分析我们感兴趣的内容就可以了),可以把这个函数简化:

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)

{

char *err_str;

kobject_init_internal(kobj);

kobj->ktype = ktype;

return;

}

OK,我们看见传入了两个参数,一个是kobject的指针,一个是kobj_type的指针,在调用完kobject_init_internal(kobj)之后,就将传入的ktype赋值给了kobject的ktype成员。我们先来看看ktype到底是何方神胜。在分析ktype之前,我们先要往上跑一层,这一层我们选择int device_register(struct device *dev)这个函数,先给出函数原型:

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);

init_MUTEX(&dev->sem);

spin_lock_init(&dev->devres_lock);

INIT_LIST_HEAD(&dev->devres_head);

device_init_wakeup(dev, 0);

device_pm_init(dev);

set_dev_node(dev, -1);

}

我们找到我们感兴趣的kobject_init(&dev->kobj, &device_ktype);

我们查看device_ktype的定义:

static struct kobj_type device_ktype = {

.release = device_release,

.sysfs_ops = &dev_sysfs_ops,

};

很明显release的作用就是release,至于怎么release我们先不看,下面一个就是sysfs_ops。这个sysfs与用户空间通信的一个接口,我们点击进去查看一下:

static struct sysfs_ops dev_sysfs_ops = {

.show = dev_attr_show,

.store = dev_attr_store,

};分别对应了我们读和写sysfs下面节点的两个动作。至于里面干嘛的我们先不管。从上面我们知道,ktype包含了一个sysfs的读写接口,另外包含了一个具有release功能的函数。

回到我们之前的内容:简化版的kobject_init函数:

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)

{

char *err_str;

kobject_init_internal(kobj);

kobj->ktype = ktype;

return;

}

剩下的就是kobject_init_internal(kobj)了。

static void kobject_init_internal(struct kobject *kobj)

{

if (!kobj)

return;

kref_init(&kobj->kref);

INIT_LIST_HEAD(&kobj->entry);

kobj->state_in_sysfs = 0;

kobj->state_add_uevent_sent = 0;

kobj->state_remove_uevent_sent = 0;

kobj->state_initialized = 1;

}

这个函数的功能纯粹就是初始化。

从这个初始经我们了解一些更新的东西:

kref_init(&kobj->kref);

这个叫引用计数,kref_init的作用就是将kobjct->kref设为1。

接下来就是初始化kobject->entry这条链表(linux内核的链表是非常重要且比较精妙的,网上相关的好文章也很多,请同志们自行查阅学习)。

接下来就是一堆的位域。

kobj->state_in_sysfs这个成员正如其名:指明是否使用了sysfs。初始化为0,显然是说:哥现在还没用。

kobj->state_add_uevent_sent、kobj->state_remove_uevent_sent 这两个成员的名命也是非常直观的:指明是否有加载或删除事件。这个是和热插拔相关的,当我们增加一个设备或者删除一个设备的时候,会在合适的时候将此位域置为1。

kobj->state_initialized指明kobject是否有被初始化,这们是唯一个置1的。显然自身被初始化了。

在分析之前有必要说明一下,为了让我们的分析更加简练,我们只会在合适的时候分析结构体的相关成员,不会在没有用到的情况下将成员的作用全都描述出来。

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;

}

/*

* for statically allocated devices, which should all be converted

* some day, we need to initialize the name. We prevent reading back

* the name, and force the use of dev_name()

*/

if (dev->init_name) {

dev_set_name(dev, "%s", dev->init_name);

dev->init_name = NULL;

}

if (!dev_name(dev))

goto name_error;

pr_debug("device: '%s': %s/n", dev_name(dev), __func__);

parent = get_device(dev->parent);

setup_parent(dev, parent);

/* use parent numa_node */

if (parent)

set_dev_node(dev, dev_to_node(parent));

/* first, register with generic layer. */

/* we require the name to be set before, and pass NULL */

error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);

if (error)

goto Error;

/* notify platform of device entry */

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);

/* Notify clients of device addition. This call must come

* after dpm_sysf_add() and before kobject_uevent().

*/

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);

/* tie the class to the device */

klist_add_tail(&dev->knode_class,

&dev->class->p->class_devices);

/* notify any interfaces that the device is here */

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))

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;

}

当你看到这一大段的时候,是不是感觉很郁闷,我也很郁闷,但是哥很高兴的说:依我们目前的功能,我们只分析kobject_add(&dev->kobj, dev->kobj.parent, NULL)就够了。从人生的低谷瞬间又找回自信其实很简单,就在现在。先给出函数定义:

int kobject_add(struct kobject *kobj, struct kobject *parent,

const char *fmt, ...)

{

va_list args;

int retval;

if (!kobj)

return -EINVAL;

if (!kobj->state_initialized) {

printk(KERN_ERR "kobject '%s' (%p): tried to add an "

"uninitialized object, something is seriously wrong./n",

kobject_name(kobj), kobj);

dump_stack();

return -EINVAL;

}

va_start(args, fmt);

retval = kobject_add_varg(kobj, parent, fmt, args);

va_end(args);

return retval;

}

这下代码少多了。

我们可以看到核心函数是kobject_add_varg(kobj, parent, fmt, args),其定义如下:

static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,

const char *fmt, va_list vargs)

{

int retval;

retval = kobject_set_name_vargs(kobj, fmt, vargs);

if (retval) {

printk(KERN_ERR "kobject: can not set name properly!/n");

return retval;

}

kobj->parent = parent;

return kobject_add_internal(kobj);

}

其中的kobject_set_name_vargs就是用于设置kobject的名字。

int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,

va_list vargs)

{

const char *old_name = kobj->name;

char *s;

if (kobj->name && !fmt)

return 0;

kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);

if (!kobj->name)

return -ENOMEM;

/* ewww... some of these buggers have '/' in the name ... */

while ((s = strchr(kobj->name, '/')))

s[0] = '!';

kfree(old_name);

return 0;

}

下面就是kobject_add_internal这个函数了,其定义如下:

static int kobject_add_internal(struct kobject *kobj)

{

int error = 0;

struct kobject *parent;

if (!kobj)

return -ENOENT;

if (!kobj->name || !kobj->name[0]) {

WARN(1, "kobject: (%p): attempted to be registered with empty "

"name!/n", kobj);

return -EINVAL;

}

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);

kobj->parent = parent;

}

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>");

error = create_dir(kobj);

if (error) {

kobj_kset_leave(kobj);

kobject_put(parent);

kobj->parent = NULL;

/* be noisy on error issues */

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;

return error;

}

凭着一个程序员的直觉,我们可以看到最重要的是create_dir(kobj);没错,哥猜的很对,就是它了,它和sysfs相关,创建了一个目录,具体这个函数因为牵涉的非常广,我们暂且不做分析。君子报仇,十年不晚,我们看谁笑到最后。在create_dir(kobj)之后我们将kobj->state_in_sysfs =置为1,很亲切吧。撞到老相识的感觉很爽吧,我们在后续分析内核的过程中会撞到越来越多的老相识,并且结识更多的新朋友。连著名歌唱家殷秀梅都知道学习内核的方法:结识新朋友,不忘老朋友……(80后朋友应该都认识,90后的有可能就不认识了)。

接下来我们来分析一下和kset有关的一个函数,那就是先给出函数原型:

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);

if (!kset)

return NULL;

error = kset_register(kset);

if (error) {

kfree(kset);

return NULL;

}

return kset;

}

和上一节分析kobject一样,为了更好的讲解这个函数我们先要跳到上一层,我们先有必要看一下都有哪些朋友调用了它:int bus_register(struct bus_type *bus)。大名鼎鼎的总线注册。

我们看到bus_register函数中有这样几行代码:

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);

可见kset和总线是关系的。OK。我们以第一段为基础讲解,分别传入了一个常字符串”devices”,一个空指针,一个kobject指针。

函数内部首先调用

static struct kset *kset_create(const char *name,

struct kset_uevent_ops *uevent_ops,

struct kobject *parent_kobj)

{

struct kset *kset;

int retval;

kset = kzalloc(sizeof(*kset), GFP_KERNEL); //分配一个kset结构体并初始化

if (!kset)

return NULL;

retval = kobject_set_name(&kset->kobj, name); //将传入的常字符串赋值给//kset->kobj->name

if (retval) {

kfree(kset);

return NULL;

}

kset->uevent_ops = uevent_ops; //将uevent_ops

kset->kobj.parent = parent_kobj; //将父类kobject指针赋值给kset->kobj.parent

/*

* The kobject of this kset will have a type of kset_ktype and belong to

* no kset itself. That way we can properly free it when it is

* finished being used.

*/

kset->kobj.ktype = &kset_ktype; //将kset_ktyp赋值给kset->kobj.parent

kset->kobj.kset = NULL; //将NULL赋值给kset->kobj.kset

return kset;

}

从上面标红的注释我们发现kset内嵌的kobject的重要性了。这是kset和kobject的重要关系。有一句话来形容叫我中有你,你中有我。接下来我们将做好的kset的指针的形式传给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;

}

void kset_init(struct kset *k)

{

kobject_init_internal(&k->kobj);

INIT_LIST_HEAD(&k->list);

spin_lock_init(&k->list_lock);

}

接下来就是kobject_add_internal(&k->kobj),又撞到老相识了,让我们再次高歌:结识新朋友,不忘老朋友…

好了,kobject,kobj_type,kset的关系我们大概清楚了,下面是我画的一个图用于表示这三者的关系:



转载自:/article/1887170.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: