内容转载于http://blog.csdn.net/yueqian_scut/article/details/46771595。有格式的调整和内容的删改,如有侵权,请告知删除 。

本文将从代码级去理解Linux设备类和设备文件的创建过程。

一、设备类相关知识

  • 设备类是虚拟的,并没有直接对应的物理实物,只是为了更好地管理同一类设备导出到用户空间而产生的目录和文件。
  • 整个过程涉及到sysfs文件系统,该文件系统是为了展示Linux设备驱动模型而构建的文件系统,是基于ramfs,linux根目录中的/sysfs即挂载了sysfs文件系统。
  • Struct kobject数据结构是sysfs的基础,kobject在sysfs中代表一个目录;
  • linux的驱动(struct driver)、设备(struct device)、设备类(struct class)均是从kobject进行派生的,因此他们在sysfs中都对应于一个目录。
  • 数据结构中附属的struct device_attribute、driver_attribute、class_attribute等属性数据结构在sysfs中则代表一个普通的文件。
  • Struct kset是struct kobject的容器,即Struct kset可以成为同一类struct kobject的父亲,而其自身也有kobject成员,因此其又可能和其他kobject成为上一级kset的子成员。

二、两种创建设备文件的方式

  • 在设备驱动中,cdev_add将struct file_operations和设备号注册到系统后,为了能够自动产生驱动对应的设备文件,需要调用class_create和device_create,并通过uevent机制调用mdev(嵌入式linux由busybox提供)来调用mknod创建设备文件。
  • 当然也可以不调用这两个接口,那就手工通过命令行mknod来创建设备文件。

三、设备类和设备相关数据结构

(1)include/linux/kobject.h

[cpp] view plaincopy
  1. struct kobject
  2. {
  3. const char *name;//名称
  4. struct list_head entry;//kobject链表
  5. struct kobject *parent;//即所属kset的kobject
  6. struct kset *kset;//所属kset
  7. struct kobj_type *ktype;//属性操作接口
  8. };
  9. struct kset
  10. {
  11. struct list_head list;//管理同属于kset的kobject
  12. struct kobject kobj;//可以成为上一级父kset的子目录
  13. const struct kset_uevent_ops *uevent_ops;//uevent处理接口
  14. };
  • 假设kobject A代表一个目录,kset B代表几个目录(包括A)的共同的父目录。则A.kset=B;A.parent=B.kobj.

(2)include/linux/device.h

[cpp] view plaincopy
  1. struct class
  2. {//设备类
  3. const char *name; //设备类名称
  4. struct module *owner;//创建设备类的module
  5. struct class_attribute *class_attrs;//设备类属性
  6. struct device_attribute *dev_attrs;//设备属性
  7. struct kobject *dev_kobj;//kobject再sysfs中代表一个目录
  8. struct class_private *p;//设备类得以注册到系统的连接件
  9. };

(3)drivers/base/base.h

[cpp] view plaincopy
  1. struct class_private
  2. {
  3. //该设备类同样是一个kset,包含下面的class_devices;同时在class_subsys填充父kset
  4. struct kset class_subsys;
  5. struct klist class_devices;//设备类包含的设备(kobject)
  6. struct class *class;//指向设备类数据结构,即要创建的本级目录信息
  7. };

(4)include/linux/device.h

[cpp] view plaincopy
  1. struct device
  2. {//设备
  3. struct device *parent;//sysfs/devices/中的父设备
  4. struct device_private *p;//设备得以注册到系统的连接件
  5. struct kobject kobj;//设备目录
  6. const char *init_name;//设备名称
  7. struct bus_type *bus;//设备所属总线
  8. struct device_driver *driver; //设备使用的驱动
  9. struct klist_node knode_class;//连接到设备类的klist
  10. struct class *class;//所属设备类
  11. const struct attribute_group **groups;
  12. }

(5)drivers/base/base.h

[cpp] view plaincopy
  1. struct device_private
  2. {
  3. struct klist klist_children;//连接子设备
  4. struct klist_node knode_parent;//加入到父设备链表
  5. struct klist_node knode_driver;//加入到驱动的设备链表
  6. struct klist_node knode_bus;//加入到总线的链表
  7. struct device *device;//对应设备结构
  8. };

(6)解释

  • class_private是class的私有结构,class通过class_private注册到系统中;
  • device_private是device的私有结构,device通过device_private注册到系统中。
  • 所谓注册到系统中,即把相应的数据结构加入到系统已经存在的链表中,但是这些链接的细节并不希望暴露给用户,所以才有private的结构。
  • 而class和device则通过sysfs向用户层提供信息。

四、创建设备类目录文件

1、在驱动通过cdev_add将struct file_operations接口集和设备注册到系统后,即利用class_create接口来创建设备类目录文件。

led_class = class_create(THIS_MODULE, "led_class");

__class_create(owner, name, &__key);

cls->name = name;//设备类名

cls->owner = owner;//所属module

retval = __class_register(cls, key);

struct class_private *cp;

//将类的名字led_class赋值给对应的kset

kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);

//填充class_subsys所属的父kset:ket:sysfs/class.

cp->class_subsys.kobj.kset = class_kset;

//填充class属性操作接口

cp->class_subsys.kobj.ktype = &class_ktype;

cp->class = cls;//通过cp可以找到class

cls->p = cp;//通过class可以找到cp

//创建led_class设备类目录

kset_register(&cp->class_subsys);

//在led_class目录创建class属性文件

add_class_attrs(class_get(cls));

2、继续展开kset_register

kset_register(&cp->class_subsys);

kobject_add_internal(&k->kobj);

// parent即class_kset.kobj,即/sysfs/class对应的目录

parent = kobject_get(kobj->parent);

create_dir(kobj);

//创建一个led _class设备类目录

sysfs_create_dir(kobj);

//该接口是sysfs文件系统接口,代表创建一个目录,不再展开。

3.上述提到的class_kset在class_init被创建

class_kset = kset_create_and_add("class", NULL, NULL);

第三个传参为NULL,代表默认在/sysfs/创建class目录。

五、创建设备目录和设备属性文件

1、利用class_create接口来创建设备类目录文件后,再利用device_create接口来创建具体设备目录和设备属性文件。

led_device = device_create(led_class, NULL, led_devno, NULL, "led");

device_create_vargs

dev->devt = devt;//设备号

dev->class = class;//设备类led_class

dev->parent = parent;//父设备,这里是NULL

kobject_set_name_vargs(&dev->kobj, fmt, args)//设备名”led”

device_register(dev)注册设备

2、继续展开device_register(dev)

device_initialize(dev);

dev->kobj.kset = devices_kset;//设备所属/sysfs/devices/

device_add(dev)

device_private_init(dev)//初始化device_private

dev_set_name(dev, "%s", dev->init_name);//赋值dev->kobject的名称
setup_parent(dev, parent);//建立device和父设备的kobject的联系
//kobject_add在/sysfs/devices/目录下创建设备目录led,kobject_add是和kset_register相似的接口,只不过前者针对kobject,后者针对kset。
kobject_add(&dev->kobj, dev->kobj.parent, NULL);
kobject_add_varg
kobj->parent = parent;
kobject_add_internal(kobj)
create_dir(kobj);//创建设备目录
//在刚创建的/sysfs/devices/led目录下创建uevent属性文件,名称是”uevent”
device_create_file(dev, &uevent_attr);
//在刚创建的/sysfs/devices/led目录下创建dev属性文件,名称是”dev”,该属性文件的内容就是设备号
device_create_file(dev, &devt_attr);
//在/sysfs/class/led_class/目录下建立led设备的符号连接,所以打开/sysfs/class/led_class/led/目录也能看到dev属性文件,读出设备号。
device_add_class_symlinks(dev);
//创建device属性文件,包括设备所属总线的属性和attribute_group属性
device_add_attrs()
bus_add_device(dev) //将设备加入总线
//触发uevent机制,并通过调用mdev来创建设备文件。
kobject_uevent(&dev->kobj, KOBJ_ADD);
//匹配设备和总线的驱动,匹配成功就调用驱动的probe接口,不再展开

bus_probe_device(dev);

3、展开kobject_uevent(&dev->kobj, KOBJ_ADD);

kobject_uevent_env(kobj, action, NULL);
kset = top_kobj->kset; 
uevent_ops = kset->uevent_ops; //即device_uevent_ops
// subsystem即设备所属的设备类的名称”led_class”
subsystem = uevent_ops->name(kset,
 kobj);
//devpath即/sysfs/devices/led/
devpath = kobject_get_path(kobj,
 GFP_KERNEL);
//添加各种环境变量
add_uevent_var(env, "ACTION=%s",
 action_string);
add_uevent_var(env, "DEVPATH=%s",
 devpath);
add_uevent_var(env, "SUBSYSTEM=%s",
 subsystem);
uevent_ops->uevent(kset, kobj,
 env);
add_uevent_var(env, "MAJOR=%u",
 MAJOR(dev->devt));
add_uevent_var(env, "MINOR=%u",
 MINOR(dev->devt));
add_uevent_var(env, "DEVNAME=%s",
 name);
add_uevent_var(env, "DEVTYPE=%s",
 dev->type->name);
//还会增加总线相关的一些属性环境变量等等。
#if defined(CONFIG_NET)//如果是PC的linux会通过socket的方式向应用层发送uevent事件消息,但在嵌入式linux中不启用该机制。
#endif
argv [0] = uevent_helper;//即/sbin/mdev
argv [1] = (char *)subsystem;//”led_class”
argv [2] = NULL;
add_uevent_var(env, "HOME=/");
add_uevent_var(env,"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
call_usermodehelper(argv[0],
 argv,env->envp, UMH_WAIT_EXEC);

4、上述提到的devices_kset在devices_init被创建
devices_kset = kset_create_and_add("devices",
 &device_uevent_ops, NULL);

//第三个传参为NULL,代表默认在/sysfs/创建devices目录

5、上述设备属性文件

static struct device_attribute devt_attr =
__ATTR(dev,
 S_IRUGO, show_dev, NULL);

static ssize_t show_dev(struct device *dev, struct device_attribute
 *attr,char *buf){{
return
 print_dev_t(buf, dev->devt);//即返回设备的设备号

}

6、devices设备目录响应uevent事件的操作

static const struct kset_uevent_ops device_uevent_ops = {
.filter =dev_uevent_filter,
.name = dev_uevent_name,
.uevent = dev_uevent,

};

7、call_usermodehelper是从内核空间调用用户空间程序的接口。

8、对于嵌入式系统来说,busybox采用的是mdev,在系统启动脚本rcS中会使用命令“echo /sbin/mdev > /proc/sys/kernel/hotplug”。

uevent_helper[]数组即读入/proc/sys/kernel/hotplug文件的内容,即“/sbin/mdev” .

六、创建设备文件

  • 以上描述都是在sysfs文件系统中创建目录或者文件,而应用程序访问的设备文件则需要创建在/dev/目录下。该项工作由mdev完成
  • Mdev的原理:解释/etc/mdev.conf文件定义的命名设备文件的规则,并在该规则下根据环境变量的要求来创建设备文件。
  • Mdev.conf由用户层指定,因此更具灵活性。
  • 下面是mdev配置脚本,最终我们会跟踪到mknod在/dev/目录下创建了设备文件。
[cpp] view plaincopy
  1. Busybox/util-linux/mdev.c
  2. int mdev_main(int argc UNUSED_PARAM, char **argv)
  3. xchdir("/dev");
  4. if (argv[1] && strcmp(argv[1], "-s")//系统启动时mdev
  5. –s才会执行这个分支
  6. else
  7. action= getenv("ACTION");
  8. env_path= getenv("DEVPATH");
  9. G.subsystem= getenv("SUBSYSTEM");
  10. snprintf(temp,PATH_MAX, "/sys%s", env_path);//到/sysfs/devices/led目录
  11. make_device(temp,/*delete:*/ 0);
  12. strcpy(dev_maj_min,"/dev"); //读出dev属性文件,得到设备号
  13. open_read_close(path,dev_maj_min + 1, 64);
  14. ….
  15. mknod(node_name,rule->mode | type, makedev(major, minor))

Linux 设备文件的创建和mdev相关推荐

  1. Linux设备文件的创建

    /************************************************************************************ *本文为个人学习记录,如有错 ...

  2. linux 设备文件动态,使用 udev 高效、动态地管理 Linux 设备文件

    概述: Linux 用户常常会很难鉴别同一类型的设备名,比如 eth0, eth1, sda, sdb 等等.通过观察这些设备的内核设备名称,用户通常能知道这些是什么类型的设备,但是不知道哪一个设备是 ...

  3. linux设备文件,Linux 文件系统与设备文件

    1 Linux 文件系统 1.1 Linux 文件系统与设备驱动关系 下图表明了 Linux 中虚拟文件系统.磁盘/Flash文件系统以及一般的设备文件与设备驱动程序之间的关系. 文件系统与设备驱动之 ...

  4. Linux设备文件简介

    设备文件简介 本文为转载,留给自己学习之用 大家可以直接到原作者的空间查看: <Linux设备文件简介> http://lamp.linux.gov.cn/Linux/device_fil ...

  5. Linux设备模型(热插拔、mdev 与 firmware)

    转自:http://blog.chinaunix.net/space.php?uid=20543672&do=blog&cuid=460882 热插拔 有 2 个不同角度来看待热插拔: ...

  6. linux设备文件生成,Linux设备文件自动生成(示例代码)

    第一种是使用mknod手工创建:# mknod 第二种是自动创建设备节点:利用udev(mdev)来实现设备文件的自动创建,首先应保证支持udev(mdev),由busybox配置. 具体udev相关 ...

  7. Linux设备文件简介。

    概述 设备管理是linux中比较基础的东西,但是由于Linux智能程度的越来越高,Udev的使用越来越广泛,使得越来越多的Linux新用户对 /dev 目录下的东西变得不再熟悉.有时候遇见问题就会变得 ...

  8. linux设备文件列表,linux 设备文件

    设备文件: b:按块为单位,随机访问的设备 如:硬盘 c:按字符为单位,线性设备 如:键盘 [root@localhost tm]# ll /dev/ crw-rw---- 1 root audio  ...

  9. linux设备文件的分类,Linux设备模型组件-类设备-设备类及subsystem

    Linux设备模型一.sysfs文件系统: sysfs文件系统是Linux2.6内核引入的,它被看成是与proc.devfs和devpty等同类别的文件系统,sysfs文件系统也是一个虚拟文件系统,它 ...

最新文章

  1. Oracle中的数据字典技术及常用数据字典总结
  2. input 强大的 input 标签
  3. 正则表达式中空格的危害
  4. 【星球知识卡片】残差网络家族10多个变种学习卡片,请收下!
  5. S3C2440的LCD编程
  6. python面向对象之类的成员
  7. DIY制作otto机器人
  8. java类和对象:封装、继承和多态
  9. 数据遍历打印得小列子
  10. JAVA语法——选择排序
  11. select中常用的对象
  12. Unity无缝切换场景
  13. Wireshark捕获IP报文——分片与不分片
  14. 安全管家安卓_网速管家安卓5.4版本全面上线,多场景网络体验全面升级
  15. javaee图书管理系统mysql_基于Java+Jsp+Servlet+Mysql的图书馆管理系统
  16. 通过金矿模型介绍动态规划(转载)
  17. L13:数据结构-5(树和二叉树)
  18. 【Matlab学习手记】三维矢量场图
  19. Apple pay 苹果支付
  20. Spring Security安全框架

热门文章

  1. Javascript DOM对属性的操作
  2. TextTree - 文本资料收集轻量级工具
  3. C++对C的一些加强和变化
  4. Compass样式重置
  5. Android工程的编译过程
  6. WCF中使用HttpSession
  7. 读取config文件中的键值
  8. VS2005中解决方案管理器中看不到解决方案节点的解决办法
  9. 开关语句、循环语句、goto
  10. 数据库小知识点(一直更新)