用户空间驱动

一个第一次涉及内核问题的 Unix 程序猿, 可能会紧张写一个模块. 编写一个用户程序来

直接读写设备port可能easy些.

确实, 有几个论据倾向于用户空间编程, 有时编写一个所谓的用户空间设备驱动对照钻研

内核是一个明智的选择. 在本节, 我们讨论几个理由, 为什么你可能在用户空间编写驱动.

本书是关于内核空间驱动的, 可是, 所以我们不超越这个介绍性的讨论.

用户空间驱动的优点在于:

? 完整的 C 库能够连接. 驱动能够进行很多奇怪的任务, 不用依靠外面的程序(实现

使用策略的工具程序, 经常随着驱动自身公布).

? 程序猿能够在驱动代码上执行经常使用的调试器, 而不必走调试一个执行中的内核的弯

路.

? 假设一个用户空间驱动挂起了, 你可简单地杀掉它. 驱动的问题不可能挂起整个系

统, 除非被控制的硬件真的疯掉了.

? 用户内存是可交换的, 不象内核内存. 一个不常使用的却有非常大一个驱动的设备不

会占领别的程序能够用到的 RAM, 除了在它实际在用时.

? 一个精心设计的驱动程序仍然能够, 如同内核空间驱动, 同意对设备的并行存取.

? 假设你必须编写一个封闭源代码的驱动, 用户空间的选项使你easy避免不明朗的许可

的情况和改变的内核接口带来的问题.

比如, USB 驱动能够在用户空间编写; 看(仍然年幼) libusb 项目, 在

libusb.sourceforge.net 和 “gadgetfs” 在内核源代码里. 还有一个样例是 X server: 它确

切地知道它能处理哪些硬件, 哪些不能, 并且它提供图形资源给全部的 X 客户. 注意, 然

而, 有一个缓慢可是固定的漂移向着基于 frame-buffer 的图形环境, X server仅仅是作为

一个server, 基于一个内核空间的真实的设备驱动, 这个驱动负责真正的图形操作.

经常, 用户空间驱动的编写者完毕一个server进程, 从内核接管作为单个代理的负责硬件

控制的任务. 客户应用程序就能够连接到server来进行实际的操作; 因此, 一个聪明的驱

动经常能够同意对设备的并行存取. 这就是 X server怎样工作的.

可是用户空间的设备驱动的方法有几个缺点. 最重要的是:

? 中断在用户空间无法用. 在某些平台上有对这个限制的解决方法, 比如在 IA32 体

系上的 vm86 系统调用.

? 仅仅可能通过内存映射 /dev/mem 来使用 DMA, 并且仅仅有特权用户能够这样做.

? 存取 I/O port仅仅能在调用 ioperm 或者 iopl 之后. 此外, 不是全部的平台支持这

些系统调用, 而存取/dev/port 可能太慢而无效率. 这些系统调用和设备文件都要

求特权用户.

? 响应时间慢, 由于须要上下文切换在客户和硬件之间传递信息或动作.

? 更不好的是, 假设驱动已被交换到硬盘, 响应时间会长到不可接受. 使用 mlock 系

统调用可能会有帮助, 可是经常的你将须要锁住很多内存页, 由于一个用户空间程

序依赖大量的库代码. mlock, 也, 限制在授权用户上.

? 最重要的设备不能在用户空间处理, 包含但不限于, 网络接口和块设备.

如你所见, 用户空间驱动不能做的事情毕竟太多. 感兴趣的应用程序还是存在: 比如, 对

SCSI 扫描器设备的支持( 由 SANE 包实现 )和 CD 刻录器 ( 由 cdrecord 和别的工具实

现 ). 在两种情况下, 用户级别的设备情况依赖 “SCSI gneric” 内核驱动, 它输出了低层

的 SCSI 功能给用户程序, 因此它们能够驱动它们自己的硬件.

一种在用户空间工作的情况可能是有意义的, 当你開始处理新的没实用过的硬件时. 这样

你能够学习去管理你的硬件, 不必操心挂起整个系统. 一旦你完毕了, 在一个内核模块中

封装软件就会是一个简单操作了.

用户空间驱动的优缺点:

用户空间驱动的优点:

1 完整的 C 库能够连接. 驱动能够进行很多奇怪的任务, 不用依靠外面的程序(实现使用策略的工具程序, 经常随着驱动自身公布);

2 程序猿能够在驱动代码上执行经常使用的调试器, 而不必走调试一个执行中的内核的弯路;

3 假设一个用户空间驱动挂起了, 你可简单地杀掉它. 驱动的问题不可能挂起整个系统, 除非被控制的硬件真的疯掉了。

4 用户内存是可交换的, 不象内核内存. 一个不常使用的却有非常大一个驱动的设备不会占领别的程序能够用到的 RAM, 除了在它实际在用时;

5 一个精心设计的驱动程序仍然能够, 如同内核空间驱动, 同意对设备的并行存取;

6 假设你必须编写一个封闭源代码的驱动, 用户空间的选项使你easy避免不明朗的许可的情况和改变的内核接口带来的问题;

7 一种在用户空间工作的情况可能是有意义的, 当你開始处理新的没实用过的硬件时. 这样你能够学习去管理你的硬件, 不必操心挂起整个系统. 一旦你完毕了, 在一个内核模块中封装软件就会是一个简单操作了。

用户空间驱动的缺点:

1 中断在用户空间无法用. 在某些平台上有对这个限制的解决方法, 比如在 IA32体系上的 vm86 系统调用;

2 仅仅可能通过内存映射 /dev/mem 来使用 DMA, 并且仅仅有特权用户能够这样做;

3 存取 I/O port仅仅能在调用 ioperm 或者 iopl 之后. 此外,不是全部的平台支持这些系统调用, 而存取/dev/port可能太慢而无效率. 这些系统调用和设备文件都要求特权用户;

4 响应时间慢, 由于须要上下文切换在客户和硬件之间传递信息或动作。

5 更不好的是。假设驱动已被交换到硬盘, 响应时间会长到不可接受。 使用 mlock 系统调用可能会有帮助。可是经常的你将须要锁住很多内存页, 由于一个用户空间程序依赖大量的库代码. mlock, 也, 限制在授权用户上。最重要的设备不能在用户空间处理, 包含但不限于, 网络接口和块设备。

以下来看在前面的基础上加上中断的情况。由于用户空间不能操作中断,所以仅仅能使用内核驱动,以下贴出代码,详细解析我在《GPIO中断程序》一文中有讲述,此处不再赘述。


    /*
     * PB18_IRQTest.c
     * This is  a test program for sam9260, using PB19(J5_18 pin) input a signal to PB18(J5_16 pin),
     * PB18 receive this signal as IRQ and make the LED linking on PB17((J5_14 pin)) turn on or turn off
     *
     *           @Author: Cun Tian Rui
     *           @Date :March.18.2011
     */  

    #include <linux/types.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/platform_device.h>
    #include <linux/cdev.h>
    #include <linux/ioctl.h>
    #include <linux/fs.h>
    #include <linux/gpio.h>
    #include <asm/arch/hardware.h>
    #include <asm/arch/gpio.h>
    #include <linux/interrupt.h>
    #include <asm/io.h>
    #include <asm/arch/board.h>
    #include <linux/cdev.h>
    #include <asm/arch/gpio.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
    #include <asm/arch/at91_pio.h>
    #include <asm/arch/at91_aic.h>
    #include <asm/arch/at91_pmc.h>   

    void led_on()
    {
         at91_set_gpio_output(AT91_PIN_PB17,1);
    }   

    void led_off()
    {
         at91_set_gpio_output(AT91_PIN_PB17 ,0);
    }   

    struct light_dev *light_devp;
    int light_major = 200;   

    struct light_dev
    {
        struct cdev cdev;
        unsigned char value;
    };   

    MODULE_AUTHOR("Cun Tian Rui");
    MODULE_LICENSE("Dual BSD/GPL");   

    static void io_init(void)
    {
        at91_set_gpio_input(AT91_PIN_PB18, 1);
        at91_set_deglitch(AT91_PIN_PB18, 1);
        at91_sys_write(1 + PIO_IDR,  1<<18);
        at91_sys_write(1 + PIO_IER,  (~(1<<18)));
        at91_sys_write(AT91_PMC_PCER, 1 << 3);
    }   

    struct gpio_irq_desc
    {
        int irq;
        unsigned long flags;
        char *name;
    };   

    static struct gpio_irq_desc PB18_IRQ={AT91_PIN_PB18,AT91_AIC_SRCTYPE_LOW,"PB18"};   

    static irqreturn_t PB18_intHandle(int irq, void *dev_id)
    {
        led_on();
        return IRQ_RETVAL(IRQ_HANDLED);
    }   

    int light_open(struct inode *inode,struct file *filp)
    {
        int err;
        struct light_dev *dev;
        dev = container_of(inode->i_cdev,struct light_dev,cdev);
        filp->private_data = dev;
        io_init();
        err = request_irq(PB18_IRQ.irq,PB18_intHandle,PB18_IRQ.flags,PB18_IRQ.name,(void*)0);
        if(err)
            {
            free_irq(PB18_IRQ.irq,(void*)0);
            return  -EBUSY;
        }   

        return 0;
    }   

    int light_release(struct inode *inode,struct file *filp)
    {
        free_irq(PB18_IRQ.irq,(void*)0);
        return 0;
    }   

    // ioctl
    int light_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,
    unsigned long arg)
    {
        struct light_dev *dev = filp->private_data;   

        switch(cmd)
        {
            case 0:
                at91_set_gpio_output(AT91_PIN_PB19,0);
            break;   

            case 1:
              at91_set_gpio_output(AT91_PIN_PB19,1);
           led_off();
            break;   

            default:   

                return -ENOTTY;
            // break;
        }   

        return 0;
    }        

    struct file_operations light_fops =
    {
        .owner = THIS_MODULE,
        .ioctl = light_ioctl,
        .open  = light_open,
        .release = light_release,
    };   

    static void light_setup_cdev(struct light_dev *dev,int index)
    {
        int err,devno = MKDEV(light_major,index);   

        cdev_init(&dev->cdev,&light_fops);
        dev->cdev.owner = THIS_MODULE;
        dev->cdev.ops = &light_fops;   

        err = cdev_add(&dev->cdev,devno,1);   

        if(err)
        {
            printk(KERN_NOTICE "Error %d adding LED%d",err,index);
        }
    }   

    int light_init(void)
    {
        int result;   

        dev_t dev = MKDEV(light_major,0);
        if(light_major)
        {   

            result = register_chrdev_region(dev,1,"PB18_IRQTest");
        }   

        if(result < 0)
        {
            return result;
        }   

        light_devp = kmalloc(sizeof(struct light_dev),GFP_KERNEL);
        if(!light_devp)
        {
            result = - ENOMEM;
            goto fail_malloc;
        }   

        memset(light_devp,0,sizeof(struct light_dev));
        light_setup_cdev(light_devp,0);   

        return 0;   

        fail_malloc:unregister_chrdev_region(dev,light_devp);
        return result;   

    }   

    void light_cleanup(void)
    {
        cdev_del(&light_devp->cdev);
        kfree(light_devp);
        unregister_chrdev_region(MKDEV(light_major,0),1);
    }   

    module_init(light_init);
    module_exit(light_cleanup);  

最后给出自己写的一个在用户层读写AT91sam9260SDRAM某一个位置的小程序结束本文。


    #include <stdio.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>   

    #define SDRAM_PHY_Position 0x20800000
    int main (int args, char* arg[])
    {
    int i;
    int fd;
    char* mem;
    char *buff = "HELLO";
    //open /dev/mem with read and write mode
    if ((fd = open ("/dev/mem", O_RDWR)) < 0)
    {
           perror ("open error");
           return -1;
    }
    //map physical memory 0-10 bytes
    mem = mmap (0, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, SDRAM_PHY_Position);
    if (mem == MAP_FAILED) {
    perror ("mmap error:");
    return 1;
    }
    //Read old value
    for (i = 0; i < 5; i++)
    {
           printf("\nold mem[%d]:%d", i, mem[i]);
    }
    //write memory
    memcpy(mem, buff, 5);
    //Read new value
    for (i = 0; i<5 ; i++)
    {
           printf("\nnew mem[%d]:%c", i, mem[i]);
    }
    printf("\n");
    munmap (mem, 10); //destroy map memory
       close (fd);   //close file
       return 0;
    }  
时间: 2024-08-05 17:54:32

用户空间驱动的相关文章

Linux usb子系统(三):通过usbfs操作设备的用户空间驱动

内核中提供了USB设备文件系统(usbdevfs,Linux 2.6改为usbfs,即USB文件系统),它和/proc类似,都是动态产生的.通过在/etc/fstab文件中添加如下一行:none /proc/bus/usb usbfs defaults或者输入命令:mount -t usbfs none /proc/bus/usb可以实现USB设备文件系统的挂载. 一个典型的/proc/bus/usb/devices文件的结构如下(运行的arm Linux 2.6.37内核上的机器上插入了一个u

【转】用户空间编写驱动程序

设备驱动程序大概可分为两种: 内核驱动程序及用户空间驱动程序. l  内核驱动程序 是内核空间实现的驱动程序,它使用内核资源,内核栈.它包括可加载的内核驱动模块.在这里我想主要说说用户空间驱动程序的编写. l  用户空间驱动程序 就是指在用户空间实现的驱动程序.可以认为,它跟普通的用户程序没有什么两样,它使用用户进程空间和栈.这里说下,我不是讨论接到串口,并口上什么设备! 大家不要以为在用户空间写驱动程序“不过瘾!”.其实,作为设备驱动程序,其主要做的事就是配置设备寄存器(一家之言).所以也不用

Linux 设备驱动之 UIO 用户态驱动优缺点分析

[摘要]linux用户态的设备驱动开发:并不是所有的设备驱动程序都要在内核编写,有些情况下,在用户空间编写驱动程序能够更好地解决遇到的问题.本文对用户态驱动优缺点进行分析. 1.用户空间驱动程序的优点 1.可以和整个C库链接. 2.在驱动中可以使用浮点数,在某些特殊的硬件中,可能需要使用浮点数,而linux内核并不提供浮点数的支持.如果能在用户态实现驱动,就可以轻松解决这一问题. 3.驱动问题不会导致整个系统挂起.内核态驱动的一些错误常常导致整个系统挂起. 4.用户态的驱动调试方便. 5.可以给

内核空间与用户空间的通信方式

内核空间与用户空间的通信方式 下面总结了7种方式,主要对以前不是很熟悉的方式做了编程实现,以便加深印象. 1.使用API:这是最常使用的一种方式了 A.get_user(x,ptr):在内核中被调用,获取用户空间指定地址的数值并保存到内核变量x中. B.put_user(x,ptr):在内核中被调用,将内核空间的变量x的数值保存到到用户空间指定地址处. C.Copy_from_user()/copy_to_user():主要应用于设备驱动读写函数中,通过系统调用触发. 2.使用proc文件系统:

Linux内核工程导论——用户空间设备管理

用户空间设备管理 用户空间所能见到的所有设备都放在/dev目录下(当然,只是一个目录,是可以变化的),文件系统所在的分区被当成一个单独的设备也放在该目录下.以前的2.4版本的曾经出现过devfs,这个思路非常好,在内核态实现对磁盘设备的动态管理.可以做到当用户访问一个设备的设备的时候,devfs驱动才会去加载该设备的驱动.甚至每个节点的设备号都是动态获得的.但是该机制的作者不再维护他的代码,linux成员经过讨论,使用用户态的udev代替内核态的devfs,所以现在的devfs已经废弃了.用户态

如何看待Linux操作系统的用户空间和内核空间

作为中央核心处理单元的CPU,除了生产工艺的不断革新进步外,在处理数据和响应速度方面也需要有权衡.稍有微机原理基础的人都知道Intel X86体系的CPU提供了四种特权模式ring0~ring3,其中ring0特权最高,ring3的特权最低,之所以要做这样的区分一个主要目的是保护资源,通俗来讲要保护的资源无非就是"内存.I/O端口以及执行特殊机器指令的能力".任何一个时刻,x86 CPU都是在一定的特权模式下运行.同样,对于ARM体系的CPU 一共有七种运行模式,分别是:用户模式(us

CMA连续物理内存用户空间映射---(一)

背景: 在多媒体和图像处理等应用中,经经常使用到大块内存,尤其是硬件编解码.须要内核分配大块的物理连续内存. 这里希望通过把从内核分配的连续物理内存映射到用户空间.在用户空间经过处理,又能够入队到驱动中. 前提: Kernel Config中 依据需求配置和调整CMA的大小. 方法: (一) 1.驱动注冊misc设备. 2.驱动实现IOCTL的内存分配,使用dma_alloc_writecombine从CMA中拿出一个内存. 3.驱动实现mmap,通过remap_pfn_range,把上面第二步

用户空间和内核空间通讯之【proc文件系统】

今天我们介绍另一种用户内核空间通信的方法:proc文件系统. proc文件系统作为linux提供的一种虚拟文件系统并不占用实际外围存储空间,它仅存在于内存中,系统断电即消失.proc文件系统最开始的设计主要是为满足内核向用户态进程报告其状态而设计,并没有为输入做规定和说明.随着发展,现在的proc文件系统已经演变成一个"用户-内核"空间半双工的通信方式了(虽然目前已经开始有点混乱了,但某些早期开发的软件代码中还在继续使用这个文件系统).用户不但可以从proc文件系统中读取内核的相关状态

linux下热插拔事件的产生是怎样通知到用户空间,kobject_uevent_env之uevent【转】

转自:http://blog.csdn.net/myarrow/article/details/8259888 1.kobject, ktype, kset 1) kobject: 代表sysfs中的目录. 2) ktype: 代表kobject的类型,主要包含release函数和attr的读写函数.比如,所有的bus都有同一个bus_type:所有的class都有同一个class_type. 3) kset: 包含了subsystem概念,kset本身也是一个kobject,所以里面包含了一个