Linux设备驱动模型一 sysfs

1 Linux设备模型

Linux 2.5的内核引入了一种新的设备模型,目的是对计算机上的所有设备进行统一的管理。

它包含以下基础结构:

类型

说明

设备Devices

设备是此模型中最基本的类型,以设备本身的连接按层次组织

设备驱动Device Drivers

在一个系统中安装多个相同设备,只需要一份驱动程序的支持

总线类型Bus Types

在整个总线级别对此总线上连接的所有设备进行管理

设备类别Device Classes

这是按照功能进行分类组织的设备层次树;如 USB 接口和 PS/2 接口的鼠标都是输入设备,都会出现在 /sys/class/input/ 下

Linux通过以上模型把实现设备的统一管理,使驱动和设备自动的匹配。

2 sysfs文件系统

sysfs是一种基于内存的虚拟文件系统,它提供目录、属性文件、链接文件等,让用户空间能便捷地访问kernel的设备信息。

在内部实现上,sysfs以kobject数据结构为最小的粒度,进而封装成kset、subsystem、bus_type、class等模型来组织设备间的拓扑关系。

2.1sysfs顶层目录

/sys的顶层目录结构如下:

/sys 下的子目录

所包含的内容

/sys/devices

这是内核对系统中所有设备的分层次表达模型,也是 /sys 文件系统管理设备的最重要的目录结构,下文会对它的内部结构作进一步分析;

/sys/dev

这个目录下维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件,它是在内核 2.6.26 首次引入;

/sys/bus

这是内核设备按总线类型分层放置的目录结构, devices 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,它也是构成 Linux 统一设备模型的一部分;

/sys/class

这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在 /sys/class/input 之下,而不论它们是以何种总线连接到系统。它也是构成 Linux 统一设备模型的一部分;

/sys/block

这里是系统中当前所有的块设备所在,按照功能来说放置在 /sys/class 之下会更合适,但只是由于历史遗留因素而一直存在于 /sys/block, 但从 2.6.22 开始就已标记为过时,只有在打开了 CONFIG_SYSFS_DEPRECATED 配置下编译才会有这个目录的存在,并且在 2.6.26 内核中已正式移到 /sys/class/block, 旧的接口 /sys/block 为了向后兼容保留存在,但其中的内容已经变为指向它们在 /sys/devices/ 中真实设备的符号链接文件;

/sys/firmware

这里是系统加载固件机制的对用户空间的接口,关于固件有专用于固件加载的一套API,在附录 LDD3 一书中有关于内核支持固件加载机制的更详细的介绍;

/sys/fs

这里按照设计是用于描述系统中所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点,但目前只有 fuse,gfs2 等少数文件系统支持 sysfs 接口,一些传统的虚拟文件系统(VFS)层次控制参数仍然在 sysctl (/proc/sys/fs) 接口中中;

/sys/kernel

这里是内核所有可调整参数的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的 slab 分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于 sysctl (/proc/sys/kernel) 接口中 ;

/sys/module

这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko文件),都可能会出现在 /sys/module 中:

编译为外部模块(ko文件)在加载后会出现对应的 /sys/module/<module_name>/, 并且在这个目录下会出现一些属性文件和属性目录来表示此外部模块的一些信息,如版本号、加载状态、所提供的驱动程序等;

编译为内联方式的模块则只在当它有非0属性的模块参数时会出现对应的 /sys/module/<module_name>, 这些模块的可用参数会出现在 /sys/modules/<modname>/parameters/<param_name> 中,

如 /sys/module/printk/parameters/time 这个可读写参数控制着内联模块 printk 在打印内核消息时是否加上时间前缀;

所有内联模块的参数也可以由 "<module_name>.<param_name>=<value>" 的形式写在内核启动参数上,如启动内核时加上参数 "printk.time=1" 与 向 "/sys/module/printk/parameters/time" 写入1的效果相同;

没有非0属性参数的内联模块不会出现于此。

/sys/power

这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。

/sys/slab (对应 2.6.23 内核,在 2.6.24 以后移至 /sys/kernel/slab)

从2.6.23 开始可以选择 SLAB 内存分配器的实现,并且新的 SLUB(Unqueued Slab Allocator)被设置为缺省值;如果编译了此选项,在 /sys 下就会出现 /sys/slab ,里面有每一个 kmem_cache 结构体的可调整参数。对应于旧的 SLAB 内存分配器下的 /proc/slabinfo 动态调整接口,新式的 /sys/kernel/slab/<slab_name> 接口中的各项信息和可调整项显得更为清晰。

2.2 sysfs初始化分析

首先看fs/sysfs/mount.c中的sysfs_init函数

[cpp] view plain copy
  1. int __init sysfs_init(void)
  2. {
  3. int err = -ENOMEM;
  4. /*创建高速缓存*/
  5. sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",
  6. sizeof(struct sysfs_dirent),
  7. 0, 0, NULL);
  8. if (!sysfs_dir_cachep)
  9. goto out;
  10. /*初始化sysfs bdi设备信息*/
  11. err = sysfs_inode_init();
  12. if (err)
  13. goto out_err;
  14. /*注册sysfs文件系统*/
  15. err = register_filesystem(&sysfs_fs_type);
  16. if (!err) {
  17. sysfs_mnt = kern_mount(&sysfs_fs_type);
  18. if (IS_ERR(sysfs_mnt)) {
  19. printk(KERN_ERR "sysfs: could not mount!\n");
  20. err = PTR_ERR(sysfs_mnt);
  21. sysfs_mnt = NULL;
  22. unregister_filesystem(&sysfs_fs_type);
  23. goto out_err;
  24. }
  25. } else
  26. goto out_err;
  27. out:
  28. return err;
  29. out_err:
  30. kmem_cache_destroy(sysfs_dir_cachep);
  31. sysfs_dir_cachep = NULL;
  32. goto out;
  33. }

sysfs_dir_cachep是一个高速缓存,后续在sysfs中需要用到的对象内存,都会在这个缓存中开辟。

sysfs_inode_init函数初始化backing_dev_info对象,它描述磁盘块设备的相关信息,暂不深究。

register_filesystem函数向系统注册sysfs文件系统,其中sysfs_fs_type是一个file_system_type对象,用来描述sysfs文件系统信息:

[cpp] view plain copy
  1. struct file_system_type {
  2. /*名称*/
  3. const char *name;
  4. /*指定文件系统的一些特性*/
  5. int fs_flags;
  6. /*文件系统加载函数*/
  7. struct dentry *(*mount) (struct file_system_type *, int,
  8. const char *, void *);
  9. void (*kill_sb) (struct super_block *);
  10. /*指向module的指针,如果是非module方式注册的,owner为NULL*/
  11. struct module *owner;
  12. /*所有的file_system通过next指针组成链表,如ext2、ext3、proc等*/
  13. struct file_system_type * next;
  14. /*superblock链表 */
  15. struct list_head fs_supers;
  16. struct lock_class_key s_lock_key;
  17. struct lock_class_key s_umount_key;
  18. struct lock_class_key s_vfs_rename_key;
  19. struct lock_class_key i_lock_key;
  20. struct lock_class_key i_mutex_key;
  21. struct lock_class_key i_mutex_dir_key;
  22. struct lock_class_key i_alloc_sem_key;
  23. };
  24. /*sysfs file_system*/
  25. static struct file_system_type sysfs_fs_type = {
  26. .name       = "sysfs",
  27. .mount      = sysfs_mount,
  28. .kill_sb    = sysfs_kill_sb,
  29. };

需要注意的是,系统把所有的file_system_type通过next指针链接成链表。

sysfs_fs_type还定义了其mout成员函数为sysfs_mount。

register_filesystem函数的实现如下:

[cpp] view plain copy
  1. /**
  2. *  register_filesystem - register a new filesystem
  3. *  @fs: the file system structure
  4. *
  5. *  Adds the file system passed to the list of file systems the kernel
  6. *  is aware of for mount and other syscalls. Returns 0 on success,
  7. *  or a negative errno code on an error.
  8. *
  9. *  The &struct file_system_type that is passed is linked into the kernel
  10. *  structures and must not be freed until the file system has been
  11. *  unregistered.
  12. */
  13. int register_filesystem(struct file_system_type * fs)
  14. {
  15. int res = 0;
  16. struct file_system_type ** p;
  17. BUG_ON(strchr(fs->name, '.'));
  18. if (fs->next)
  19. return -EBUSY;
  20. INIT_LIST_HEAD(&fs->fs_supers);
  21. write_lock(&file_systems_lock);
  22. /*所有filesystem通过next组成链表,在链表中查找名称为fs->name的filestem
  23. 如果找到,则返回filestem地址;没找到,则返回链表尾的next指针地址*/
  24. p = find_filesystem(fs->name, strlen(fs->name));
  25. if (*p)
  26. res = -EBUSY;
  27. else
  28. *p = fs;/*链接到链表尾*/
  29. write_unlock(&file_systems_lock);
  30. return res;
  31. }

register_filesystem通过调用find_filesystem在文件系统链表中查找名称为fs->name的文件系统,如果找到,则返回file_system_type地址。如果没找到,则返回链表尾的next指针地址,并把当前的file_system_type链接到链表尾端,这样就完成了sysfs文件系统的注册。

以下是find_filesystem函数的具体实现:

[cpp] view plain copy
  1. static struct file_system_type **find_filesystem(const char *name, unsigned len)
  2. {
  3. struct file_system_type **p;
  4. /*通过next成员遍历file_systems链表*/
  5. for (p=&file_systems; *p; p=&(*p)->next)
  6. if (strlen((*p)->name) == len &&
  7. strncmp((*p)->name, name, len) == 0)
  8. break;
  9. return p;
  10. }

回到sysfs_fs_type的sysfs_mount函数,顾名思义,它完成了sysfs文件系统的加载,以下是具体实现:

[cpp] view plain copy
  1. static struct dentry *sysfs_mount(struct file_system_type *fs_type,
  2. int flags, const char *dev_name, void *data)
  3. {
  4. struct sysfs_super_info *info;
  5. enum kobj_ns_type type;
  6. struct super_block *sb;
  7. int error;
  8. info = kzalloc(sizeof(*info), GFP_KERNEL);
  9. if (!info)
  10. return ERR_PTR(-ENOMEM);
  11. for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
  12. info->ns[type] = kobj_ns_grab_current(type);
  13. /*创建superblock*/
  14. sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info);
  15. if (IS_ERR(sb) || sb->s_fs_info != info)
  16. free_sysfs_super_info(info);
  17. if (IS_ERR(sb))
  18. return ERR_CAST(sb);
  19. if (!sb->s_root) {
  20. sb->s_flags = flags;
  21. /*设置superblock,初始化inode和dentry*/
  22. error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
  23. if (error) {
  24. deactivate_locked_super(sb);
  25. return ERR_PTR(error);
  26. }
  27. sb->s_flags |= MS_ACTIVE;
  28. }
  29. return dget(sb->s_root);
  30. }

mount函数先是通过sget函数创建supserblock,再调用sysfs_fill_super初始化inode和dentry信息,下面是sysfs_fill_super的具体实现:

[cpp] view plain copy
  1. static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
  2. {
  3. struct inode *inode;
  4. struct dentry *root;
  5. sb->s_blocksize = PAGE_CACHE_SIZE;
  6. sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
  7. sb->s_magic = SYSFS_MAGIC;
  8. /*设置superblock的操作函数为sysfs_ops*/
  9. sb->s_op = &sysfs_ops;
  10. sb->s_time_gran = 1;
  11. /* get root inode, initialize and unlock it */
  12. mutex_lock(&sysfs_mutex);
  13. /*创建和初始化inode*/
  14. inode = sysfs_get_inode(sb, &sysfs_root);
  15. mutex_unlock(&sysfs_mutex);
  16. if (!inode) {
  17. pr_debug("sysfs: could not get root inode\n");
  18. return -ENOMEM;
  19. }
  20. /* instantiate and link root dentry */
  21. /*创建dentry并关联到inode*/
  22. root = d_alloc_root(inode);
  23. if (!root) {
  24. pr_debug("%s: could not get root dentry!\n",__func__);
  25. iput(inode);
  26. return -ENOMEM;
  27. }
  28. /*dentry关联到sysfs_root*/
  29. root->d_fsdata = &sysfs_root;
  30. /*superblock关联到dentry*/
  31. sb->s_root = root;
  32. return 0;
  33. }

在文件系统中,inode(索引项)用来描述文件在物理存储介质中的索引信息,dentry(目录项)用来描述文件的逻辑信息如文件名等。

sysfs_fill_super先是调用sysfs_get_inode创建和初始化inode,接着通过d_alloc_root函数创建dentry,这里创建的inode和dentry都是针对root结点的。

下面是创建inode的过程:

[cpp] view plain copy
  1. /**
  2. *  sysfs_get_inode - get inode for sysfs_dirent
  3. *  @sb: super block
  4. *  @sd: sysfs_dirent to allocate inode for
  5. *
  6. *  Get inode for @sd.  If such inode doesn't exist, a new inode
  7. *  is allocated and basics are initialized.  New inode is
  8. *  returned locked.
  9. *
  10. *  LOCKING:
  11. *  Kernel thread context (may sleep).
  12. *
  13. *  RETURNS:
  14. *  Pointer to allocated inode on success, NULL on failure.
  15. */
  16. struct inode * sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd)
  17. {
  18. struct inode *inode;
  19. /*从superblock中查找inode,没找到则创建一个新的inode并返回*/
  20. inode = iget_locked(sb, sd->s_ino);
  21. if (inode && (inode->i_state & I_NEW))
  22. sysfs_init_inode(sd, inode);/*初始化inode*/
  23. return inode;
  24. }

sysfs_get_inode函数调用sysfs_init_inode来初始化inode,详细如下:

[cpp] view plain copy
  1. static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
  2. {
  3. struct bin_attribute *bin_attr;
  4. inode->i_private = sysfs_get(sd);
  5. inode->i_mapping->a_ops = &sysfs_aops;
  6. inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
  7. inode->i_op = &sysfs_inode_operations;
  8. set_default_inode_attr(inode, sd->s_mode);
  9. sysfs_refresh_inode(sd, inode);
  10. /* initialize inode according to type */
  11. /*根据sysfs_dirent的类型,设置inode不同的操作函数,分别有目录,属性文件,BIN文件,链接文件
  12. 1)目录对应kobject
  13. 2)属性文件对应kobject的attribute
  14. 3)BIN文件对应kobject的bin_attribute
  15. 4)链接文件对应*/
  16. switch (sysfs_type(sd)) {
  17. case SYSFS_DIR:
  18. inode->i_op = &sysfs_dir_inode_operations;
  19. inode->i_fop = &sysfs_dir_operations;
  20. break;
  21. case SYSFS_KOBJ_ATTR:
  22. inode->i_size = PAGE_SIZE;
  23. inode->i_fop = &sysfs_file_operations;
  24. break;
  25. case SYSFS_KOBJ_BIN_ATTR:
  26. bin_attr = sd->s_bin_attr.bin_attr;
  27. inode->i_size = bin_attr->size;
  28. inode->i_fop = &bin_fops;
  29. break;
  30. case SYSFS_KOBJ_LINK:
  31. inode->i_op = &sysfs_symlink_inode_operations;
  32. break;
  33. default:
  34. BUG();
  35. }
  36. unlock_new_inode(inode);
  37. }

这里指定不同的sysfs_dirent类型所对应的inode操作函数。sysfs的文件类型分别有以下几种:

目录文件

属性文件

BIN文件

链接文件

这几种文件与kobject的对应关系如下(kobject将在下一章中详细介绍):

1)目录对应kobject

2)属性文件对应kobject的attribute

3)BIN文件对应kobject的bin_attribute

4)链接文件对应kobject的sysfs_elem_bin_attr

参考文章:使用 /sys 文件系统访问 Linux 内核

Linux设备驱动模型一 sysfs相关推荐

  1. linux 统一设备模型 pci,Linux设备驱动模型摘抄

    Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄 Linux设备驱动模型摘抄(1) Linux统一设备模型 简介 Li ...

  2. Linux设备驱动模型1——简介和底层架构

    以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 一.linux设备驱动模型简介 1.什么是设备驱动模型? (1)类class.总线bus.设备device.驱动d ...

  3. 设备驱动,字符设备驱动、(总线)设备驱动模型、sysfs文件系统、平台设备驱动

    以下内容转载于微信公众号:嵌入式企鹅圈.如有侵权,请告知删除. 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术. 对于初学者来说会非常 ...

  4. linux平台设备驱动模型是什么意思,Linux设备驱动模型之我理解

    点击(此处)折叠或打开 /* my_bus.c   */ #include #include #include #include #include #include "my_bus.h&qu ...

  5. Linux中kobject的作用,Linux设备驱动模型-- 数据结构Kset/KObject

    前言 Kset和kobject是Linux设备驱动模型中的核心数据结构,其主要作用是将系统中的设备抽象出来,以树状结构组织,方便系统统一管理. 而这个统一管理的地方,就是sysfs,先放一张示例图,阐 ...

  6. 设备驱动模型与sysfs

    设备驱动模型与sysfs Linux在2.6版本引入了设备驱动模型,设备驱动模型负责统一实现和维护一些特性,诸如:热插拔.对象生命周期.用户空间和驱动空间的交互等基础设施 1.设备驱动模型基本概念 设 ...

  7. Linux设备驱动模型三 kset

    Linux设备驱动模型三 kset 1 kset数据结构 kset的定义在前文已有描述,我们再回顾一下: [cpp] view plain copy struct kset { /*与子kobject ...

  8. Linux设备驱动模型二 kobject

    Linux设备驱动模型二 kobject 1 kobject 1.1 kobject数据结构 kobject是sysfs文件系统的基础数据结构,它定义在include/linux/kobjec.h中 ...

  9. 五.linux设备驱动模型

    站在设备驱动这个角度分析,设备驱动模型是如何构建出来,起到什么作用,认识它并在写驱动的时候去利用设备驱动模型 目录 一.linux 设备驱动模型简介 1.1. 什么是设备驱动模型 1.2. 为什么需要 ...

最新文章

  1. Go 学习笔记(34)— Go 方法声明、方法调用、方法值、方法表达式、切片对象方法、指针对象方法
  2. banana pi BPI-R1 原理图下载地址:
  3. python setup.py install 安装的包 卸载方法
  4. Bootstrap4+MySQL前后端综合实训-Day07-AM【ajax局部刷新——(单个/批量)删除用户按钮的实现、更新用户信息按钮的实现】
  5. vsphere平台windows虚拟机克隆的小插曲(无法登陆系统)
  6. intel编译器_Intel编译器编译并行版lammps
  7. printf如何消后续0_Hello World背后的故事:如何在Linux上编译C语言程序
  8. [问题]apparmor 问题导致mysql切换datadir目录失败
  9. 【优化分类】基于matlab遗传算法结合爬山算法优化极限学习机分类【含Matlab源码 1660期】
  10. windows下安装sqlmap 详细教程
  11. 开源资产管理软件OCS Inventory 实践
  12. Flutter技术在会展云中大显身手
  13. 新手网站制作教程:网站建设流程及步骤有哪些?
  14. 【CF487E】Tourists-圆方树+multiset+树链剖分
  15. dfs和bfs走迷宫
  16. 用swoole实现简单IM聊天室demo
  17. 计算机网络-IP和子网掩码及网络划分
  18. CSS权威指南(五)字体
  19. 如何在 Exchange Online 中对指定邮箱账户启用 SMTP 基本身份认证?
  20. 亚信科技java笔试题答案_亚信科技笔试题

热门文章

  1. Redis学习手册(List数据类型)(转)
  2. Red Hat EnterPrise Linux 5.4下web服务器的综合使用(普通站点、虚拟主机、安全性、...
  3. sketchup ruby编程之绘制梯段
  4. 工信部召开地方信息安全工作会议
  5. MOSS2007-学习笔记-备忘录-单点登录-(1)-我的网站'?
  6. 研发工程规范性模型-DevSecOps
  7. 10个最棒的jQuery视频插件
  8. html排版跟代码不一致_用壹伴助手,几分钟搞定公众号排版
  9. c语言中输入大数,如何使用C语言实现输入10个数按从大到小的顺序排序输出
  10. 滑动验证码研究-后续