引子

本文是嵌入式企鹅圈开篇--《linux字符设备驱动剖析》的姐妹篇,在上述文章里面我们具体描写叙述了字符设备驱动框架涉及的驱动注冊、通过设备文件来訪问驱动等知识。并明白通过device_create接口并结合mdev来创建设备文件。但没有展开这个知识点。

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

通过这两篇文章,我们将能够对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的子成员。

本文无意对sysfs和linux设备驱动模型进行展开,以后再另写文章进行分析。

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

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

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

1include/linux/kobject.h

struct kobject {

const char *name;//名称

struct list_head entry;//kobject链表

struct kobject *parent;//即所属kset的kobject

struct kset *kset;//所属kset

struct kobj_type *ktype;//属性操作接口

};

struct kset {

struct list_head list;//管理同属于kset的kobject

struct kobject kobj;//能够成为上一级父kset的子文件夹

const struct kset_uevent_ops *uevent_ops;//uevent处理接口

};

如果Kobject A代表一个文件夹,kset B代表几个文件夹(包含A)的共同的父文件夹。

则A.kset=B;
A.parent=B.kobj.

2include/linux/device.h

struct class {//设备类

const char *name; //设备类名称

struct module *owner;//创建设备类的module

struct class_attribute *class_attrs;//设备类属性

struct device_attribute *dev_attrs;//设备属性

struct kobject *dev_kobj;//kobject再sysfs中代表一个文件夹

….

struct class_private *p;//设备类得以注冊到系统的连接件

};

3drivers/base/base.h

struct class_private {

//该设备类相同是一个kset,包括以下的class_devices。同一时候在class_subsys填充父kset

struct kset class_subsys;

struct klist class_devices;//设备类包括的设备(kobject)

struct class *class;//指向设备类数据结构,即要创建的本级文件夹信息

};

4include/linux/device.h

struct device {//设备

struct device *parent;//sysfs/devices/中的父设备

struct device_private *p;//设备得以注冊到系统的连接件

struct kobject kobj;//设备文件夹

const char *init_name;//设备名称

struct bus_type *bus;//设备所属总线

struct device_driver *driver; //设备使用的驱动

struct klist_node knode_class;//连接到设备类的klist

struct class *class;//所属设备类

const struct attribute_group **groups;

}

5drivers/base/base.h

struct device_private {

struct klist klist_children;//连接子设备

struct klist_node knode_parent;//增加到父设备链表

struct klist_node knode_driver;//增加到驱动的设备链表

struct klist_node knode_bus;//增加到总线的链表

struct device *device;//相应设备结构

};

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” .

六、创建设备文件

轮到mdev出场了,以上描写叙述都是在sysfs文件系统中创建文件夹或者文件,而应用程序訪问的设备文件则须要创建在/dev/文件夹下。该项工作由mdev完毕。

Mdev的原理是解释/etc/mdev.conf文件定义的命名设备文件的规则。并在该规则下依据环境变量的要求来创建设备文件。Mdev.conf由用户层指定,因此更具灵活性。本文无意展开对mdev配置脚本的分析。

Busybox/util-linux/mdev.c

int mdev_main(int argc UNUSED_PARAM, char **argv)

xchdir("/dev");

if (argv[1] && strcmp(argv[1], "-s")//系统启动时mdev
–s才会运行这个分支

else

action
= getenv("ACTION");

env_path
= getenv("DEVPATH");

G.subsystem
= getenv("SUBSYSTEM");

snprintf(temp,
PATH_MAX, "/sys%s", env_path);//到/sysfs/devices/led文件夹

make_device(temp,
/*delete:*/ 0);

strcpy(dev_maj_min,
"/dev"); //读出dev属性文件。得到设备号

open_read_close(path,
dev_maj_min + 1, 64);

….

mknod(node_name,
rule->mode | type, makedev(major, minor))

终于我们会跟踪到mknod在/dev/文件夹下创建了设备文件。

请关注本人的微信公众号-嵌入式企鹅圈。谢谢。

Linux 设备文件的创建和mdev的更多相关文章

  1. (转载)使用 udev 高效、动态地管理 Linux 设备文件

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

  2. 嵌入式 使用udev高效、动态地管理Linux 设备文件

    本文以通俗的方法阐述 udev 及相关术语的概念.udev 的配置文件和规则文件,然后以 Red Hat Enterprise Server 为平台演示一些管理设备文件和查询设备信息的实例.本文会使那 ...

  3. 【转】使用 udev 高效、动态地管理 Linux 设备文件

    简介: 本文以通俗的方法阐述 udev 及相关术语的概念.udev 的配置文件和规则文件,然后以 Red Hat Enterprise Server 为平台演示一些管理设备文件和查询设备信息的实例.本 ...

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

    本文转自:https://www.ibm.com/developerworks/cn/linux/l-cn-udev/index.html 概述: Linux 用户常常会很难鉴别同一类型的设备名,比如 ...

  5. 使用 udev 管理 Linux 设备文件

    本文以通俗的方法阐述 udev 及相关术语的概念.udev 的配置文件和规则文件,然后以 Red Hat Enterprise Server 为平台演示一些管理设备文件和查询设备信息的实例.本文会使那 ...

  6. Linux设备文件自动生成

    第一种是使用mknod手工创建:# mknod <devfilename> <devtype> <major> <minor> 第二种是自动创建设备节点 ...

  7. Linux设备模型(热插拔、mdev 与 firmware)【转】

    转自:http://www.cnblogs.com/hnrainll/archive/2011/06/10/2077469.html 转自:http://blog.chinaunix.net/spac ...

  8. Linux设备文件简介(转载)

    Linux 中的设备有2种类型:字符设备(无缓冲且只能顺序存取).块设备(有缓冲且可以随机存取).每个字符设备和块设备都必须有主.次设备号,主设备号相同的设 备是同类设备(使用同一个驱动程序).这些设 ...

  9. Linux设备文件三大结构:inode,file,file_operations

    驱动程序就是向下控制硬件,向上提供接口,这里的向上提供的接口最终对应到应用层有三种方式:设备文件,/proc,/sys,其中最常用的就是使用设备文件,而Linux设备中用的最多的就是字符设备,本文就以 ...

随机推荐

  1. Oracle 查看锁定表用户的SQL

    #描述:当执行如下SQL select * From XXX for Updata 发现表“XXX”被锁定 #只需要执行如下SQL 查看锁定该表的用户 SELECT object_name, mach ...

  2. 2017.7.15清北夏令营精英班Day1解题报告

    成绩: 预计分数:20+10+40 实际分数:100+10+40. 一百三十多人的比赛全场rand7还水了个鼠标+键盘 unbelievable! 考试题目链接: https://www.luogu. ...

  3. table-layout:fixed属性

    说实话,第一次见到这个属性有点懵圈,真是没见过这个属性 好了,直接说作用 table-layout其实是一种表格算法,用来显示表格单元格.行.列的算法规则. 固定表格布局: 固定表格布局与自动表格布局 ...

  4. jar文件配置冲突问题transformResourcesWithMergeJavaResForDebug

    先看本人AS报错异常 Error:Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'. > c ...

  5. 【SQL优化】SQL优化工具

    SQLAdvisor 是由美团点评公司北京DBA团队开发维护的 SQL 优化工具:输入SQL,输出索引优化建议. 它基于 MySQL 原生词法解析,再结合 SQL 中的 where 条件以及字段选择度 ...

  6. .csv文件内容分隔符

    CSV文件默认以英文逗号做为列分隔符,换行符作为行分隔符.  如果不提供网页形式只用命令行或二进制程序输出数据到CSV,只需要将数据段按,分割,行按\n分割,写入到一个.csv文件即可.  但有时字段 ...

  7. php语法学习:轻松看懂PHP语言

    基础语法 开头结尾 PHP脚本以 "<?php " 开头以 "?>" 结尾 <!DOCTYPE html> <html>&l ...

  8. div+css布局自适应小结

    一.两栏布局(左定宽,右自动)1. float + margin即固定宽度元素设置float属性为left,自适应元素设置margin属性,margin-left应>=定宽元素宽度.举例: &l ...

  9. jenkins 展示报告

    1.下载插件 HTML Publisher plugin 2.设置说明 3.展示css 下载插件 (1).Startup Trigger: 可实现在Jenkins节点(master/slave)启动时 ...

  10. SOAP扩展PHP轻松实现WebService

    最近在一个PHP项目中对接外部接口涉及到WebService,搜索引擎上相关文章不是很多,找到的大都是引用一个号称很强大的开源软件 NuSOAP(下载地址:http://sourceforge.net ...