zoukankan      html  css  js  c++  java
  • 字符设备驱动三

    七、 字符设备驱动程序之poll机制(第九课)

    对于系统的调用,基本都可以在它的名字前加上 "sys_" 前缀,这就是内核中对应的函数。比如系统调用 open、read、write、poll,与之对应的内核函数为:sys_open、sys_read、sys_write、sys_poll

    sys_poll 函数位于 fs/select.c 文件中


    poll_initwait 函数的作用是初始化一个 poll_wqueues* 类型的变量 table




    __pollwait 这个函数把我们当前进程挂入我们驱动程序里定义的一个队列里

    接着分析 (do_poll) 函数


    break的条件:1、count非零(驱动程序中的 ev_press 变量为1时,也就是中断发生,驱动程序的函数返回非0值);2、超时;3、有信号等待处理

    否则进入休眠

    poll机制驱动程序(forth_chrdev.c)

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    
    #include <linux/irq.h>
    #include <linux/poll.h>
    
    #include <asm/uaccess.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    /*
     * eint 0  GPF0
     * eint 2  GPF2
     * eint 11 GPG3
     * eint 19 GPG11
     */
    static struct class *forth_chrdev_class;
    static struct class_device *forth_chrdev_class_dev;
    
    static volatile unsigned char key_val;
    
    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
    
    /* 中断事件标志, 中断服务程序将它置1,s3c24xx_buttons_read将它清0 */
    static volatile int ev_press = 0;
    
    struct pin_desc{
            unsigned int pin;
            unsigned int key_val;
        };
    struct pin_desc pins_desc[4] = {
            {S3C2410_GPF0,  0x01},
            {S3C2410_GPF2,  0x02},
            {S3C2410_GPG3,  0x03},
            {S3C2410_GPG11, 0x04},
        };
    
    static irqreturn_t button_irq(int irq, void *dev_id)
    {
        struct pin_desc *pindesc = (struct pin_desc *)dev_id;
        unsigned char pinval;
        printk("irq = %d
    
    ", irq);
        pinval = s3c2410_gpio_getpin(pindesc->pin);
        if(pinval)
        {
            /* 松开 */
            key_val = pindesc->key_val;
        }
        else
        {
            /* 按下 */
            key_val = 0x80|pindesc->key_val;
        }
           wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
            ev_press = 1;
        return IRQ_HANDLED;
    }
    
    
    static int forth_chrdev_open(struct inode *inode, struct file *file)
    {
        printk("forth_chrdev_open
    
    ");
        request_irq(IRQ_EINT0,   button_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
        request_irq(IRQ_EINT2,   button_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
        request_irq(IRQ_EINT11, button_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
        request_irq(IRQ_EINT19, button_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
    
        return 0;
    }
    
    
    static ssize_t forth_chrdev_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
    {
        if (count != 1)
            return -EINVAL;
    
        /* 如果没有按键动作, 休眠 */
        wait_event_interruptible(button_waitq, ev_press);
    
        copy_to_user(buf, &key_val, 1);
        ev_press = 0;
    
        return 1;
    }
    
    int forth_chrdev_release(struct inode *inode, struct file *file)
    {
        printk("forth_chrdev_release
    
    ");
        free_irq(IRQ_EINT0,   &pins_desc[0]);
        free_irq(IRQ_EINT2,   &pins_desc[1]);
        free_irq(IRQ_EINT11, &pins_desc[2]);
        free_irq(IRQ_EINT19, &pins_desc[3]);
    
        return 0;
    }
    
    static unsigned int forth_chrdev_poll(struct file *file, poll_table *wait)
    {
        unsigned int mask = 0;
        //不会立即休眠,只是把当前进程挂接进去,也就是执行指针指向后的__pollwait函数。
        poll_wait(file, &button_waitq, wait);
    
        if (ev_press)
            mask |= (POLLIN | POLLRDNORM);
    
        return mask;
    }
    
    
    static struct file_operations forth_chrdev_fops = {
        .owner    =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
        .open     =  forth_chrdev_open,
        .read     =  forth_chrdev_read,
        .release  =  forth_chrdev_release,
        .poll      =  forth_chrdev_poll,    //这个.poll所指向的函数最终是在(do_pollfd)函数中的(file->f_op->pll)函数中被调用
        };
    
    
    int major;
    static int forth_chrdev_init(void)
    {
        major = register_chrdev(0, "forth_chrdev", &forth_chrdev_fops);  //注册
        forth_chrdev_class = class_create(THIS_MODULE, "forthchrdev");
        forth_chrdev_class_dev = class_device_create(forth_chrdev_class, NULL, MKDEV(major, 0), NULL, "forth_chrdev");
    
        return 0;
    }
    
    static void forth_chrdev_exit(void)
    {
        unregister_chrdev(major, "forth_chrdev");                    //卸载
        class_device_unregister(forth_chrdev_class_dev);
        class_destroy(forth_chrdev_class);
    }
    
    module_init(forth_chrdev_init);
    module_exit(forth_chrdev_exit);
    
    MODULE_LICENSE("GPL");
    

    poll机制测试程序(forth_chrdev_test.c)

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    #include <poll.h>
    
    int main(int argc, char **argv)
    {
        int fd,ret;
        volatile unsigned char key_val;
    
        struct pollfd fds[1];
    
        fd = open("/dev/forth_chrdev",O_RDWR);
        if(fd < 0)
        printf("can't open!
    ");
        fds[0].fd       = fd;
        fds[0].events = POLLIN;    //POLLIN There is data to read.
    
        while(1)
        {
            //poll可以多个文件查询,此处只查询一个文件
            ret = poll(fds, 1, 5000);    //poll的返回条件有2:超时时间到;中断发生
            if(ret == 0)    //超时时间到则返回0
            {
                printf("timeout
    
    ");
            }
            else
            {
                read(fd,&key_val,1);
                printf("key_val = 0x%x
    
    ", key_val);
                printf("
    
    ");
            }
        }
        return 0;
    }
    
    

    测试:

    1. 若五秒没有中断发生,则打印(timeout)字符串。
    2. 若任意时刻中断发生,则立刻响应中断

    总结poll机制:

    1. poll>sys_poll>do_sys_poll>poll_initwait,poll_initwait 函数注册了函数 __pollwait,它就是我们驱动程序执行poll_wait时,真正被调用的函数。
    2. 接下来执行 file->f_op->poll,也就是我们驱动程序里自己实现的poll函数,它会调用 poll_wait函数,把自己挂入某个队列里,这个队列(button_waitq)是我们的驱动自己定义的,在挂入之前还会判断一下设备是否就绪。
    3. 进程被唤醒的条件有:一、超时时间到,二、被驱动程序唤醒。
    4. 如果进程被唤醒则返回,返回值为0表示超时时间到,返回非0则表示被驱动程序唤醒。

    八、 字符设备驱动程序之异步通知(第十课)

    获取按键值有三种方式:1.查询(耗资源);2.中断(虽然会休眠,但是如果一直没有按键按下的话会一直等待,永远不会返回);3.poll机制(指定超时时间返回)
    这三种方法有共同点:都是应用程序主动的读
    那么有没有一种方法,当有按键按下了会有驱动程序来提醒应用程序来读呢?
    答:使用异步通知(用signal来实现),进程之间通过命令来发信号

        kill -5 pid:向pid发送5这个信号
    

    sigal.c

    测试:

    1. 后台运行程序并查看pid
    2. 向应用程序的pid进程发送信号值(USR1=10)


      向应用程序的pid进程发送这个信号值就可以执行我们自己实现的函数

    信号要点:

    1. 应用程序注册信号处理函数
    2. 谁发?(驱动函数发)
    3. 发给谁?(发给应用程序,所以应用程序需要告诉驱动程序pid)
    4. 怎么发?(驱动程序的中断处理函数里会调用 kill_fasync 函数)

    目标:按下按键时,驱动程序通知应用程序

    首先搜索一下(kill_fasync),看看别人是怎么用的


    查看一下这个结构是怎么定义的

    这个结构被定义出来了,但是在什么地方被初始化的呢?由于这个结构被定义的 static 关键字,所以只在本文件能被使用,在本文件中搜索一下这个(rtc_async_queue)结构

    同样这个(rtc_fasync)函数也被限制该文件内使用,搜索一下这个函数在什么地方被调用


    1. 应用程序调用F_SETOWN命令之后就会把应用程序的pid告诉驱动程序,这个命令的处理是由内核帮我们做的。
    2. F_SETFL命令是指每当flag被改变后,驱动程序中的fasync函数就会被调用。

    1. 应用程序会调用这个函数来告诉驱动程序我自己的pid
    2. 应用程序去读出flag
    3. 修改flag,加上fasync

    异步通知程序(fifth_chrdev.c)

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    
    #include <linux/irq.h>
    #include <linux/poll.h>
    
    #include <asm/uaccess.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    /*
     * eint 0  GPF0
     * eint 2  GPF2
     * eint 11 GPG3
     * eint 19 GPG11
     */
    static struct class *fifth_chrdev_class;
    static struct class_device *fifth_chrdev_class_dev;
    
    static volatile unsigned char key_val;
    
    static struct fasync_struct *button_async;
    
    struct pin_desc{
            unsigned int pin;
            unsigned int key_val;
        };
    struct pin_desc pins_desc[4] = {
            {S3C2410_GPF0,  0x01},
            {S3C2410_GPF2,  0x02},
            {S3C2410_GPG3,  0x03},
            {S3C2410_GPG11, 0x04},
        };
    
    static irqreturn_t button_irq(int irq, void *dev_id)
    {
        struct pin_desc *pindesc = (struct pin_desc *)dev_id;
        unsigned char pinval;
        printk("irq = %d
    
    ", irq);
        pinval = s3c2410_gpio_getpin(pindesc->pin);
        if(pinval)
        {
            /* 松开 */
            key_val = pindesc->key_val;
        }
        else
        {
            /* 按下 */
            key_val = 0x80|pindesc->key_val;
        }
            //button_async这个结构中包含应用程序的pid号
            kill_fasync (&button_async, SIGIO, POLL_IN);
    
        return IRQ_HANDLED;
    }
    
    
    static int fifth_chrdev_open(struct inode *inode, struct file *file)
    {
        printk("fifth_chrdev_open
    
    ");
        request_irq(IRQ_EINT0,   button_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
        request_irq(IRQ_EINT2,   button_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
        request_irq(IRQ_EINT11, button_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
        request_irq(IRQ_EINT19, button_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
    
        return 0;
    }
    
    
    static ssize_t fifth_chrdev_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
    {
        if (count != 1)
            return -EINVAL;
    
        copy_to_user(buf, &key_val, 1);
    
        return 1;
    }
    
    int fifth_chrdev_release(struct inode *inode, struct file *file)
    {
        printk("fifth_chrdev_release
    
    ");
        free_irq(IRQ_EINT0,   &pins_desc[0]);
        free_irq(IRQ_EINT2,   &pins_desc[1]);
        free_irq(IRQ_EINT11, &pins_desc[2]);
        free_irq(IRQ_EINT19, &pins_desc[3]);
    
        return 0;
    }
    
    //当应用程序调用这个接口“fcntl(fd, SETFL, oflags | FASYNC)”时,函数“fasync_helper”就会被调用
    static int fifth_chrdev_fasync(int fd, struct file *filp, int on)
    {
        printk("fifth_chrdev_fasync
    
    ");
        //这个函数获取应用程序的pid并放入(button_async)这个结构
        return fasync_helper(fd, filp, on, &button_async);
    }
    
    static struct file_operations fifth_chrdev_fops = {
        .owner    =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
        .open     =  fifth_chrdev_open,
        .read     =  fifth_chrdev_read,
        .release  =  fifth_chrdev_release,
        .fasync   =  fifth_chrdev_fasync,
        };
    
    
    int major;
    static int fifth_chrdev_init(void)
    {
        major = register_chrdev(0, "fifth_chrdev", &fifth_chrdev_fops);  //注册
        fifth_chrdev_class = class_create(THIS_MODULE, "fifthchrdev");
        fifth_chrdev_class_dev = class_device_create(fifth_chrdev_class, NULL, MKDEV(major, 0), NULL, "fifth_chrdev");
    
        return 0;
    }
    
    static void fifth_chrdev_exit(void)
    {
        unregister_chrdev(major, "fifth_chrdev");                    //卸载
        class_device_unregister(fifth_chrdev_class_dev);
        class_destroy(fifth_chrdev_class);
    }
    
    module_init(fifth_chrdev_init);
    module_exit(fifth_chrdev_exit);
    
    MODULE_LICENSE("GPL");
    

    异步通知测试程序(fifth_chrdev_test.c)

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    #include <signal.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    static int fd;
    void my_signal_fun(int signum)
    {
        unsigned char key_val;
        read(fd, &key_val, 1);
        printf("key_val = 0x%x
    ", key_val);
    }
    
    
    int main(int argc, char **argv)
    {
        volatile unsigned char key_val;
        signal(SIGIO, my_signal_fun);
        int Oflags;
        fd = open("/dev/fifth_chrdev",O_RDWR);
        if(fd < 0)
        {
            printf("can't open!
    ");
            return -1;
        }
    
        fcntl(fd, F_SETOWN, getpid());
    
        Oflags = fcntl(fd, F_GETFL);
    
        fcntl(fd, F_SETFL, Oflags | FASYNC);
    
        while(1)
        {
            sleep(1000);
        }
        return 0;
    }
    
    

    测试:

    1. 修改Makefile
    2. 编译并拷贝到(first_fs)目录
    3. 加载模块(insmod ./fifth_chrdev.ko)
    4. 运行测试程序(./fifth_chrdev_test)


    所占资源:

    九、 字符设备驱动程序之同步互斥阻塞(第十一课)

    目的:同一时刻,只能有一个APP来打开同一个驱动程序

    方法一:用(canopen)变量做一个标记


    第一步:设置一个静态的全局变量

    第二步:一旦打开该驱动就让这个变量自减,自减后若等于0表示没有打开过,可以继续往下走;若有人打开过了,则变量自减之后等于负1,就会进入if里面把变量自加之后返回 "EBUSY"。

    第三步:在关闭这个设备的时候,在函数内让变量加1即可。

    但实质上有漏洞:因为我们linux是多任务的系统
    Linux是多任务系统,A程序执行过程中,有可能被切换成B程序执行这种情况。
    从汇编的角度看"canopen--",其实是被分成了3步去执行:1.读出,2.修改,3.写回。在这个分部执行的过程中很有可能中间会被切换出去

    解决办法:使用原子操作

    方法二:原子操作

    原子操作指的是在执行过程中不会被别的代码路径所中断的操作。

        常用原子操作函数举例:
        atomic_t v = ATOMIC_INIT(0);     //定义原子变量v并初始化为0
        atomic_read(atomic_t *v);        //返回原子变量的值
        void atomic_inc(atomic_t *v);    //原子变量增加1
        void atomic_dec(atomic_t *v);    //原子变量减少1
        int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false。
    

    第一步:修改代码,将 "canopen" 变量定义成"原子" 变量

    第二步:把打开设备文件函数的判断语句改为原子的测试函数

    第三步:把关闭设备文件函数的变量自加改为原子自加

    这样就不会发生"A程序执行中被B程序切换出去"的情况了,原子操作中间是不会被打断的。

    测试:

    在同一时刻,只有一个应用程序可以打同一个驱动程序,其余应用程序就无法打开这个驱动程序了

    方法三:信号量

    信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。当获取不到信号量时,进程进入休眠等待状态。
    在操作之前要申请(获取)信号量,若申请不到要么就返回要么就休眠等待;若申请到了就可以继续往下操作,操作完了之后得释放掉信号量,这时若有其他应用程序在等待信号量,就会去唤醒哪个应用程序。

        定义信号量:
        struct semaphore sem;
        初始化信号量
        void sema_init (struct semaphore *sem, int val);
        void init_MUTEX(struct semaphore *sem);//初始化为0
    
        static DECLARE_MUTEX(button_lock);     //定义互斥锁,也可以用以上3行代码自己定义然后初始化
    
        获得信号量:
        void down(struct semaphore * sem);//第二次无法获取到信号量的就会在这里陷入休眠,只有第一个应用程序去释放掉信号量的时候才会被唤醒,这个将死的程序不能被杀死(如果杀死这个将死程序,当杀死一个应用程序的时候,这两个程序都会被杀死)
        int down_interruptible(struct semaphore * sem);//若获取不到信号量就会休眠,但是这个休眠状态是可以被打断的,可以被结束掉
        int down_trylock(struct semaphore * sem);//试图取获取信号量,若获取不到就会返回,就不会陷入休眠
        释放信号量:
        void up(struct semaphore * sem);
    

    第一步:定义并初始化一个互斥变量

    这个宏会定义和初始化这个变量

    第二步:在驱动程序的open函数中获取信号量

    第三步:在驱动程序的release函数中释放信号量

    测试:
    第一次运行时应用程序调用open会获取一次信号量

    再次执行应用程序调用同一个驱动程序时会处于将死(D)状态

    方法四:阻塞(O_RDWR)和非阻塞(O_NONBLOCK)[默认为阻塞]

    阻塞操作:是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
    比如读取一个按键值,当前没有按键按下的话,那么就一直等待按键被按下了才返回。

    非阻塞操作:进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。
    比如读取一个按键值当前没有按键值可读取也立刻返回,单返回一个错误。

        格式:fd = open("设备名称", O_RDWR | O_NONBLOCK);//非阻塞
    

    在应用程序是通过open设备时,若传入的参数 "| O_NONBLOCK" 就是非阻塞操作,若不传入这个标记时,默认为阻塞操作。
    那么,对于阻塞或者非阻塞驱动程序会进行处理,这个"O_NONBLOCK"标记在上面的"file"这个变量中获取。这个结构时内核提供的。

    在驱动程序的open函数中判断阻塞方式

    在驱动程序的read函数中也得判断阻塞方式

    驱动程序(sixth_chrdev.c)

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    
    #include <linux/irq.h>
    
    #include <asm/uaccess.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    /*
     * eint 0  GPF0
     * eint 2  GPF2
     * eint 11 GPG3
     * eint 19 GPG11
     */
    static struct class *sixth_chrdev_class;
    static struct class_device *sixth_chrdev_class_dev;
    
    static volatile unsigned char key_val;
    
    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
    
    /* 中断事件标志, 中断服务程序将它置1,s3c24xx_buttons_read将它清0 */
    static volatile int ev_press = 0;
    
    static DECLARE_MUTEX(button_lock);     //定义互斥锁
    
    struct pin_desc{
            unsigned int pin;
            unsigned int key_val;
        };
    struct pin_desc pins_desc[4] = {
            {S3C2410_GPF0,  0x01},
            {S3C2410_GPF2,  0x02},
            {S3C2410_GPG3,  0x03},
            {S3C2410_GPG11, 0x04},
        };
    
    static irqreturn_t button_irq(int irq, void *dev_id)
    {
        struct pin_desc *pindesc = (struct pin_desc *)dev_id;
        unsigned char pinval;
        //printk("irq = %d
    
    ", irq);
        pinval = s3c2410_gpio_getpin(pindesc->pin);
        if(pinval)
        {
            /* 松开 */
            key_val = pindesc->key_val;
        }
        else
        {
            /* 按下 */
            key_val = 0x80|pindesc->key_val;
        }
            wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
            ev_press = 1;
    
        return IRQ_HANDLED;
    }
    
    
    static int sixth_chrdev_open(struct inode *inode, struct file *file)
    {
        printk("sixth_chrdev_open
    
    ");
        if(file->f_flags & O_NONBLOCK)
        {
            if (down_trylock(&button_lock))
                return -EBUSY;
        }
        else
        {
            /* 获取信号量 */
            down(&button_lock);
        }
        request_irq(IRQ_EINT0,   button_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
        request_irq(IRQ_EINT2,   button_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
        request_irq(IRQ_EINT11, button_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
        request_irq(IRQ_EINT19, button_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
    
        return 0;
    }
    
    
    static ssize_t sixth_chrdev_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
    {
        if (count != 1)
            return -EINVAL;
        if(file->f_flags & O_NONBLOCK)
        {
            if (!ev_press)
                return -EAGAIN;
        }
        else
        {
    
            /* 如果没有按键动作, 休眠 */
            wait_event_interruptible(button_waitq, ev_press);
        }
    
        copy_to_user(buf, &key_val, 1);
        ev_press = 0;
    
        return 1;
    }
    
    int sixth_chrdev_release(struct inode *inode, struct file *file)
    {
        printk("sixth_chrdev_release
    
    ");
        free_irq(IRQ_EINT0,   &pins_desc[0]);
        free_irq(IRQ_EINT2,   &pins_desc[1]);
        free_irq(IRQ_EINT11, &pins_desc[2]);
        free_irq(IRQ_EINT19, &pins_desc[3]);
        up(&button_lock);
        return 0;
    }
    
    
    static struct file_operations sixth_chrdev_fops = {
        .owner    =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
        .open     =  sixth_chrdev_open,
        .read     =  sixth_chrdev_read,
        .release  =  sixth_chrdev_release,
        };
    
    
    int major;
    static int sixth_chrdev_init(void)
    {
        major = register_chrdev(0, "sixth_chrdev", &sixth_chrdev_fops);  //注册
        sixth_chrdev_class = class_create(THIS_MODULE, "sixthchrdev");
        sixth_chrdev_class_dev = class_device_create(sixth_chrdev_class, NULL, MKDEV(major, 0), NULL, "sixth_chrdev");
    
        return 0;
    }
    
    static void sixth_chrdev_exit(void)
    {
        unregister_chrdev(major, "sixth_chrdev");                    //卸载
        class_device_unregister(sixth_chrdev_class_dev);
        class_destroy(sixth_chrdev_class);
    }
    
    module_init(sixth_chrdev_init);
    module_exit(sixth_chrdev_exit);
    
    MODULE_LICENSE("GPL");
    

    测试程序(sixth_chrdev_test.c)

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    #include <fcntl.h>
    
    #include <sys/types.h>
    #include <unistd.h>
    
    int fd;
    
    int main(int argc, char **argv)
    {
        volatile unsigned char key_val;
        int ret;
    
        fd = open("/dev/sixth_chrdev",O_RDWR | O_NONBLOCK);//非阻塞:O_NONBLOCK
        if(fd < 0)
        {
            printf("can't open!
    ");
            return -1;
        }
    
        while(1)
        {
            ret = read(fd, &key_val, 1);
            printf("ret : %d,  key_val = 0x%x
    ", ret, key_val);
            sleep(5);
        }
        return 0;
    }
    

    测试(非阻塞):
    第一次运行应用程序时会成功获取到信号量,但是会不停的读取按键值,若没有按键按下则返回"-EAGAIN"

    第二次运行应用程序就会无法打开

    <wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

  • 相关阅读:
    sp_executesql 带输出参数的 EXEC 动态执行 (z)
    查端口是否被占用 DOS 命令netstat(Z)
    MS SQL处理树型结构数据 将子节点记录的各项字段值累加赋值给其父节点
    lazarus unidac使用注意事项
    UNIDAC在arm linux运行出错的解决方法
    [转]为Linux下的Lazarus添加中文输入支持
    银河麒麟(aarch64)安装Lazarus步骤
    raspberry 添加拼音五笔输入法
    lazarus跨平台编译步骤
    拷贝构造函数与赋值操作符的具体实现 【转】参考度4.5星
  • 原文地址:https://www.cnblogs.com/luosir520/p/11446964.html
Copyright © 2011-2022 走看看