您的位置:首页 > 运维架构 > Linux

自动创建设备文件

2016-05-04 22:12 302 查看
自动创建设备文件
设备文件是非常重要的文件,是应用程序与设备驱动交换数据,控制硬件的桥梁。在驱动程序中open、release的实现过程中其中的一个参数struct inode实质就是设备文件的索引,没有这个索引也就没有后期的各种操作,通常设备文件也被称为设备文件节点。因此没有设备文件后期的各种实现都是多余的。设备文件的创建有两种方法,其中就是在创建文件系统过程中用到的mknod命令。

该命令的形式如下:
mknod filename (type,c,b,l) 主设备号 次设备号
其中type说明是那一类设备(字符设备c,块设备b,套接字l),主设备号用来确定那一类设备,而次设备号主要用来确定这一类设备中的某一个设备。
例如:mknod memdev0  c 555 0 就是创建了一个主设备号为555,次设备号为0的字符设备。

这种方法比较快速,但是在编写设备驱动的时候很难确定那个设备号是可以使用的,因此很不方便开发。在2.4内核中引入了devfs,但是因为性能等方面的原因,在2.6内核中被udev逐渐取代。udev的设备命名策略、权限控制和事件处理都是在用户态下完成的,它利用sysfs中的信息来进行创建设备文件节点等工作。其实对于我们写程序而言并没有多大的区别,这是内核的设计者考虑的问题。两个都能够实现设备文件的动态创建,具体的实现方法也很类似。在嵌入式中是采用mdev实现类似udev的动态创建设备文件,在制作文件系统的过程中应该注意在linux
system项选上mdev,不过一般默认情况下都选择上。

在驱动中动态添加设备文件节点会减少麻烦。
具体的实现主要包括两个过程。
1、创建一个设备类,主要依据函数class_create()实现。
2、依据设备类创建一个设备文件,主要依据device_create()或者有些较低版本中的class_device_create()实现。

基本的实现过程应该是在设备驱动初始化过程中首先得到申请到设备号之后创建一个设备类,采用class_create()实现。

函数class_create()的形式如下:

#define class_create(owner, name)        \

({                        \

    static struct lock_class_key __key;    \

    __class_create(owner, name, &__key);    \

})

参数的意义:owner是指设备的拥有者,因此可以直接THIS_MODULE复制给owner,而name是设备类的名字。返回值是一个设备类的指针。这样就创建了一个设备类。

static struct class *myclass;

...

static int memdev_init(void)

{

  ...

 /*如果定义了主设备号采用静态申请的方式*/

        if(mem_major)

                result = register_chrdev_region(devno,2,"mem_dev");

        else/*动态申请设备号*/

        {

                result = alloc_chrdev_region(&devno,0,2,"mem_dev");

                mem_major = MAJOR(result);

        }

        /*错误处理*/

        if(result < 0)

                return result;

        /*在设备号申请完成以后可以为设备创建一个设备类,用于设备文件的创建*/

        myclass = class_create(THIS_MODULE,"memdev_class");

        /*创建一个设备*/

        /*初始化cdev,并将相关的文件操作添加进来*/

        cdev_init(&cdev,&mem_fops);

   ...

}

在设备初始化完成、绑定好文件操作、设备添加到内核中以后然后根据设备类要创建设备文件,依据device_create实现函数,其中的函数形式如下实现。

struct device *device_create(struct class *class, struct
device *parent,

             dev_t devt, void *drvdata, const char *fmt, ...)

{

    va_list vargs;

    struct device *dev;

    va_start(vargs, fmt);

    dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);

    va_end(vargs);

    return dev;

}

参数的意义分别是设备类指针、设备的父设备,设备号、以及设备的数据、然后是设备文件的名字,参数具有不定性。具体的实现如下:

...

       /*初始化cdev,并将相关的文件操作添加进来*/

        cdev_init(&cdev,&mem_fops);

        /*设备引用*/

        cdev.owner = THIS_MODULE;

        cdev.ops = &mem_fops;

        /*注册字符设备*/

        cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS);

        /*以上设备添加完成*/

        /*分配两个内存空间,此处是在物理内存上实现分配,实质是创建两个设备的描述*/

        mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct
mem_dev),GFP_KERNEL);

        if(!mem_devp)/*出错的相应操作*/

        {

                result = -ENOMEM;

                /*错误处理,采用典型的goto语句*/

                goto fail_malloc;

        }

        /*清除空间*/

        memset(mem_devp,0,sizeof(struct
mem_dev));

        for(i = 0; i < MEMDEV_NR_DEVS; ++i)

        {

                device_create(myclass,NULL,MKDEV(mem_major,i),NULL,"memdev%d",i);

                /*

                myclass为设备类

                NULL 表示父设备为空

                MKDEV(mem_major,i) 表示设备号

                NULL 表示设备数据为空

                后面的参数是用来设置 设备文件的名字

                */

                mem_devp[i].size = MEMDEV_SIZE;

                /*对设备的数据空间分配空间*/

                mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);

                /*问题,没有进行错误的控制*/

                memset(mem_devp[i].data,0,MEMDEV_SIZE);

                /*初始化定义的互信息量*/

                //mutex_init(&mem_devp[i].sem);

                //初始化定义的自旋锁ua

                spin_lock_init(&mem_devp[i].lock);

        }

...

以上的操作都是在模块初始化过程中完成的。这样在加载过程中就会在/dev目录下添加好设备文件。在设备退出过程中我们当然也要释放分配好的这些资源。具体的采用device_destroy释放分配好的设备文件,

void device_destroy(struct class *class, dev_t devt)

{

    struct device *dev;

    dev = class_find_device(class, NULL, &devt, __match_devt);

    if (dev) {

        put_device(dev);

        device_unregister(dev);

    }

}

参数主要是设备类和设备号。

同时也要释放设备类。主要采用函数class_destroy()

void class_destroy(struct class *cls)

{

    if ((cls == NULL) || (IS_ERR(cls)))

        return;

    class_unregister(cls);

}

参数是设备类。

设备的退出过程如下:

/*模块清除函数*/

static void memdev_exit(void)

{

        cdev_del(&cdev);/*注销字符设备*/

        /*释放两个物理内存*/

        kfree(mem_devp[0].data);

        kfree(mem_devp[1].data);

        device_destroy(myclass,MKDEV(mem_major,0));

        device_destroy(myclass,MKDEV(mem_major,1));

        kfree(mem_devp);/*释放设备结构体内存*/

        class_destroy(myclass);

        unregister_chrdev_region(MKDEV(mem_major,0),2);

}

基本的形式如上所示。

驱动的出错顺序与错误处理顺序应该是一个相反的过程,这样才能保证区域的包含关系。由于设备类的创建过程是在设备号申请的后面完成,因此释放应该在设备号释放之前注销掉。因此形成一个先进后处理的关系,类似于一个堆栈的形式。

测试过程:

[gong@Gong-Computer mem_waitqueue]$ ls -al /dev/mem*
crw-r----- 1 root kmem 1, 1 Dec  5 12:55 /dev/mem
[gong@Gong-Computer mem_waitqueue]$ sudo insmod memwait_queue.ko 
[sudo] password for gong: 
[gong@Gong-Computer mem_waitqueue]$ ls -al /dev/mem*
crw-r----- 1 root kmem   1, 1 Dec  5 12:55 /dev/mem
crw------- 1 root root 555, 0 Dec  5 16:50 /dev/memdev0
crw------- 1 root root 555, 1 Dec  5 16:50 /dev/memdev1
[gong@Gong-Computer mem_waitqueue]$ sudo rmmod memwait_queue
[gong@Gong-Computer mem_waitqueue]$ ls -al /dev/mem*
crw-r----- 1 root kmem 1, 1 Dec  5 12:55 /dev/mem

以上的结果表明,采用上面的方式能够自动的创建设备文件,相比手动创建更加的方便自如。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息