http://blog.chinaunix.net/uid-22547469-id-4590385.html?utm_source=jiancool

Linux设备模型就是一栋规模宏大的建筑,为了构建它,需要基本的建筑材料钢筋:kobject、若干钢筋组成的钢架结构:kset,还需要一种机制sysfs,来向外界(用户空间的程序)展示其内部构造。并且通过文件接口的方式实现与外界沟通与互动。

设备文件系统:Linux下一切皆是文件,而设备文件系统包括设备文件、设备节点以及设备特定文件,它们是驱动程序的接口 ,而在文件系统中,他们就像普通文件一样。

devfs:是一种Linux2.4内核引入的设备文件系统,用来解决Linux中设备管理混乱的问题位于内核空间,可以通过程序在设备初始化时在/dev下创建设备文件,卸载时将它删除,现已被udev取代。

udev:udev是一种工具,它能够根据系统中的硬件设备的状况动态更新设备文件,包括设备文件的创建,删除等。设备文件通常放在/dev目录下,使用udev后,在/dev下面只包含系统中真实存在的设备。它于硬件平台无关的,位于用户空间,需要内核sysfs和tmpfs的支持,sysfs为udev提供设备入口和uevent通道,tmpfs为udev设备文件提供存放空间。

mdev:在嵌入式系统中,通常可以用udev的轻量级版本mdev,集成于busybox。

sysfs:是Linux2.6所提供的一种跟devfs一样的虚拟文件系统,基于ramfs(内存文件系统)开发,挂在到/sys目录,也是用来对系统的设备进行管理的。sysfs以文件目录层次结构展示了系统硬件设备间的一个拓扑图。

设备、驱动、总线:这些都是对象,已经是宏伟建筑给外界的展现形式的那部分了,主要是与这三个打交道。

详细实现:

1kobject[钢筋]

kobject是组成设备模型的一个最底层的核心数据结构,与sysfs文件系统自然的绑定在一起:每个kobject对应sysfs文件系统中一个目录。kobject结构所能处理的任务以及所支持的代码包括:

(1)引用计数;

(2)维持容器的层次列表和组。

(3)为容器的属性提供一种用户态查看的视图。

(4)热插拔事件的处理:当系统中的硬件热插拔时,在kset(其内嵌有kobject)内核对象的控制下,将产生事件以通知用户空间,该结构一般没有单独定义而是嵌入到其他设备结构体中。

kobject定义如下:

点击(此处)折叠或打开

  1. <kobject.h>
  2. struct kobject{
  3. const char *name; //该内核对象的名称,在sysfs中,表现形式是一个新的目录名
  4. struct list_head entry;//用来将一些列的内核对象构成链表
  5. struct kobject *parent;//指向设备分层的上一层的节点,构建层次化关系
  6. struct kset *kset;    //所属的kset对象的指针
  7. struct kobj_type ktype;//用于保存属性,在sysfs中表现为属性文件
  8. struct sysfs_dirent *sd;
  9. struct kref kref;//对象引用计数
  10. ......
  11. }

创建kobject的时候,都回、会给每个kobject一系列默认属性。这些属性保存在kobj_type结构中。

点击(此处)折叠或打开

  1. <kobject.h>
  2. struct kobj_type {
  3. void (*release)(struct kobject *);
  4. struct sysfs_ops    * sysfs_ops;//真正实现这些属性
  5. struct attribute    ** default_attrs;//属性列表,最后一个元素用零填充。
  6. };
  7. <sysfs.h>
  8. struct attribute {
  9. const char        * name;//属性的名字
  10. struct module         * owner;//指向模块的指针
  11. mode_t            mode;//保护位,只读:S_IRUGO。可写:S_IWUSR
  12. };
  13. struct sysfs_ops {
  14. ssize_t    (*show)(struct kobject *, struct attribute *,char *);
  15. ssize_t    (*store)(struct kobject *,struct attribute *,const char *, size_t);
  16. }

kobject一般不单独定义,常内嵌于其他结构体中。

经常遇到相反的问题,对于给定的一个kobject指针,如何获得包含他的结构指针呢?嘎嘎,大名鼎鼎的container_of宏有有用了。

比如kp是指向kobj的指针,而kobj是cdev device的一个成员,如何根据kp获取只想cdev的指针,如下:

struct cdev *device=container_of(kp,struct cdev,kobj);

kobject相关函数:

(1)初始化前第一个操作,将整个kobject设置为0,通常使用memset函数,例如:memset(&kobj,0,sizeof(struct kobject));

(2)设定kobject中的name成员,使用如下函数:
int kobject_set_name(struct kobject *kobj,const char *fmt,...)

(3)初始化一个内核对象的kobject结构,建立kojbect对象间的层次关系,在sysfs中建立一个目录:

int kobject_init_and_add(struct kobject *kobj,

struct kobj_type ktype,struct kobject *parent,const char *fmt,...)

(4)从linux设备层次中删除kobj对象:

void kobject_del(struct kobject *kobj);

(5)增加kobject引用计数:

struct kobject * kobject_get(struct kobject * kobj)

(6)减少kobject引用计数:

void kobject_put(struct kobject * kobj)

(7)每个kobject都必须有一个release方法,该方法保存在kobject相关联的kobj_type中。

在sysfs中,kobject对应目录,kobject的属性对应这个目录下的文件。调用show和store函数来读写文件,就可以得到属性中的内容。

点击(此处)折叠或打开

  1. kobject.c
  2. #include <linux/device.h>
  3. #include <linux/module.h>
  4. #include <linux/kernel.h>
  5. #include <linux/init.h>
  6. #include <linux/string.h>
  7. #include <linux/sysfs.h>
  8. #include <linux/stat.h>
  9. #include <linux/mm.h>
  10. /* 每个kobject必须有一个release方法,该方法保存在kobject相关联的kobj_type中 */
  11. void kobj_release(struct kobject *kobj)
  12. {
  13. printk("obj_release!\n");
  14. }
  15. ssize_t ops_show(struct kobject *kobj,struct attribute *attr,char *c)
  16. {
  17. printk("ops_show\n");
  18. printk("attr->name:%s \n",attr->name);
  19. return strlen(attr->name);
  20. }
  21. ssize_t ops_store(struct kobject *kobj,struct attribute *attr,const char *buf,size_t count)
  22. {
  23. printk("ops_store\n");
  24. printk("buf:%s\n",buf);
  25. return count;
  26. }
  27. struct attribute first_attr={
  28. .name="first_attr",//属性名字
  29. .mode=S_IRWXUGO,    //读写执行权限
  30. };
  31. struct attribute second_attr={
  32. .name="second_attr",//属性名字
  33. .mode=S_IRUGO,        //只读
  34. };
  35. static struct attribute *def_attrs[]={//指针数组,每个成员存储了一个attribute的地址。
  36. &first_attr,
  37. &second_attr,
  38. NULL,
  39. };
  40. struct sysfs_ops sysops={        //定义sysops
  41. .show        =    ops_show,
  42. .store    =    ops_store,
  43. };
  44. struct kobj_type ktype={    //定义ktype
  45. .release    =    kobj_release,    //每个kojbect必须包含的release方法
  46. .sysfs_ops    =    &sysops,        //实现属性的函数
  47. .default_attrs    =    def_attrs,//属性列表,二级指针,最后一个元素必须用NULL填充
  48. };
  49. struct kobject kobj;    //定义kobject结构体变量
  50. static __init int kobj_test_init(void) {        //加载kobject.ko时执行该函数
  51. printk("\n kobj_test_init\n");
  52. memset(&kobj,0,sizeof(struct kobject));
  53. printk("memset kobj\n");
  54. kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");
  55. printk("kobject_init_and_add\n");
  56. return 0;
  57. }
  58. static __exit void kobj_test_exit(void) {    //卸载kobject时执行此函数
  59. printk("kobj_test_exit\n");
  60. kobject_del(&kobj);
  61. //return 0;
  62. }
  63. module_init(kobj_test_init);
  64. module_exit(kobj_test_exit);
  65. MODULE_AUTHOR("MANGO");
  66. MODULE_LICENSE("Dual BSD/GPL")

编译完成后生成kobject.ko文件,执行sudo insmod kobject.ko

加载成功,因该kobject的parent是NULL,会在sys的目录之间建立如下文件:

打开文件夹后,会看到两个属性文件:

first_attr是777权限,second_attr是只读权限,和程序中写的相同。

接下来,执行 cat first_attr,会发现驱动层自动调用了ktype中的sysfs_ops的show函数:

同理,执行echo “123” > first_attr,应该调用ops_store函数,执行结果如下:

和预想完全相同。同理可测试second_attr文件,只是不能写而已。

2、kset[由若干个kobject(钢筋)组成的钢架结构]

kset是同种类型的kobject对象的集合,也可以说是对象的容器,通过kset数据结构可以将kobjects组成一颗层次树。kset的名字保存在内嵌的kobject中。

包含在kset中的所有kobject被组织成一个双向循环链表,list是这个链表的链表头。kset数据结构还内嵌了一个kobject对象(kobj),所有属于这个kset的kobject对象的parent域均指向这个内嵌的对象。此外,kset还依赖kobj维护引用计数。

kset和它的kobject的关系如下图所示:

黑线:kset child list

绿线:kobject->parent

蓝线:kobject->kset

注意:一个kobject的父节点不一定是包含他的kset。

数据结构如下:

点击(此处)折叠或打开

  1. struct kset {
  2. struct kobj_type    * ktype;
  3. struct list_head    list;//用于连接该kset中所有kobject的链表头
  4. spinlock_t        list_lock;//用于互斥访问
  5. struct kobject        kobj;//嵌入的kobject
  6. struct kset_uevent_ops    * uevent_ops;
  7. }

kset相关函数:

创建一个对象时,通常要把一个kobject添加到kset中去。两个步骤:

先把kobject内的kset指针指向目的kset

int kobject_add(struct kobject *kobj);

(1)初始化一个kset对象:

void kset_init(struct kset * k);

(2)用来初始化并向系统注册一个kset对象:

int kobject_register(struct kobject *kobj);

(3)注销kset

void kset_unregister(struct kset *k);

(4)某些时候,将kobject从kset中删除,使用:

void kobject_del(struct kobject *kobj);

点击(此处)折叠或打开

  1. kset.c
  2. #include <linux/device.h>
  3. #include <linux/module.h>
  4. #include <linux/kernel.h>
  5. #include <linux/init.h>
  6. #include <linux/string.h>
  7. #include <linux/sysfs.h>
  8. #include <linux/stat.h>
  9. #include <linux/mm.h>
  10. struct kset kset_p;    //父级kset结构体变量
  11. struct kset kset_c;    //子级kset结构体变量
  12. int kset_filter(struct kset *kset,struct kobject *kobj) {
  13. printk("kset_filter()kobj->name:%s\n",kobj->name);
  14. return 1;
  15. }
  16. const char *kset_name(struct kset *kset,struct kobject *kobj) {
  17. static char buf[20];
  18. printk("kset_name()kobj->name:%s\n",kobj->name);
  19. sprintf(buf,"%s","kset_name");
  20. return buf;
  21. }
  22. int kset_uevent(struct kset *kset,struct kobject *kobj,struct kobj_uevent_env *env){
  23. int i=0;
  24. printk("kset_uevent()kobj->name:%s\n",kobj->name);
  25. printk("env:\n");
  26. while(i<env->envp_idx){
  27. printk("%s\n",env->envp[i]);
  28. i++;
  29. }
  30. return 0;
  31. }
  32. struct kset_uevent_ops uevent_ops={
  33. .filter        =    kset_filter,
  34. .name            =    kset_name,
  35. .uevent        =    kset_uevent,
  36. };
  37. struct kobj_type my_kobj_type;    //定义ktype变量
  38. static __init int kset_test_init(void) {    //sudo insmod kset.ko调用此函数
  39. printk("\n kset_test_init\n");
  40. kobject_set_name(&kset_p.kobj,"kset_p");    //由于kset名字保存在内嵌的kobject中
  41. kset_p.uevent_ops=&uevent_ops;            //事件通知机制。
  42. kset_p.kobj.ktype=&my_kobj_type;    //为了规避oops的问题。
  43. if(kset_register(&kset_p)){                    //向系统注册一个kset对象
  44. printk("register kset p error\n");
  45. return -1;
  46. }
  47. kobject_set_name(&kset_c.kobj,"kset_c");
  48. kset_c.kobj.kset=&kset_p;
  49. kset_c.kobj.ktype=&my_kobj_type;
  50. if(kset_register(&kset_c)){
  51. printk("register kset c error\n");
  52. return -1;
  53. }
  54. return 0;
  55. }
  56. static __exit void kset_test_exit(void)    {
  57. printk("\nkset exit\n");
  58. kset_unregister(&kset_p);
  59. kset_unregister(&kset_c);
  60. }
  61. module_init(kset_test_init);
  62. module_exit(kset_test_exit);
  63. MODULE_AUTHOR("MANGO");
  64. MODULE_LICENSE("Dual BSD/GPL");

执行sudo insmod kset.ko:出现如下信息;

在/sys文件夹,会生成kset_p文件夹,打开该文件夹后,出现kset_c文件夹。

3、子系统(一个子系统其实就是对kset和一个信号量的封装)

点击(此处)折叠或打开

  1. struct subsystem{
  2. struct kset kset;
  3. struct rw_semaphore rwsem;
  4. }

子系统函数列表如下:

void subsystem_init(struct kset *);

int  subsystem_register(struct kset *);

void subsystem_unregister(struct kset *);

struct kset *subsys_get(struct kset *s);

void subsys_put(struct kset *s);

4、属性

非默认属性

如果希望在kobject的sysfs目录中添加新的属性,只需要填写一个attribute结构,并把它传递给下面的函数。

int sysfs_create_file(struct kobject *kobj,struct attribute *attr);

删除属性:

int sysfs_remove_file(struct kobject *kobj,struct attribute *attr);

二进制属性(比如支持向设备上载固件的功能)

先定义一个二进制属性结构:

struct bin_attribute {

struct attribute attr;//名字,所有者,二进制属性的权限

size_t size;//最大长度,如果没有最大值设置为0

void *private;

ssize_t (*read)(struct kobject *, char *, loff_t, size_t);

ssize_t (*write)(struct kobject *, char *, loff_t, size_t);

int (*mmap)(struct kobject *, struct bin_attribute *attr,

    struct vm_area_struct *vma);

};

必须显式的创建二进制属性

int  sysfs_create_bin_file(struct kobject *kobj,struct bin_attribute *attr);

void sysfs_remove_bin_file(struct kobject *kobj,struct bin_attribute *attr);

符号链接

sysfs具有常用的树形结构,但是内核中各对象之间的关系远比这复杂。这些树并不能表示驱动程序及其管理的设备之间的关系,为了表示这种关系还需要其他的指针,那就是符号链接。

int sysfs_create_link(struct kobject *kobj,struct kobject *target,const char *name);

void sysfs_remove_link(struct kobject *, const char * name);


5、热插拔机制(从内核空间发送到用户空间的通知)

当一个设备动态加入系统时(比如插入一个U盘),设备驱动程序可以检查到这种设备状态的变化(加入或移除),然后通过某种机制使得在用户空间找到该设备对应的驱动程序模块并加载之。在linux系统上有两种机制可以在设备状态发生变化时,通知用户空间去加载或者卸载该设备所对应的驱动程序模块:一个是udev,另一个是/sbin/hotplug。在Linux早期阶段只有唯一的/sbin/hotplug工具,随着内核的发展,udev机制逐渐取代了/sbin/hotplug。很多ubuntu系统已经不提供/sbin/hotplug工具了。

udev热插拔机制:以守护进程的形式运行,通过侦听内核发出来的 uevent 来管理 /dev目录下的设备文件。不像之前的设备管理工具,udev 在用户空间 (user space) 运行,而不在内核空间 (kernel space) 运行。(验证:ps aux | grep udevd);当设备添加 / 删除时,udev的守护进程侦听来自内核的uevent,以此添加或者删除 /dev下的设备文件,所以udev只为已经连接的设备产生设备文件,而不会在/dev下产生大量虚无的设备文件;通过 Linux 默认的规则文件,udev在/dev/ 里为所有的设备定义了内核设备名称,比如/dev/sda、/dev/hda、/dev/fd等等。由于udev是在用户空间(user space)运行,Linux 用户可以通过自定义的规则文件,灵活地产生标识性强的设备文件名,比如 /dev/boot_disk、/dev/root_disk、/dev/color_printer等等。

1.the linux device model--kobject kset学习笔记相关推荐

  1. Linux内核设计与实现学习笔记目录

    **注:**这是别人的笔记,我只是把目录抄过来 <Linux内核设计与实现学习笔记> 1.<Linux内核设计与实现>读书笔记(一)-内核简介 2.<Linux内核设计与 ...

  2. 《Linux高性能服务器编程》学习笔记

    <Linux高性能服务器编程>学习笔记 Linux高性能服务器编程 TCP/IP协议族 TCP/IP协议族体系结构以及主要协议 数据链路层 网络层 传输层 应用层 封装 分用 测试网络 A ...

  3. 《深度探索C++对象模型(Inside The C++ Object Model )》学习笔记

    来源:http://dsqiu.iteye.com/blog/1669614 之前一直对C++内部的原理的完全空白,然后找到<Inside The C++ Object Model>这本书 ...

  4. 鸟叔linux私房菜基础篇简体,鸟叔的Linux私房菜基础篇-学习笔记(一)

    鸟叔的Linux私房菜基础篇-学习笔记(一) 开机进入命令行模式: ctrl+alt+[F1-F6]的任意键进入命令行编辑界面 ctrl+alt+F7进入图形界面模式 开始下达指令 [dmtsai@s ...

  5. Linux C/C++ 开发(学习笔记十一 ):TCP服务器(并发网络网络编程 一请求一线程)

    Linux C/C++ 开发(学习笔记十一 ):TCP服务器(并发网络网络编程 一请求一线程) 一.TCP服务器(一请求一线程) 的原理 二.完整代码 三.测试 四.补充 一.TCP服务器(一请求一线 ...

  6. 《鸟哥的Linux私房菜》个人学习笔记-第一篇

    <鸟哥的Linux私房菜>个人学习笔记-基础篇 这是一篇一个linux菜鸡自学的笔记 csdn上的各位大手子们好,本人实习生一枚最近想自己深入学习下linux,所以在社区里发博客,希望能记 ...

  7. Class4 Linux云上环境搭建学习笔记

    Class4 Linux云上环境搭建学习笔记 Linux的远程管理 为Linux环境安装图形化桌面(Gnome) 学习Linux的基本操作 更新一个官方教程 附阿里云高校学习计划的地址 class4 ...

  8. Linux设备模型 kobject kset

    http://www.wowotech.net/device_model/13.html 1. 前言 在"Linux内核的整体架构"中,蜗蜗有提到,由于Linux支持世界上几乎所有 ...

  9. The Linux device model

    /sys和 /dev的疑问 1./dev 下放的是设备文件,是由应用层mknod创建的文件.假设底层驱动对mknod的设备号有相应的驱动,如open等函数.那么应用层open "/dev/* ...

最新文章

  1. ASP.net中太长的数据缩略显示
  2. 平面上给定n条线段,找出一个点,使这个点到这n条线段的距离和最小。
  3. 毕业即失业?你到底在焦虑什么
  4. kafka topic 一段时间不消费_全网最通俗易懂的 Kafka 入门
  5. 页眉页脚怎么单独设置某一页里面的_Word小技巧:如何从任意页面开始设置页眉页脚...
  6. 中国电子学会scratch等级考试三级
  7. Java Stream API进阶篇
  8. 由任意二叉树的前序遍历序列和中序遍历序列求二叉树的思想方法_算法与数据结构基础 - 二叉树(Binary Tree)...
  9. 【bzoj1911】[Apio2010]特别行动队 斜率优化dp
  10. Android学习笔记---Android平台1.使用dom解析xml文件
  11. 单元测试的四大具体效益
  12. E72上安装fring使用skypeout拨打电话
  13. Scala的Tuple元素个数的限制问题
  14. 使用inno setup制作安装包
  15. Ps流转H264流 代码实现
  16. word或excel打开很慢的处理办法
  17. 手游脚本_雷电模拟器
  18. 阿里巴巴icon在vue项目中使用方法
  19. Apple Silicon M1 MacBook Air上手详细评测M1的强大!
  20. vue 网页滚动到指定位置显示动画效果

热门文章

  1. NDK-r14b + FFmpeg-release-3.4 linux下编译FFmpeg
  2. mybatis多产数_freeCodeCamp杰出贡献者–我们如何选择,认可和奖励多产的志愿者
  3. 蛮力写算法_蛮力算法解释
  4. react和react2_为什么React16是React开发人员的福气
  5. 业余爱好者linux_如何从业余爱好者变成专业开发人员
  6. 业精于勤荒于嬉---Go的GORM查询
  7. mysql常用操作记录
  8. CSS 选择器权重计算规则
  9. [51Nod 1218] 最长递增子序列 V2 (LIS)
  10. Http中的同步请求和异步请求