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

来源:互联网 发布:qt creator是什么软件 编辑:程序博客网 时间:2024/05/29 06:49

转自 http://blog.csdn.net/gqb_driver/article/details/8449588

转自:无为和尚的Linux内核大讲堂系列,并对个别地方进行了补充更正(见标红处)。http://blog.csdn.net/z2007b/article/details/6388753

可能把驱动模型放在第一章讲会有点难度,但是只要能跨过这道坎,后面就会轻松很多,驱动模型是整个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,一个是kobj_type的指针ktype,在调用完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,devices和drivers。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); //将传入的常字符串
("devices")赋值给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
.ktype
 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的关系我们大概清楚了,下面是我画的一个图用于表示这三者的关系:

 

 


好了,先抽根烟吧,下节我们继续分析。


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孩孑16岁了高中没考上怎么办 小孩的嘴巴里有点点该怎么办 小孩从出生哭笑嘴巴有点歪怎么办 儿童耳朵受伤后嘴巴歪了怎么办 手机锁屏图案忘了怎么办求解锁 忘给仓鼠买吃的了怎么办 两岁宝宝不爱吃饭只喝奶粉怎么办 下载百度云压缩包要提取密码怎么办 微信表情包图片过大无法添加怎么办 管理员吧群员全部踢了群主怎么办 微信笑脸表情斗图文字没有了怎么办 钨珠钢笔头用完笔墨了怎么办 暴走大事件被禁六一红鼻子节怎么办 弯腰推东西把腰闪了怎么办 怀孕初期弯腰捡东西腰不舒服怎么办 突然弯腰捡东西腰闪了怎么办 跑步机上的磁铁掉了怎么办 微信解封辅助申诉信息不一致怎么办 穿越火线进游戏字体变小了怎么办 微信登录显示版本过低登不上怎么办 手机显示主题版本过低不能用怎么办 杨豹平全民k歌qq登不上去怎么办 画画时桌面会有许多橡皮渣怎么办 瑜伽体式小人图大全不会画怎么办 微信公众号的字体变得不认识怎么办 三菱按动笔摔了一下断墨怎么办 员工工作一星期不到不给工资怎么办 毕业后报到证一直没去报道怎么办 一直做会计助理学不到东西怎么办 玩守望先锋人物模型没刷出来怎么办 不小心点击拔打黑客lD视频怎么办 套得太深散户都不卖庄家怎么办 黑色衣领掉色在白衣服上怎么办 电脑缝纫界下线切的短怎么办 喝完酒第二天恶心想吐怎么办 纸上怎么办画5种标准眉 电脑密码忘了怎么办最简单的方法 宿舍六个人有一个不想安空调怎么办 绝地求生开在了进游戏的页面怎么办 桃树树根有很大一个洞怎么办啊 截掉的枝干一直往下干怎么办