• Linux内核设计与实现第十周读书笔记


    第十七章 设备与模块

    关于设备驱动与设备管理,我们讨论四种内核成分。

    • 设备类型
    • 模块
    • 内核对象
    • sysfs

    17.1设备类型

    在Linux以及所有Unix系统中,设备被分为以下三种类型:

    • 块设备,块设备通常缩写为blkdev,它是可寻址的,寻址以块为单位,块大小随设备不同而不同;块设备通常支持重定位操作,也就是对数据的随机访问。块设备是通过称为“块设备节点”的特殊文件来访问,并且通常被挂载为文件系统。
    • 字符设备,字符设备通常缩写为cdev,它是不可寻址的,仅提供数据的流式访问,就是一个一个字符,或者一个一个字节。字符设备通过称为“字符设备节点”的特殊文件访问。与块设备不同,应用程序直接通过访问设备节点与字符设备交互。
    • 网络设备最常见的类型有事也以以太网设备来称呼,它提供了对网络的访问,这是通过一个物理适配器和一种特定的协议来进行的。它不通过设备节点来访问,而是通过套接字API这样的特殊接口来访问。

    并不是所有设备驱动都表示物理设备。有些设备驱动是虚拟的,仅提供访问内核功能而已。我们成为“伪设备”。

    17.2模块

    支持模块的好处是基本内核镜像可以尽可能地小,因为可选的功能和驱动程序可以利用模块形式再提供。模块允许我们方便地删除和重新载入内核代码,也方便了调试工作。

    17.2.1Hello,World

    MOUDLE_LICENSE()宏用于指定模块的版权。
    MOUDLE_AUTHOR()宏和MOUDLE_DESCRIPTION()宏指定了代码作者和模块的简要描述,它们完全是用作信息记录目的。

    17.2.2构建模块

    1.放在内核源代码树中

    • 你需要在drivers/char/目录下建立一个名为fishing的子目录。
    • 接下来需要在drivers/char/的Makefile文件中添加一行。编辑drivers/char/Makefile/并加入obj-m += fishing/。该行编译指令告诉模块构建系统,在编译模块时需要进入fishing/子目录中。更可能发生的情况是驱动程序的编译取决于一个特殊配置选项,比如可能的CONFIG_FISHING_POLE,如果这样,需要用下面的指令替换刚才的指令:obj-$(CONFIG_FISHING_POLE)  +=fishing/
    • 最后,在drivers/char/fishing/下,需要添加一个新的Makefile文件,其中需要有这条指令:obj-m += fishing.o

    2.放在内核代码外

    • 在你自己的源代码树目录中建立一个Makefile文件,只需要一行指令:obj-m := fishing.o
    • 告诉make如何找到内核源代码文件和基础的Makefile文件。make –C /kernel/source/location SUBDIRS=$PWD modules

    17.2.3安装模块

    下面的构建命令用来安装编译的模块到合适的目录下:make moudles_install,通常需要以root权限运行。

    17.2.4产生模块依赖性

    • 若想产生内核依赖关系的信息,root用户可运行命令depmod
    • 为了执行更快的更新操作,那么可以只为新模块生成依赖信息,而不是生成所有的依赖关系,这是root用户可运行命令的depmod -A

    17.2.5载入模块

    • 载入模块,以root身份运行命令:insmod module.ko
    • 卸载一个模块,以root身份运行:rmmod module
    • 为了在内核via modprobe中插入模块,需要以root身份运行:modprobe module[module parameters]
    • modprobe命令也可用来从内核中卸载模块,当然这也需要以root身份运行:modprobe -r modules

    与rmmod命令不同,modprobe也会卸载给定模块所依赖的相关模块,但其前提是这些相关模块没有被使用。

    17.2.6管理配置选项

    如果你建立了一个新子目录,而且也希望kconfig文件存在于该目录中的话,那么你必须在一个已存在的kconfig文件中将它引入。你需要加入下面一行指令:source "drivers/char/fishing/Kconfig"

    17.2.7模块参数

    定义一个模块参数可通过宏module_param()完成:module_param(name,type,perm);
    参数name既是用户可见的参数名,也是你模块中存放模块参数的变量名。参数type则存放了参数的类型。最后一个参数perm指定了模块在sysfs文件系统下对应文件的权限。

    17.2.8导出符号表

    在内核中,导出内核函数需要使用特殊的指令:EXPORT_SYMBOL()EXPORT_SYMBOL_GPL()

    导出的内核函数可以被模块调用。导出的内核符号表被看作导出的内核接口,甚至称为内核API。

    17.3设备模型

    17.3.1kobject

    设备模型的核心部分是kobject(kernel object)kobject提供了诸如引用计数、名称和父指针等字段,可以创建对象的层次结构。

    name指针指向kobject的名称。
    parent指针指向kobject的父对象。
    kref提供引用计数,ktype和kset结构体对kobject对象进行描述和分类。

    struct kobject {
           constchar              *name;
           structlist_head       entry;
           structkobject         *parent;
           structkset              *kset;
           structkobj_type     *ktype;
           structsysfs_dirent   *sd;
           structkref              kref;
           unsignedint state_initialized:1;
           unsignedint state_in_sysfs:1;
           unsignedint state_add_uevent_sent:1;
           unsignedint state_remove_uevent_sent:1;
           unsignedint uevent_suppress:1;
    };
    

    17.3.2ktype

    struct kobj_type {
           void(*release)(struct kobject *kobj);
           structsysfs_ops *sysfs_ops;
           structattribute **default_attrs;
    };
    

    ktype是为了描述kobject所具有的普遍特性。
    release指针指向在kobject引用计数减至0时要被调用的析构函数。该函数负责释放所有kobject使用的内存和其他相关清理工作。
    sysfs_ops变量指向sysfs_ops结构体。该结构体描述了sysfs文件读写时的特性。
    default_attrs指向一个attribute结构体数组。这些结构体定义了该kobject相关的默认属性。

    17.3.3kset

    kset是kobject对象的集合体。kset可把kobject集中到一个集合中,而ktype描述相关类型kobject所共有的特性。区别:具有相同ktype的kobject可以被分组到不同的kset。就是说在Linux内核中只有少数一些的ktype,却有多个kset。

    struct kset {
           structlist_head list;
           spinlock_tlist_lock;
           structkobject kobj;
           structkset_uevent_ops *uevent_ops;
    };
    

    list连接kset中所有的kobject对象,list_lock是保护这个链表元素的自旋锁,kobj指向的kobject对象代表了该集合的基类。uevent_ops指向一个结构体用于处理集合中kobject对象的热插拔操作。

    17.3.4kobject、ktype和kset的相互关系

    kobject本身的意义不大,通常情况下需要被嵌入到其他数据结构中,让那些包含它的结构具有了kobject的特性。

    kobject与一个特别的ktype对象关联,ktype定义了一些kobject相关的默认特性:析构行为、sysfs行为以及别的一些默认属性。

    kobject又归入了称作kset的集合。kset提供了两个功能:1、其中嵌入的kobject作为kobject组的基类2、kset将相关的kobject集合在一起。

    17.3.5管理和操作kobject

    • 使用kobject的第一步需要先来生命和初始化。kobject通过函数kobiect_init进行初始化。
    • 该函数的第一个参数就是需要初始化的kobject对象,在调用初始化函数前,kobject必须清空。
    • 在清零后,就可以安全的初始化parent和kset字段。
    • 这多步操作也可以由kobject_create()来自动处理,它返回一个新分配的kobject。

    17.3.6引用计数

    kobject的主要功能之一就是为我们提供了一个统一的引用计数系统。

    1.递增和递减引用计数

    • 增加一个引用计数可通过kobject_get()函数完成:struct kobject * kobject_get(struct kobject *kobj);
    • 减少引用计数通过kobject_put()完成:void kobject_put(struct kobject *kobj);

    2.kref

    struct kref{
    		atomic_t refcount;
    };
    

    其中唯一的字段是用来存放引用计数的原子变量。那么为什么采用结构体?这是为了便于进行类型检测。

    17.4sysfs

    sys文件系统是一个处于内存中的虚拟文件系统,它为我们提供了kobject对象层次结构的视图。

    17.4.1sysfs中添加和删除kobject

    想要把kobject导入sysfs,需要用到函数kobject_add ()int kobject_add(struct kobject *kobj,struct kobject *parent,const char *fmt, ...);

    kobject在sysfs中的位置取决于kobject在对象层次结构中的位置。

    从sysfs中删除一个kobject对应文件目录,需要使用函数kobject_del()
    void kobject_del(struct kobject *kobj);

    17.4.2向sysfs中添加文件

    1.默认属性

    默认的文件集合是通过kobject和kset中的ktype字段提供的。因此所有具有相同类型的kobject在它们对应的sysfs目录下都拥有相同的默认文件集合。这些属性负责将内核数据映射成sysfs中的文件。

    2.创建新属性

    内核为能在默认集合之上,再添加新属性而提供了sysfs_create_file()接口:int sysfs_cerate_file(struct kobject *kobj,const struct attribute *attr);

    除了添加文件外,还有可能需要创建符号连接。在sysfs中创建一个符号连接相当简单:int sysfs_create_link(struct kobject *kobj,struct kobject *target,char *name);

    3.删除属性

    删除一个属性需通过函数sysfs_remove_file():void sysfs_remove_file(struct kobject *kobj,const struct attribute *attr);

    sysfs_creat_link()创建的符号连接可通过函数sysfs_remove_link()删除:void sysfs_remove_link(struct kobject *kobj,char *name);

    4.sysfs约定

    • 首先,sysfs属性应该保证每个文件只导出一个值,该值应该是文本形式而且映射为简单C类型。其目的是为了避免数据的过度结构化或太凌乱。
    • 其次,在sysfs中要以一个清晰的层次组织数据。
    • 最后,记住sysfs提供内核到用户空间的服务,这多少有些用户空间ABI(应用程序二进制接口)的作用。

    17.4.3内核事件层

    • 内核事件层实现了内核到用户的消息通知系统——就是建立在上文一直讨论的kobject基础之上。
    • 每个事件都被赋予了一个动词或动作字符串表示信号。该字符串会以“被修改过”或“未挂载”等词语来描述事件。
    • 最后,每个事件都会有一个可选的负载。
    • 使用kobject和属性不但有利于很好的实现基于sysfs的事件,同时也有利于创建新kobjects对象和属性来表示新对象和数据——它们尚未出现在sysfs中。
  • 相关阅读:
    蚂蚁金服合作的RISE实验室到底有多牛?
    2016年全球IC设计大厂营收排名:高通稳居龙头
    2016年全球IC设计大厂营收排名:高通稳居龙头
    2016年全球IC设计大厂营收排名:高通稳居龙头
    2016年全球IC设计大厂营收排名:高通稳居龙头
    C++模板遇到iterator时候遇到的问题和解决方法
    C++模板遇到iterator时候遇到的问题和解决方法
    C++模板遇到iterator时候遇到的问题和解决方法
    $("div span")选取里的所有的元素
    ParseError: Unrecognised input. Possibly missing something
  • 原文地址:https://www.cnblogs.com/lxq20135309/p/5450826.html
Copyright © 2020-2023  润新知