linux内核之设备驱动模型

linux内核之设备驱动模型

  • 前言
    • 一、设备模型的基石---kobject子系统
    • 二、设备模型的总框架---总线、设备和驱动模型
    • 三、有关驱动的虚拟内存文件系统---Sysfs系统

前言

七七八八,磨磨蹭蹭,终于大体上是把LDD3弄完了(大的方面),这里还剩一部分设备驱动部分,喝杯枸杞茶,拍拍我这略显懒散的脸,把这部分记录完。

这篇博客打算写一下几个部分

  • 设备模型的基石—kobject子系统
  • 设备模型的总框架—总线、设备和驱动模型
  • 有关驱动的虚拟内存文件系统—Sysfs系统

ok,let’s begin…

一、设备模型的基石—kobject子系统

在面向对象的大型框架或者软件系统中,为了代码的复用性,大多数系统都会创造一个Object对象,以这个对象作为其他对象的祖先。作为开天辟地的老大,它一般对软件系统中所需要的对象做出了最高的抽象。 对于它的子孙们,大都需要继承Object,从而淋漓极致的发挥面向对象中继承和多态的功效。
用C语言开发的linux在设备驱动这个模块中也或多或少的用到了这中做法。
kobject是组成设备模型的基本结构,而内核中很多平台、总线或者框架的实现大都以kobject为基础(比如说后面说到的总线、设备和驱动模型)。由于linux使用C语言完成的,面向对象的那种纯纯的继承关系是体现不出来的,所以kobject的子孙们“继承”这个祖先的方式是包含一个object的结构体。如设备驱动中的字符设备cdev,其成员如下。


struct cdev { 
	struct kobject kobj;                  //内嵌的内核对象.
	struct module *owner;                 //该字符设备所在的内核模块的对象指针.
	const struct file_operations *ops;    //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.
	struct list_head list;                //用来将已经向内核注册的所有字符设备形成链表.
	dev_t dev;                            //字符设备的设备号,由主设备号和次设备号构成.
	unsigned int count;                   //隶属于同一主设备号的次设备号的个数.

这里再简单多说一点,以上所说的这种"继承"方式,有的C++教材中叫做组合的方式。同时,利用这种内嵌的组合方式,linux在实现多态的时候用到了一个叫做 container of()的方式(用结构体成员找结构体),感兴趣的可以问问度娘。

咱再回来接着说kobject,在2.6的内核中,它的基本结构为:

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

LDD3中,Kobject的作用有以下几点:
(1)、对象的引用计数。结构体中kref主要实现的就是这个作用,控制对象的生命周期,即当内核中没有代码持有对象的引用时,对象就可以删除了。
(2)、数据结构的相互关联。object把大量的数据结构联系成了层次化的体系结构。(有点类似于对象的子类父类家族树)
(3)、sysfs表述。在sysfs中显示的每一个对象,都对应这一个kobject,它被用来于内核交互。
(4)、热插拔事件处理。当系统中的硬件被热插拔时,在kobject子系统的控制下,将产生事件通知用户空间。(本文不涉及这点)

由kobject所引申出来的几个结构体还有kset、kobj_type等等,它们的作用和object差不多(或者说它们是和kobject一起配合组成以上说明的作用)。网上有很多博客说的很好,目前我也想不出什么好的创新点,在此就不赘述了。给推荐一个博客。

linux设备驱动模型

二、设备模型的总框架—总线、设备和驱动模型

在linux驱动中应该有很多框架,但最主要的应该要数这个总线、设备和驱动模型框架了。
来,先来弄张图。
linux内核之设备驱动模型_第1张图片
为了应对日益复杂的硬件设备,原先的linux内核有点吃不消,在全世界热爱linux的热血青年的共同努力下,应该在2.6版本之后就出现了上图所示的总线、设备驱动模型。
在这个模型中,有三个概念:
(1)、总线(Bus)。这里所说的总线并不是真正CPU内部的硬件总线,而是一个虚拟的概念。(之所以叫做总线,我想是因为设备、和驱动能“挂”在上面吧),从某种程度上来说,它是这个模型的控制者、总览大局的。模型中的设备和驱动需要挂在它下面才能使用。在linux内核中用bus_type来描述一个总线。

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 bus_type_private *p;
};

(2)设备(device)。在linux内核中用device结构体来描述。如下所示。

struct device {
	struct device		*parent;
	struct device_private	*p;
	struct kobject kobj;
	const char		*init_name; /* initial name of the device */
	struct device_type	*type;
	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */
	struct bus_type	*bus;		/* type of bus device is on */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	struct dev_pm_info	power;

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations such descriptors. */
	struct device_dma_parameters *dma_parms;
	struct list_head	dma_pools;	/* dma pools (if dma'ble) */
	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
	/* arch specific additions */
	struct dev_archdata	archdata;
#ifdef CONFIG_OF
	struct device_node	*of_node;
#endif

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	spinlock_t		devres_lock;
	struct list_head	devres_head;
	struct klist_node	knode_class;
	struct class		*class;
	const struct attribute_group **groups;	/* optional groups */
	void	(*release)(struct device *dev);
};

(3)驱动(driver)。在linux内核中用device_driver来描述。如下所示。

struct device_driver {
	const char		*name;
	struct bus_type		*bus;
	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */
	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
#if defined(CONFIG_OF)
	const struct of_device_id	*of_match_table;
#endif
	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内核把设备驱动分开了,一个管设备,一个管驱动。它们在使用的时候都需要“挂”到对应的bus总线上(bus_type里有对应的kset,device和device_driver中也有指明哪条bus的bus_type类型), 它们就像一对小情侣,而bus就相当于红娘。这对小情侣的配对都需要红娘来牵线,当一个device来到红娘bus注册的时候,bus红娘就从已注册的device_driver中遍历,找到能和device匹配的(用bus_type里的match函数)。当device_driver来注册时也是一样。 需要说明一点的是,一般来说,一个设备只能匹配一个driver,而一个driver能够匹配多个device。(这么说的话,这里的小情侣应该是古时候的小情侣,嘿嘿)

这里我基本只说一些大的方面,具体的细节方面,有很多博客、资料说的已经很好了。这里也给一些资料。

1、从需求的角度去理解Linux系列:总线、设备和驱动
2、Linux设备驱动中的软件架构思想
3、linux设备驱动模型
4、linux驱动中probe函数是怎么调用的

这里在多说一点,这个总线(Bus)、设备(device)、驱动(device_driver)几个结构体也算是比较底层的结构体了,linux内核中比较成熟的总线都在这底层的结构体上进行了相应的封装(成员变量和对应的操作方法上)。如bus总线、pci总线和platform总线。如果大家想深入了解这个模型的话,可以自己模拟写一个属于自己的总线、创建自己的设备和驱动。这里也给大家几个资料。

1、从零开始写个platform平台总线
2、字符驱动移植–bus(平台总线), device, driver
3、总线设备驱动模型

三、有关驱动的虚拟内存文件系统—Sysfs系统

在linux系统中除了一些真正存储在介质上的文件系统(如ext2、ext3等)外,还有一种叫做虚拟内存的文件系统,这种文件系统只存在于内存中,断电了,文件系统也就消失了。这种文件系统的作用大多是导出内核的信息,使内核空间和用户空间进行交互。如proc、tmpfs、sysfs等。
对于sysfs文件系统,它最主要的作用就是管理统一的设备、驱动。所以这个文件系统里大都是和驱动相关的。

嗯。。对,这部分还有一些class等,udev等知识点,我还没有搞的太透彻, 先是这些吧。

你可能感兴趣的:(#,Linux内核学习)