Linux核心大講堂 (一) 裝置驅動的基石驅動模型(1)

來源:互聯網
上載者:User

可能把驅動模型放在第一章講會會有點難度,但是只要能跨過這道坎,後面就會輕鬆很多,驅動模型是整個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”,一個null 指標,一個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的關係我們大概清楚了,下面是我畫的一個圖用於表示這三者的關係:

 

 


好了,先抽根煙吧,下節我們繼續分析。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.