关于udev,sys,proc,tmpfs一些定义的问题

谈这些概念之前,首先不得不说下devfs。devfs(设备文件系统)时由Linux2.4内核引入的,它的出现可以使得程序在设备初始化时在/dev目录下创建设备文件,卸载时将它删除。虽然它在2.6内核版本后已被udev取代,这里还是简要列出它的范例,方便后面的分析。

static devfs_handle_t devfs_handle
static int __init xxx_init(void){
    int ret;
    /* 内核中注册设备 */
    ret = register_chrdev(XXX_MAJOR, DEVICE_NAME, &xxx_fops);
    if(ret < 0){...}
    /* 创建设备文件 */
    devfs_handle = devfs_register(NULL, DEVICE_NAME, DEVFS_FL_DEFAULT,
        XXX_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &xxx_fops, NULL);
    return 0;
}
static void __exit xxx_exit(void){
    devfs_unregister(devfs_handle);             /* 撤销设备文件 */
    unregister_chrdev(XXX_MAJOR, DEVICE_NAME);  /* 注销设备 */
}

devfs_register()和devfs_unregister()这些API已经被删除了,但register_chrdev()和unregister_chrdev()在Linux2.6以后的内核中仍被采用,在程序中可以直接给register_chrdev()传递0主设备号以获得可用的主设备号,这个功能还是很好用的,但是,却不被推荐使用,因为在udev中出现了新的申请设备号的方式。

udev中注册设备的方法如下。

/* 主设备号和从设备号 */
static int dev_major = 0;
static int dev_minor = 0;

/* 设备结构体定义 */
struct dev_struct{
    struct cdev cdev;       /* cdev 结构体 */
    int val;                /* 设备的一个属性,这里定义它为设备的一个寄存器 */
    struct semaphore sem;   /* 信号量,用于设备驱动并发控制 */
    ...                     /* 自定义的其它数据  */
};
dev_struct *struct_devp;    /* 设备结构体指针 */

static ini __init xxx_init(void){
    int err;
    dev_t devno = 0;

    /* 动态分配主设备号和从设备号 */
    err = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
    if(err < 0){ ... }
    dev_major = MAJOR(devno);

    /*  动态申请设备结构体的内存 */
    struct_devp = kmalloc(sizeof(struct dev_struct), GFP_KERNEL);
    if(!struct_devp){ ... }
    memset(struct_devp, 0, sizeof(struct dev_struct));

    /* 初始化设备 */
    devno = MKDEV(dev_major, dev_minor);
    cdev_init(&struct_devp->cdev, &xxx_fops);
    struct_devp->cdev.owner = THIS_MODULE;
    struct_devp->cdev.ops = &xxx_fops;
    err = cdev_add(&struct_devp->cdev, devno, 1);
    if(err){ ... }
    /* 初始化信号量和寄存器val的值 */
    init_MUTE(struct_devp->sem);
    struct_devp->val = 0;
}

这里仅仅是udev方式下利用cdev结构体注册设备的过程,也就是devfs下register_chrdev()所做的东西,可以看到,代码多了几行,其驱动使用的便利性也是要产生额外的步骤的。其中,udev下申请设备号有两种方法,动态申请设备号如上面利用alloc_chrdev_region(&devno, 0, 1, DEVICE_MODE_NAME)函数,如果已知主设备号,可以通过register_chrdev_region(devno, 1, DEVICE_MODE_NAME)函数来申请。

那么,在udev方式下怎么生成设备文件呢,这就回到udev的概念上了,udev是操作系统的一个守护进程,下面是它的说明。

udev是硬件平台无关的,属于user space的进程,它脱离驱动层的关联而建立在操作系统之上,基于这种设计实现,我们可以随时修改及删除/dev下的设备文件名称和指向,随心所欲地按照我们的愿望安排和管理设备文件系统,而完成如此灵活的功能只需要简单地修改udev的配置文件即可,无需重新启动操作系统。udev已经使得我们对设备的管理如探囊取物般轻松自如。

udev创建设备文件是通过读取内核从netlink套接字里发出的信息来工作,比如,当硬件设备加入或移除时,内核会发送热插拔事件。继续分析,就设计到udev的规则文件了,我们这里只说说概念就行了。在嵌入式系统中,也可以使用udev的轻量级版本mdev,mdev集成与busybox中。Android中采用vold,它和udev是一样的,同样是监听基于netlink的套接字,并解析收到的消息。

说了这么多,下面给出在程序中利用udev机制创建设备文件的方法。接着上面xxx_init()函数里的程序继续。

static struct class* dev_class = NULL;
struct device* dev_device = NULL;
/* 在/sys/class/目录下创建设备类别目录DEVICE_CLASS */
dev_class = class_create(THIS_MODULE, DEVICE_CLASS);
if(IS_ERR(dev_class)){ ... }
/* 在/dev/目录和/sys/class/DEVICE_CLASS目录下分别创建设备文件DEVICE_NAME */
dev_device = device_create(dev_class, NULL, devno, "%s", DEVICE_NAME);
if(IS_ERR(dev_device)){ ... }

其中class_create()函数创建了一个struct class结构体,作为device_create()函数的参数。device_create()函数创建一个设备并将其注册到sysfs中,同时在系统的sys/class和sys/device目录下会生成相应的类和设备入口。此外device_create()函数还会发出用户空间udev的动作,udev会根据sysfs下的class在/dev/目录下创建设备节点,这为自动创建设备节点提供了一种途径。通过该设备节点(/dev/DEVICE_NAME),可以像访问文件的方式来访问设备,其中文件的各个访问函数由初始化设备程序cdev_init(&struct_devp->cdev, &xxx_fops);中xxx_fops结构体来指定。

通过device_create函数,我们就可以不用mknod命令手动的创建设备节点了。由此看来,udev与sysfs息息相关,下面说说sys文件系统。sysfs文件系统能直观地反应加入计算机的总线、设备的信息(通过kobject对象实现)。但是,它仅仅是一种体现和反应,并不能如devfs一样,在生成设备文件时(不管设备在不在),就直接把设备驱动加载进来(其实,这是不必要的)。如果想智能地控制外部设备的管理,还需要一个任务,一个专业的工具。在devfs里,是由一个内核线程完成的。而udev正好相反,它通过等待内核注册过的设备热插拔引起的事件,激发相应的功能。

sysfs的作用与proc有些类似,但除了与proc相同的具有查看和设定内核参数功能之外,还有为Linux统一设备驱动模型作为管理之用。sysfs相比proc,设计上比较清晰。一个proc虚拟文件可能有内部格式,如/proc/scsi/scsi,它是可读可写的,并且读写格式不一样,代表不同的操作,应用程序读到了这个文件的内容一般还需要进行字符串解析,而在写入时需要先用字符串格式化按指定的格式写入字符串进行操作;相比而言,sysfs的设计原则是一个属性文件只做一件事情,sysfs属性文件一般只有一个值,直接读取或写入。真个/proc/scsi目录在2.6内核中已被标记为过时(LEGACY),它的功能已经被相应的/sys属性文件所完全取代。新设计的内核机制应该尽量使用sysfs机制,而将proc保留给纯净的“进程文件系统”。

下面对比下通过proc文件系统控制设备以及通过sysfs访问设备属性文件的方法。首先看在sysfs下创建设备属性文件。

/* 在/sys/class/DEVICE_CLASS/DEVICE_CLASS目录下创建属性文件val */
err = device_create_file(dev_device, &dev_attr_val);
if(err < 0){ ... }
dev_set_drvdata(dev_device, struct_devp);

/* 定义设备属性 */
static DEVICE_ATTR(val, S_IRUGO|S_IWUSR, dev_val_show, dev_val_store);
/* 访问设置属性方法 */
static ssize_t dev_val_show(struct device* dev, struct device_attribute* attr, char* buf);
static ssize_t dev_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);

/* 读取设备属性 */
static ssize_t dev_val_show(struct device* dev, struct device_attribute* attr, char* buf){
    struct dev_struct* hdev = (struct dev_struct*)dev_get_drvdata(dev);
    return __dev_get_val(hdev, buf);
}
/* 写设备属性 */
static ssize_t dev_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count){
    struct dev_struct* hdev = (struct dev_struct*)dev_get_drvdata(dev);
    return __dev_set_val(hdev, buf, count);
}
/* 读取寄存器val的值到缓冲区buf中,内部使用 */
static ssize_t __dev_get_val(struct dev_struct* dev, char* buf){
    int val = 0;
    /* 同步访问 */
    if(down_interruptible(&(dev->sem))){
        return -ERESTAETSYS;
    }
    val = dev->val;
    up(&(dev->sem));
    return snprintf(buf, PAGE_SIZE, "%d\n", val);
}
/* 把缓冲区buf的值写到设备寄存器val中去,内部使用 */
static ssize_t __dev_set_val(struct dev_struct* dev, const char* buf, size_t count){
    int val = 0;
    /* 将字符串转成数字 */
    val = simple_strtol(buf, NULL, 10);
    /* 同步访问 */
    if(down_interruptible(&(dev->sem))){
        return -ERESTAETSYS;
    }
    dev->val = val;
    up(&(dev->sem));
    return count;
}

在sysfs下创建设备属性文件很简单,也就两个函数,device_create_file(dev_device, &dev_attr_val)函数将dev_device这个设备与val属性绑定,val属性通过static DEVICE_ATTR(val, S_IRUGO|S_IWUSR, dev_val_show, dev_val_store);定义,其中S_IRUGO|S_IWUSR表示val属性文件可读可写,其读写函数分别为dev_val_show()dev_val_store()dev_set_drvdata(dev_device, struct_devp)函数将dev_device设备与struct_devp设备结构体指针绑定,后面读写函数中通过struct dev_struct* hdev = (struct dev_struct*)dev_get_drvdata(dev);方式将设备结构体指针取出,从而访问设备结构体中的属性。

接着看在/proc目录下创建设备属性节点的方法,程序如下。

/* 创建/proc/hello文件 */
struct proc_dir_entry* entry;
entry = create_proc_entry(DEVICE_PROC_NAME, 0, NULL);
if(entry){
    entry->read_proc = dev_proc_read;
    entry->write_proc = dev_proc_write;
}

/* 读取设备寄存器val的值,保存在page缓冲区中 */
static ssize_t dev_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data){
    if(off > 0){
        *eof = 1;
        return 0;
    }
    return __dev_get_val(struct_devp, page);
}
/* 把缓冲区的值buff保存到设备寄存器val中 */
static ssize_t dev_proc_write(struct file* filp, const char __user* buff, unsigned long len, void* data){
    int err = 0;
    char* page = NULL;
    if(len > PAGE_SIZE){
        printk(KERN_ALERT"The buff is too large:%lu.\n", len);
        return -EFAULT;
    }
    page = (char*)__get_free_page(GFP_KERNEL);
    if(!page){
        printk(KERN_ALERT"Failed to alloc page.\n");
        return -ENOMEM;
    }
    /* 先把用户提供的缓冲区值复制到内核缓冲区中 */
    if(copy_from_user(page, buff, len)){
        printk(KERN_ALERT"Failed to copy buff from user.\n");
        err = -EFAULT;
        goto out;
    }
    err = __dev_set_val(struct_devp, page, len);
out:
    free_page((unsigned long)page);
    return err;
}

可以看到,通过proc文件系统访问设备属性与通过sys文件系统访问设备属性极为类似,在proc文件系统中,通过entry = create_proc_entry(DEVICE_PROC_NAME, 0, NULL);创建/proc/DEVICE_PROC_NAME设备节点,通过entry->read_proc = dev_proc_read; entry->write_proc = dev_proc_write;来指定访问设备属性的方法。

相对而言,sys文件系统中将设备进行了分类管理,通过class_create(THIS_MODULE, DEVICE_CLASS);的方式将设备进行分类到/sys/class目录下,class目录包含系统中的设备类型(如网卡设备、声卡设备、输入设备等),/sys下还有block目录包含所有的块设备,devices目录包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构,bus目录包含系统中的所有总线类型。sysfs下将linux驱动的拓扑结构整理为总线、设备和类的关系,将设备、驱动挂在总线上,由总线来匹配设备和驱动,达到系统探测到设备,然后再加载驱动的效果。这一篇并没有用到总线,直接就将驱动加载上了,后面再单独一篇写linux的设备驱动模型。

时间: 2024-08-02 19:42:00

关于udev,sys,proc,tmpfs一些定义的问题的相关文章

udev

如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev目录下.后来,采用了devfs, 一个基于内核的动态设备文件系统,他首次出现在2.3.46内核中.Mandrake,Gentoo等Linux分发版本采用了这种方式.devfs创建 的设备文件是动态的.但是devfs有一些严重的限制,从2.6.13版本后移走了.目前取代他的便是文本要提到的udev--

使用 /proc 文件系统来访问 linux操作系统 内核的内容 &amp;&amp; 虚拟文件系统vfs及proc详解

http://blog.163.com/he_junwei/blog/static/19793764620152743325659/ http://www.01yun.com/other/20130422/366044.html 使用 /proc 文件系统来访问 Linux 内核的内容 这个虚拟文件系统在内核空间和用户空间之间打开了一个通信窗口 简介: /proc 文件系统是一个虚拟文件系统,通过它可以使用一种新的方法在 Linux? 内核空间和用户空间之间进行通信.在 /proc 文件系统中,

/etc/udev

本文转自:无双小宝    查看原文 如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev目录下.后来,采用了devfs, 一个基于内核的动态设备文件系统,他首次出现在2.3.46内核中.Mandrake,Gentoo等Linux分发版本采用了这种方式.devfs创建 的设备文件是动态的.但是devfs有一些严重的限制,从2.6.13版本后移走了.目前

详解udev

如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev目录下.后来,采用了devfs, 一个基于内核的动态设备文件系统,他首次出现在2.3.46内核中.Mandrake,Gentoo等Linux分发版本采用了这种方式.devfs创建 的设备文件是动态的.但是devfs有一些严重的限制,从2.6.13版本后移走了.目前取代他的便是文本要提到的udev--

Linux┊详解udev

是一小部分例子,如果你的系统采用了udev方式,那你应该可以看到更多的规则.如果你想修改设备的权限或者创建信的符号连接,那么你需要熟读这些规则,特别是要仔细注意你修改的那些与之相关的设备. 修改你的udev配置 在修改udev配置之前,我们一定要仔细,通常的考虑是:你最好不要修改系统预置的那些规则,特别不要指定影响非常广泛的配置,比如上面例子中的第一行.不正确的配置可能会导致严重的系统问题或者系统根本就无法这个正确的访问设备. 而我们正确的做法应该是在/etc/udev/rules.d/下创建一

辛星浅析Linux中的devfs、sysfs和udev

Linux下有专门的文件系统用来对设备进行管理,devfs和sysfs就是其中的两种.在2.6内核之前使用的是devfs,而devfs挂载于/dev目录下,提供了一种类似于文件的方法来管理位于/dev目录下的所有设备,我们知道/dev目录下的每一个文件对应的都是一个设备,而且这些特殊文件是位于根文件系统上的,在制作文件系统的时候我们就已经建立了这些设备文件,因此通过操作这些特殊文件,可以实现与内核进行交互. 但是devfs文件系统有一些缺点:(1)比如不确定的设备映射,有时候一个设备映射的设备文

通过PROC信息调节TCP窗口

1, /sys/kernel/ipv4/tcp_rmem_xxx 这种方式验证过,把值得设置得很大的时候TCP速度有质的提升 2, /proc/sys/net/core/rmem_default 定义默认的接收窗口大小/proc/sys/net/core/rmem_max 定义接收窗口的最大大小/proc/sys/net/core/wmem_default 定义默认的发送窗口大小/proc/sys/net/core/wmem_max 定义发送窗口的最大大小/proc/sys/net/ipv4/t

关于FHS定义的一些存放的路径

FHS,就是文件系统成级标准.为什么么需要这个FHS?因为我们都知道Linux是开源的软件,由一些公司比如RedHat通过包装发行一些版本供用户使用.那不同的公司如果包装发行的版本没有一个统一的标准的话岂不是要了用户的命?当用户会使用这个公司的Linux发行版但是另外一个公司的发行版因为没有一个统一的标准,又要用户重新来学习这个发行版,多麻烦费事. /bin:其实是binary的缩写,里面存放的是一些二进制的程序,一些可执行的程序. /sbin:其实跟上面那个差不多,这个路径下存放的也是一些可执

linux下udev

如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev目录下.后来,采用了devfs,一个基于内核的动态设备文件系统,他首次出现在2.3.46 内核中.Mandrake,Gentoo等Linux分发版本采用了这种方式.devfs创建的设备文件是动态的.但是devfs有一些严重的限制,从 2.6.13版本后移走了.目前取代他的便是文本要提到的udev--