精华内容
下载资源
问答
  • 缓冲环形队列

    千次阅读 2018-05-03 09:11:10
    在程序的两个模块间进行通讯的时候,缓冲区成为... 对于写入和读出的处理可能产生的快慢不均匀的情况进行平衡,使得整个处理的速度趋于平滑的均匀状态,避免出现读出模块处理的慢速使得写入模块等待使得响应速度下降...

     

     

     

    在程序的两个模块间进行通讯的时候,缓冲区成为一个经常使用的机制。

     

    写入模块将信息写入缓冲区中,读出模块将信息读出缓冲区。这样使得:

    • 将程序清晰地划分模块,建立良好的模块化架构,使得写入和读出成为高聚合,低耦合的模块。
    • 对于写入和读出的处理可能产生的快慢不均匀的情况进行平衡,使得整个处理的速度趋于平滑的均匀状态,避免出现读出模块处理的慢速使得写入模块等待使得响应速度下降的状况;同样,也避免写入模块的快慢不均匀,使得读出模块忙闲不一的情况。
    • 可以增加处理的并发性。由于写入和读出模块的良好设计和划分,可以使得它们彼此隔离和独立,从而,使用线程和进程产生不同的并发处理,并通过缓冲区大小的调节,使得这个处理达到良好的匹配和运行状态。例如,写入模块可以有N个线程或进程,读出模块可以有M个线程和进程,缓存冲区可以配置L的大小。N、M、L可以通过模拟试验设定适应具体应用的值。也可以建立一套自动调节的机制,当然,这样会造成设计的复杂性。

    缓冲区显然不适合下面的情况:

    • 数据的接收处理原本就是密切相关,难以划分模块。
    • 处理中的模块间明显不存在处理不均匀的情况,或者不是主要问题。
    • 需要同步响应的情况。显然,写入端只是将信息入队到队列中,并不能得到读出端的处理响应信息,只能适合于异步的信息传递的情况。

    缓冲区的设计:

    • 缓冲区是一个先进先出队列。写入模块将信息插入队列;读出模块将信息弹出队列。
    • 写入模块与读出模块需要进行信息的协调和同步。
    • 对于多线程和多进程的写入或读出模块,写入模块间以及读出模块间需要进行临界区处理。

     

    队列使用环形队列,如上图。环形队列的特点是,不需要进行动态的内存释放和分配,使用固定大小的内存空间反复使用。在实际的队列插入和弹出操作中,是不断交叉进行的。空队列时头指针和尾指针重合且都为0,当分配一个内存时head(头指针)加1;当释放一个内存时tail(尾指针)加1。这样就实现了先入先出。当入队操作时,head会增加;而出队操作时,tail会增加。入队的速度快的时候,head有可能追上 tail,这个时候说明队列已经满了,不能再进行入队的操作了,需要等待出队 操作腾出队列的空间。当 出队 的操作快,使得 tail 追上 head,这个时候说明队列已空了,不能再进行出队 操作了,需要等待 入队 进来数据。

    下面是C语言编程常用到的语句:       head:队头

    tail:队尾      

    ·  初始化,head = tail = 0;          length:队列长度

    ·  出队列,tail = (tail + 1) % length; //队尾在不超过31时加1,超过时队尾值如程序所示

    ·  入队列,head = (head + 1) % length; //队头在不超过31时加1,超过时队头值如程序所示

    ·  判断队列满,(head + 1) % length == tail;   //例如:队尾为5,队头为4时 ,则表明队列已满

    ·  判断队列空,tail==head。                 //只要队头与队尾重合就表明队列为空

    在入队列前应判断队列是否已满,在出队列前应判断队列是否已空

     

    至此,我们可以总结一下:

    • 环形队列的数据结构和基本操作都是相当简单的。主要的操作就是入队和出队。
    • 环形队列可能需要线程或者进程间共享操作。尤其是进程间的共享,使得内存分配成为了一个相对复杂的问题。对于这个问题,我们将基础的数据结构定义为无指针和引用成员,适合于内存管理的类,然后又设计了一个代理的进行二次包装的类,将内存分配的功能作为模版可定制的参数。

    这里,我们设计并实现了环形队列的数据结构,并也为这个结构能够定制存放到共享内存设计好了方针策略。但下面的工作,将更富于挑战性:

    • 对于入队的操作,需要操作前等待队列已经有了空间,也就是说队列没有满的状态。等到这个状态出现了,才继续进行入队的操作,否则,入队操作挂起。
    • 对于出队 的操作,需要操作前等待队列有了数据,也就是说队列不为空的状态。等到这个状态出现了,才继续进行出队的操作,否则,出队操作挂起。
    • 上面的入队操作/出队操作一般是不同的进程和线程。同时,也有可能有多个入队的操作和出队操作的进程和线程。

     

     

     

    展开全文
  • 在搜索文献时,我们发现没有更多令人鼓舞的词:《 》一书断言,建立免等待队列是不可能的: 推论5.4.1。 从一组原子寄存器构造一个队列,堆栈,优先级队列,集合或列表的免等待实现是不可能的。 尽管FIFO队列可...
  • 内存缓冲和无效队列

    千次阅读 2018-12-08 15:09:01
    存储缓冲区和无效队列还是为了更好的利用CPU的高执行率。 首先印出来一个概念:缓存行(cacheline),数据在缓存里面不是杂乱无章的存储的,它是以缓存行的形式存储的。缓存行的大小就是CPU一次处理的最大数据的大小...

    存储缓冲区和无效队列还是为了更好的利用CPU的高执行率。

    首先印出来一个概念:缓存行(cacheline),数据在缓存里面不是杂乱无章的存储的,它是以缓存行的形式存储的。缓存行的大小就是CPU一次处理的最大数据的大小。CPU读取和修改数据都是直接对缓存行进行操作的,高速缓存里面都是有一个个的缓存行组成的。
    在此特别感谢http://www.wowotech.net/kernel_synchronization/memory-barrier.html 这篇文章的作者,里面写的非常细。

    1.MESI协议的缺点

    关于MESI协议我之前的一篇文章https://blog.csdn.net/qq_30055391/article/details/84862422 介绍过。
    在MESI协议里面如何一个CPU要修改一个处于shared状态的变量非常麻烦的。首先、
    1.本地缓存行将会通过寄存器控制器向远程拥有相同缓存行的寄存器发送一个RFO请求(Request For Owner),告诉其他CPU里面的缓存把缓存里面的值为valid状态,然后待收到各个缓存的(valid ack)已经完成无效状态修改的回应之后,
    2.再把自己的状态改为Exclusive,之后再进行修改。
    3.修改后再改为Modified状态,数据写入缓存行。
    上面这几步大家可以看到第一步的时候,CPU需要在等待所有的valid ack之后才会进行下面的操作。这部分就会让CPU产生一定的阻塞,无法充分利用CPU。这个时候就印出来了存储缓冲区 storeBuffer。

    2.存储缓冲区

    存储缓冲区的作用就是修改一个变量的时候,直接执行修改的操作不直接镇对缓存行,而是针对一个叫制作storeBuffer的位置来操作的。这样CPU在执行修改操作的时候,直接把数据写入到storeBuffer里面,并发出广播告知其他CPU,你们的缓存里面需要变为validate状态,然后去执行其他的操作,等接受到validate ack的时候才会回来把缓冲区里面的值写入到缓存行里面。
    下面是一个简单的架构图。
    在这里插入图片描述

    3.无效队列

    存储缓冲器的大小是有限的。如果存储缓冲区满了,那么依然需要等待validate ack的回复后才可以继续执行运算。但是对方的CPU可能当时正在执行其他的操作。无法及时把缓存里面的值改为validate状态,并发回validate ack操作。这个时候为了继续优化这段CPU空闲的时间。就出现了无效队列!
    在这里插入图片描述
    无效队列的作用就是CPU接收到validate广播的时候马上返回给对方validate ack响应。等当前的操作执行完再回来真正的把缓存里面的值标识为validate状态。

    4.存储缓存跟无效队列造成的问题

    存储缓存造成的问题就是在对方CPU还没有给我们回答的时候我们已经执行下一步代码了。
    无效队列造成的问题就是在CPU已经给对方应答的时候自己本身还没有去把这个值validate掉。
    上面的这两种情况有的时候我们是不希望它发生的。这个时候我们上一篇文章讲到的内存屏障就派上用场了。

    展开全文
  • 驱动等待队列,poll和select编程

    千次阅读 2018-11-16 20:03:21
    1.定义一个等待队列及初始化 1)动态初始化 wait_queue_head_t wq; //全局变量 init_waitqueue_head(&wq); //安装模块时候执行了初始化 2)静态初始化 DECLARE_WAIT_QUEUE_...

    一、等待队列

    1.定义一个等待队列及初始化
    1)动态初始化

    wait_queue_head_t  wq;       //全局变量
    init_waitqueue_head(&wq);   //安装模块时候执行了初始化
    

    2)静态初始化

    DECLARE_WAIT_QUEUE_HEAD(wq);    //这句等效于上面两句
    

    功能:建立一个等待队列,并且初始化好
    参数:
    wq:是一个类型wait_queue_head_t的变量,就是驱动自己定义的等待队列头。

    2.等待队列睡眠

    wait_event(wq, condition)
    //功能:建立不可以杀进程(信号不能唤醒,效果和msleep相同)。
    
    wait_event_interruptible(wq, condition)
    //功能:它可以被信号唤醒。休眠过程中,进程可以接收信号,收到后不管条件如何,直接返回。
    
    wait_event_timeout(wq, condition, timeout)
    //功能:休眠期间效果和 wait_event ,但是有一个超时时间 ,时间到不管条件如何,直接返回。
    
    wait_event_interruptible_timeout(wq, condition, timeout)
    //功能:休眠期间效果和 wait_event_interruptible相同。区别是有超时功能,时间到不管条件如何,直接返回。
    

    参数:
    wq: 是一个类型wait_queue_head_t的变量,就是驱动自己定义的等待队列头。
    Condition :可以为任何类型,通常定义为整形,值为0进入休眠,值为非0直接返回。
    Timeout :超时时间。

    3.等待队列的唤醒

    wake_up(wq)	//常用
    //功能:用于唤醒各种方式进入休眠的进程,只唤醒队列上的一个进程。
    
    wake_up_all(wq)
    //功能:效果和wake_up相同,只是能唤醒队列上所有的进程。
    
    wake_up_interruptible(wq)	//常用
    //功能:只能用于唤醒一个 使用wait_event_interruptible*休眠的进程。
    
    wake_up_interruptible_all(wq)
    //功能:能唤醒队列所有 使用wait_event_interruptible*休眠的进程。
    

    参数:
    wq: 是一个类型wait_queue_head_t指针。

    代码例子:
    驱动层;

    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/miscdevice.h>
    #include <linux/interrupt.h>
    #include <linux/irq.h>
    #include <linux/gpio.h>
    #include <asm/uaccess.h>
    #include <linux/delay.h>      //msleep
    
    //1)第一步: 添加相关头文件
    #include <linux/wait.h>        //等待队列相关数据结构及函数
    #include <linux/sched.h>       //进程状态宏定义
    
    //2)定义一个等待队列头变量,并且初始化
    static DECLARE_WAIT_QUEUE_HEAD(wq);
    
    //按键数量
    #define BTN_SIZE   4
    //按键缓冲区,'0'表示没有按键,'1'表示按下了
    static char keybuf[] = {"0000"};
    /*把一个当成一个对象来看待,方便编程,定义一个描述按键结构*/
    struct button_desc {
        int  gpio;   //存放io口编号
        int  number; //存放按键编号,根据自己需要设计,
        char *name;  //按键名字,随便,但是要有意义
    };
    /* 定义4个按键的信息 */
    static struct button_desc buttons[] = {
        { EXYNOS4_GPX3(2), 0, "KEY0" },
        { EXYNOS4_GPX3(3), 1, "KEY1" },
        { EXYNOS4_GPX3(4), 2, "KEY2" },
        { EXYNOS4_GPX3(5), 3, "KEY3" },
    };
    init_waitqueue_head
    
    /* 按键动作标志,在中断程序中置1,read成功复制数据后清0 */
    int press = 0;
    
    //中断服务函数
    irqreturn_t key_isr(int irq, void* dev)
    {
        //存放按键状态
        int dn = 0;
        int index = 0;
    
        //这里进行还原原来的类型
        struct button_desc *bdata = (struct button_desc *)dev;
    
        //把读取到的结果取逻辑非,因为程序设计使用正逻辑。'1'表示按下。
        dn = !gpio_get_value(bdata->gpio);
    
        //取得当前中断对应 的按键编号
        index = bdata->number;
        //把按键状态更新到对应的按缓冲中
        keybuf[index] = dn + '0';
    
        //输出按键提示
      //  printk("%s %s\r\n", bdata->name, dn ? "down" : "up");
    
       //4) 在等待条件变成真的地方调用  wake_up*函数唤醒休眠的进程
        press = 1;
    	
    	wake_up_interruptible(&wq) ;//通知内核到wq这个队列头上的链表去查询每一个休眠的进程 的条件是否变成了真。
                        //如果进程等待的条件还没有是真,则继续休眠。
    
        return IRQ_HANDLED;
    }
    
    
    static ssize_t tiny4412_read (struct file *flp,char __user *buff,size_t count,loff_t * off)
    {
        int ret = 0;
        
        //用户传递0,直接返回
        if(!count) {
            return 0;
        }
    
        //修正参数
        if(count > BTN_SIZE ) {
            count = BTN_SIZE;
        }
    
    
        /* 没有按键动作( 按下和松开时候 )*/
        if (!press) {
            if (flp->f_flags & O_NONBLOCK) { //调用open("/dev/button",flags) --》flags 存放在filp->f_flags = flags
                return -EAGAIN;
            } else {
               //3)在需要休眠的地方使用wait_event*函数进行进行。
                //休眠,等待有按键动作唤醒进程。
               wait_event_interruptible(wq, press);
            }
        }
        /* 清标志 */
        press = 0;
    
        //复制数据到用户空间
        ret = copy_to_user(buff, keybuf, count);
        if(ret) {
            printk("error:copy_to_user\r\n");
            return -EFAULT;
        }
        return count;
    }
    
    static const struct file_operations dev_fops = {
        .read   =   tiny4412_read,
        .owner  =   THIS_MODULE,
    };
    
    #define LEDS_MAJOR  255   //255
    #define DEVICE_NAME  "mybtn"
    static struct miscdevice misc = {
        .minor = LEDS_MAJOR, //次设备号
        .name  = DEVICE_NAME,//设备名
        .fops  = &dev_fops,  //文件操作方法
    };
    
    static int __init btn_init(void)
    {
        int ret;
        int irq;
        int i;
        int flags;
    
        flags = IRQ_TYPE_EDGE_BOTH; //设置为双边触发
        for ( i = 0; i < 4 ; i++ ) {
            //得到中断号
            irq = gpio_to_irq(buttons[i].gpio); //keyX
    
            //注册中断
            ret = request_irq(irq, key_isr, flags, buttons[i].name, (void*)&buttons[i]);
            if(ret < 0) {
                break;
            }
        }
    
        //如果不是全部成功,则反向注销已经注册的中断
        if(ret < 0) {
            for ( --i; i; i-- ) {
                irq = gpio_to_irq(buttons[i].gpio); //keyX
                disable_irq(irq);
                free_irq(irq, (void*)&buttons[i]);
            }
            return ret;
        }
    
        //注册杂项设备
        ret = misc_register(&misc);       //注册混杂设备
        return ret;
    }
    
    static void __exit btn_exit(void)
    {
        int i = 0;
        int irq;
    
        //注销中断
        for (i = 0; i < 4; i++) {
            irq = gpio_to_irq(buttons[i].gpio); //keyX
            disable_irq(irq);
            free_irq(irq, (void*)&buttons[i]);
        }
        //注销杂项设备
        misc_deregister(&misc);
    }
    
    module_init(btn_init);
    module_exit(btn_exit);
    MODULE_LICENSE("GPL");
    

    应用层:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>    //lseek
    #include <sys/ioctl.h> //ioctl
    #include <poll.h>      //poll
    
    char save_buf[10] = {"0000"};   //存放数据使用
    
    int main(void)
    {
        int fd;                      //存放文件描述符号
        int ret;
        int i;
        struct pollfd fds[1];
    
        //以非阻塞方式打开
        //fd = open("/dev/mybtns", O_RDWR | O_NONBLOCK );
         //以读写方式进行打开,默认是阻塞方式打开的
        fd = open("/dev/mybtns", O_RDWR );
        if(fd < 0) {
            printf("open error\r\n");
            return -1;
        }
    
        //实际程序需要循环读取按键动作,然后根据动作完成不同事情
        while(1) {
            char cur_buf[10] = {0};   //临时存放数据使用
    
            fds[0].fd     = fd;
            fds[0].events = POLLIN;  //要监测读事件
    
            //ret = poll(fds, 1, -1);   //   永远阻塞直到有变化 
            //ret = poll(fds, 1, 0);      //   非阻塞
            ret = poll(fds, 1, 2000); //   2秒超时
            //判断查询结果
            if(ret < 0) {
                perror("poll");
                exit(0);
            } else if(ret == 0) {
                printf("timeout\r\n");
    			continue;
            } else {
                //分别判断每个fd
                if(fds[0].revents & POLLIN) {
                    //回读当前的4个灯状态
                    read(fd, cur_buf, 4);
                    for(i = 0; i < 4; i++) {
                        if(cur_buf[i] != save_buf[i]) {
                            save_buf[i] = cur_buf[i] ; //更新当前按键状态
                            if(save_buf[i] == '1') {
                                printf("K%d press\r\n", i + 1);
                            } else {
                                printf("K%d up\r\n", i + 1);
                            }
                            printf("keys:%s\r\n", save_buf);
                        }
                    }
                    printf("keys:%s\r\n", save_buf);
                }
            }
        }
        //关闭文件
        close(fd);
        return 0;
    }
    

    二、poll接口

    1.驱动层

    void poll_wait(struct file * pfile, wait_queue_head_t * wait_address, poll_table *p)
    

    参数:
    pfile:由unsigned int xxxx_poll(struct file *filp,struct poll_table_struct *wait)第一个参数传递
    wait_address:上面的定义并且初始化的等待队列头wq
    p:由unsigned int xxxx_poll(struct file *filp,struct poll_table_struct *wait)第二个参数传递
    返回值:成功:返回设备的状态掩码( 正数):可读,可写,出错掩码;失败:负数,错误码。

    返回值掩码含义
    POLLIN如果设备无阻塞的读,就返回该值
    POLLRDNORM通常的数据已经准备好,可以读了,就返回该值。
    POLLERR如果设备发生错误,就返回该值。
    POLLOUT如果设备可以无阻塞地写,就返回该值
    POLLWRNORM设备已经准备好,可以写了,就返回该值。

    设备可读,通常返回: (POLLIN | POLLRDNORM)
    设备可写,通常返回: (POLLOUT | POLLWRNORM)

    2.应用层

    int poll(struct pollfd fd[], nfds_t nfds, int timeout)
    

    功能:可以阻塞/非阻塞地监测多个文件的可读、 可写、 错误事件发生。 poll 函数退出后, struct pollfd 变量的fd,events 值被清零,需要重新设置, revents 变量包含了监测结果。
    参数:
    fd:表示被监视的文件描述符(不用申明,需要定义和初始化赋值),结构如下:

    struct pollfd {
    int fd; //文件描述符
    short events; //请求的事件
    short revents; //返回的事件
    };
    

    fd:打开文件的文件描述符
    events:传入需要监测事件
    revents:传出返回的事件

    nfds:要监视的文件描述符的数量。
    timeout:大于0 :等待指定数目的毫秒数;0 :立即返回,不阻塞进程;-1 :永远等待, 直到有任何一个监测的文件描述符发生变化
    返回值:
    大于0 : fd 数组中准备好读,写或出错状态的那些文件描述符号的总数量( 我们要关心这种情况)
    等于0 : 超时
    小于0 : 调用函数失败

    返回值意义
    POLLIN普通或优先级带数据可读
    POLLRDNORM普通数据可读
    POLLRDBAND优先级带数据可读
    POLLPRI高优先级数据可读
    POLLOUT普通数据可写
    POLLWRNORM普通数据可写
    POLLWRBAND优先级带数据可写
    POLLERR发生错误
    POLLHUP发生挂起
    POLLNVAL描述字不是一个打开的文件

    注意:后三个只能作为描述字的返回结果存储在 revents 中,而不能作为测试条件用于 events 中。

    三、select函数

    1.应用层 (对应设备驱动的 poll接口)

    int select(int nfds,fd_set *readset, fd_set *writeset,fd_set *exceptset, struct timeval*timeout)
    //exceptset 三个集合中的 fd, 所以如果想检测它们, 则需要在返回后再次添加。 
    

    参数说明:
    ndfs: select 监视的文件文件描述符中值最大值+1。
    readset: select 监视的可读文件描述符集合。可以传入 NULL 值,表示不关心任何文件的读变化。
    writeset: select 监视的可写文件描述符集合。可以传入 NULL 值,表示不关心任何文件的写变化。
    exceptset: select 监视的异常文件描述符集合。
    timeout:本次 select()的超时结束时间。
    时间结构定义如下:

    struct timeval{
    long tv_sec; /*秒 */
    long tv_usec; /*微秒 */
    }
    

    返回值:
    大于 0:执行成功则返回文件描述符状态已改变的个数;
    等于0:代表已超过 timeout 时间, 文件描述符状态还没有发生改变;
    等于-1:函数有错误发生错误原因存于 errno,此时参数 readset, writeset, exceptset 和 timeout 的值变成不可预测。

    代码例子:
    驱动层代码:

    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/miscdevice.h>
    #include <linux/interrupt.h>
    #include <linux/irq.h>
    #include <linux/gpio.h>
    #include <asm/uaccess.h>
    #include <linux/delay.h>      //msleep
    #include <linux/poll.h>        //poll接口的相关定义及函数
    
    //1)第一步: 添加相关头文件
    #include <linux/wait.h>        //等待队列相关数据结构及函数
    #include <linux/sched.h>       //进程状态宏定义
    
    //2)定义一个等待队列头变量,并且初始化
    static DECLARE_WAIT_QUEUE_HEAD(wq);
    
    //按键数量
    #define BTN_SIZE   4
    
    //按键缓冲区,'0'表示没有按键,'1'表示按下了
    static char keybuf[] = {"0000"};
    
    /*把一个当成一个对象来看待,方便编程,定义一个描述按键结构*/
    struct button_desc {
        int  gpio;   //存放io口编号
        int  number; //存放按键编号,根据自己需要设计,
        char *name;  //按键名字,随便,但是要有意义
    };
    
    /* 定义4个按键的信息 */
    static struct button_desc buttons[] = {
        { EXYNOS4_GPX3(2), 0, "KEY0" },
        { EXYNOS4_GPX3(3), 1, "KEY1" },
        { EXYNOS4_GPX3(4), 2, "KEY2" },
        { EXYNOS4_GPX3(5), 3, "KEY3" },
    };
    
    /* 按键动作标志,在中断程序中置1,read成功复制数据后清0 */
    int press = 0;
    
    //中断服务函数
    irqreturn_t key_isr(int irq, void* dev)
    {
        //存放按键状态
        int dn = 0;
        int index = 0;
    
        //这里进行还原原来的类型
        struct button_desc *bdata = (struct button_desc *)dev;
    
        //把读取到的结果取逻辑非,因为程序设计使用正逻辑。'1'表示按下。
        dn = !gpio_get_value(bdata->gpio);
    
        //取得当前中断对应 的按键编号
        index = bdata->number;
        //把按键状态更新到对应的按缓冲中
        keybuf[index] = dn + '0';
    
        //输出按键提示
      //  printk("%s %s\r\n", bdata->name, dn ? "down" : "up");
    
       //4) 在等待条件变成真的地方调用  wake_up*函数唤醒休眠的进程
        press = 1;
    	
    	wake_up_interruptible(&wq) ;//通知内核到wq这个队列头上的链表去查询每一个休眠的进程 的条件是否变成了真。
                        //如果进程等待的条件还没有是真,则继续休眠。
    
        return IRQ_HANDLED;
    }
    
    static ssize_t tiny4412_read (struct file *flp, char __user *buff,size_t count,loff_t * off)
    {
        int ret = 0;
    
        //用户传递0,直接返回
        if(!count) {
            return 0;
        }
    
        //修正参数
        if(count > BTN_SIZE ) {
            count = BTN_SIZE;
        }
    
        /* 没有按键动作( 按下和松开时候 )*/
        if (!press) {
            if (flp->f_flags & O_NONBLOCK) { //调用open("/dev/button",flags) --》flags 存放在filp->f_flags = flags
                return -EAGAIN;
            } else {
              //  while(press == 0) {  //这个循环中不能是独占类型代码,否则进程会死循环。 不能是while(press==0);
              //      msleep(5);       //休眠,进程放弃CPU,这种休眠后不可被信号中断。
               // }
               //3)在需要休眠的地方使用wait_event*函数进行进行。
                //休眠,等待有按键动作唤醒进程。
               wait_event_interruptible(wq, press);
            }
        }
    
        /* 清标志 */
        press = 0;
    
        //复制数据到用户空间
        ret = copy_to_user(buff, keybuf, count);
        if(ret) {
            printk("error:copy_to_user\r\n");
            return -EFAULT;
        }
        return count;
    }
    //轮询接口
    //这个函数执行时候不会引起阻塞,
    unsigned int tiny4412_poll (struct file *pfile, struct poll_table_struct *wait)
    {
        unsigned int mask = 0;       //一定要初始化为0,因为mask是局部变量
    
        //1)调用 poll_wait 把当前进程添加到等待队列中
        poll_wait(pfile, &wq, wait); //这个函数不会引起进程的阻塞
    
        //2)返回设备状态掩码(是否可读可写标志)
        if(press) {
            mask = POLLIN | POLLRDNORM;
        }
        return mask;
    }
    
    static const struct file_operations dev_fops = {
        .read   =   tiny4412_read,
    	.poll   =   tiny4412_poll,
        .owner  =   THIS_MODULE,
    };
    
    #define LEDS_MAJOR  255   //255
    #define DEVICE_NAME  "mybtns"
    
    static struct miscdevice misc = {
        .minor = LEDS_MAJOR, //次设备号
        .name  = DEVICE_NAME,//设备名
        .fops  = &dev_fops,  //文件操作方法
    };
    
    static int __init btn_init(void)
    {
        int ret;
        int irq;
        int i;
        int flags;
    
        flags = IRQ_TYPE_EDGE_BOTH; //设置为双边触发
        for ( i = 0; i < 4 ; i++ ) {
            //得到中断号
            irq = gpio_to_irq(buttons[i].gpio); //keyX
    
            //注册中断
            ret = request_irq(irq, key_isr, flags, buttons[i].name, (void*)&buttons[i]);
            if(ret < 0) {
                break;
            }
        }
    
        //如果不是全部成功,则反向注销已经注册的中断
        if(ret < 0) {
            for ( --i; i; i-- ) {
                irq = gpio_to_irq(buttons[i].gpio); //keyX
                disable_irq(irq);
                free_irq(irq, (void*)&buttons[i]);
            }
            return ret;
        }
        //注册杂项设备
        ret = misc_register(&misc);       //注册混杂设备
        return ret;
    }
    
    static void __exit btn_exit(void)
    {
        int i = 0;
        int irq;
        //注销中断
        for (i = 0; i < 4; i++) {
            irq = gpio_to_irq(buttons[i].gpio); //keyX
            disable_irq(irq);
            free_irq(irq, (void*)&buttons[i]);
        }
        //注销杂项设备
        misc_deregister(&misc);
    }
    
    module_init(btn_init);
    module_exit(btn_exit);
    MODULE_LICENSE("GPL");
    

    应用层代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>    //lseek
    #include <sys/ioctl.h> //ioctl
    #include <poll.h>      //poll
    
        //select
    #include <sys/select.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    #define   poll_or_select    0//poll:1;select:0
    
    char save_buf[10] = {"0000"};   //存放数据使用
    
    int main(void)
    {
        int fd;                      //存放文件描述符号
        int ret;
        int i;
    #if      poll_or_select
            struct pollfd fds[1];
    #else
    	     fd_set readset ;             //定义监测读集合
    		struct timeval timeout; 
    #endif    
        //以非阻塞方式打开
        //fd = open("/dev/mybtns", O_RDWR | O_NONBLOCK );
         //以读写方式进行打开,默认是阻塞方式打开的
        fd = open("/dev/mybtns", O_RDWR );
        if(fd < 0) {
            printf("open error\r\n");
            return -1;
        }
    
        //实际程序需要循环读取按键动作,然后根据动作完成不同事情
        while(1) {
            char cur_buf[10] = {0};   //临时存放数据使用
    #if  poll_or_select    
    
                fds[0].fd     = fd;
                fds[0].events = POLLIN;  //要监测读事件
        
                //ret = poll(fds, 1, -1);   //   永远阻塞直到有变化 
                //ret = poll(fds, 1, 0);      //   非阻塞
                ret = poll(fds, 1, 2000); //   2秒超时
    #else
    		//必须每重新设备,因为每次select返回时。timeout变成值会清成为0
            timeout.tv_sec   = 2;
            timeout.tv_usec  = 0;
    
            //一般在重新添加监测对象时候前需要前0全部fd
            FD_ZERO(&readset);        //清集合
    
            //必须每次重新添加要监测的对象,因为每次select返回时。readset变成部分值会清成为0
            FD_SET(fd, &readset);     //添加监测对象到集合
    
            //永远阻塞直到有文件状态发生变化
            ret = select(fd+1,&readset,NULL,NULL,NULL);
            //2秒超时
            //ret = select(fd + 1, &readset, NULL, NULL, &timeout);
    #endif        
            //判断查询结果
            if(ret < 0) {
                perror("poll"); perror("select");
                exit(0);
            } else if(ret == 0) {
                printf("timeout\r\n");
    			continue;
            } else {
                //分别判断每个fd
    #if  poll_or_select              
                    if(fds[0].revents & POLLIN) 
    #else                
                     if(FD_ISSET(fd, &readset))
    #endif                 
                {
                    //回读当前的4个灯状态
                    read(fd, cur_buf, 4);
                    for(i = 0; i < 4; i++) {
                        if(cur_buf[i] != save_buf[i]) {
                            save_buf[i] = cur_buf[i] ; //更新当前按键状态
                            if(save_buf[i] == '1') {
                                printf("K%d press\r\n", i + 1);
                            } else {
                                printf("K%d up\r\n", i + 1);
                            }
                            printf("keys:%s\r\n", save_buf);
                        }
                    }
                    printf("keys:%s\r\n", save_buf);
                }
            }
        }
    
        //关闭文件
        close(fd);
        return 0;
    }
    
    展开全文
  • ZeroMQ系统的缓冲队列

    千次阅读 2013-06-06 14:48:43
    ZeroMQ绑定socket端口通讯,socket端口本身具有缓冲区,可以放置一些来不及处理的信息,ZeroMQ自身也实现一个缓冲队列,默认队列长度为1000。如果系统来不及处理,导致缓冲队列填满,则依据不同的通讯模式执行阻塞...

    ZeroMQ是一个消息队列系统,很容易实现分布式架构。传递的内容都是一个个的消息,在使用的时候不用关心具体的细节,但是可以研究一下他的基本结构。

    ZeroMQ绑定socket端口通讯,socket端口本身具有缓冲区,可以放置一些来不及处理的信息,ZeroMQ自身也实现一个缓冲队列,默认队列长度为1000。如果系统来不及处理,导致缓冲队列填满,则依据不同的通讯模式执行阻塞动作或者直接丢弃相关消息。结构图如下:

    缓冲队列长度可以调整,以python代码为例:
    import zmq
    context = zmq.Context()
    p = context.socket(zmq.PUSH)
    p.setsockopt(zmq.SNDHWM, 2000)
    发送缓冲队列长度调整为2000
    执行p.setsockopt(zmq.SNDBUF, 200 *1024)将socket缓冲区大小调整为200K

    ZeroMQ的PUSH/PULL是一种推送模式,pull端绑定一个端口,用于接收消息,多个push端可以同时推送消息到达pull端。当pull端处理不过来,接收缓冲队列填满,这时如果push端的发送缓冲队列也满了,则push端会阻塞,等待pull端处理完成,push端才会继续执行。

    PUB/SUB是一种广播订阅模式,pub端绑定端口,用于广播消息,多个sub端来订阅相关的消息。Pub端对缓冲队列没有要求,会一直不停写到socket缓冲区中,scoket缓冲区满了,则会替换旧有的数据。Sub端不断读取socket内容到缓冲队列,如果缓冲队列满了,则sub端不再接受新消息,直接丢弃,直到缓冲队列有空闲,再继续接受新消息。

    展开全文
  • 实现一个双缓冲队列

    千次阅读 2017-02-18 20:40:12
    今天要介绍的双缓冲队列就是个不错的选择。 双缓冲队列就是冲着同步/互斥的开销来的。我们知道,在多个线程并发访问同一个资源的时候,需要特别注意线程的同步问题。稍稍不注意,噢货,程序结果不正确了。 ...
  • 本文讲的缓冲队列为链接方式, 链接方式一般通过预分配一个结点作为接力点来实现无锁队列, 优点是实现简单, 缺点是浪费一个结点的内存, 当结点内存块尺寸较大时浪费就大了。如何不浪费一个结点内存的链接方式无锁队列...
  • 管道: 消息缓冲队列: 共享缓冲区:
  • 等待队列Wait Queue 定义等待队列头 初始化等待队列头 定义等待队列元素 添加移除等待队列 等待事件 唤醒队列 在等待队列上睡眠 轮询 selectpoll 或 epoll 设备驱动中的 poll 模板 实例 等待队列 轮询 grammar_...
  • C++ 队列缓冲

    千次阅读 2019-06-13 10:00:48
    阻塞式消息队列、消息数据缓冲区、环形缓冲区、在自定义应用层协议中的应用..
  • 当生产者往缓冲区中放入元素时,判断是否已经到了该队列的末尾,若到了末尾则应该,绕回到队列的首部放入元素,也就是第0个位置。 而消费者也是类似的操作(当取元素的位置到了队列最后一个,则绕回到第0个位置).....
  • 服务器端利器--双缓冲队列

    千次阅读 2012-11-27 09:22:15
    传统队列是生产者线程和消费者线程...双缓冲队使用两个队列,将读写分离,一个队列专门用来读,另一个专门用来写,当读队列空或写 队列满时将两个队列互换。这里为了保证队列的读写顺序,当读队列为空且写队列不为
  • 基于STM32的串口数据环形缓冲队列

    千次阅读 2019-03-20 21:45:46
    BTW:在串口循环发送一组数据的时候,应该在发送第一个字节之前也加上判断缓冲区是否为空。 正确形式如下: //这个函数会循环执行 void stop(void){ //发送10给上位机,使其进入数据解调 float_data.d = 10 * ...
  • 这一种数据处理机制是“非缓冲中断方式”,虽然这种数据处理方式不消耗时间,但是这种数据处理方式严重的缺点是:数据无缓冲区,如果先前接收的的数据如果尚未发送完成(处理完成),然后串口又接收到新的数据,新...
  • 生产者消费者模型与双缓冲队列

    千次阅读 2011-07-02 10:08:32
    传统队列是生产者线程和消费者线程从同一个队列中存取数据,必然需要互斥访问,在互相同步等待中浪费了宝贵的时间,使队列吞吐量受影响。双缓冲队使用两个队列,将读写分离,一个队列专门用来读,另一个专门用来写,...
  • Linux 阻塞IO(等待队列)原理及架构

    千次阅读 2019-03-25 17:44:36
    一. 阻塞操作 阻塞操作是指在执行折本操作时,若不能获得自愿,则挂起进程直到满足可操作性的条件后在进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。... 等待队列 在...
  • 在Linux驱动程序中,可以使用等待队列(wait queue)来实现阻塞进程的唤醒。等待队列可以用来同步对系统资源的访问。 1.定义和初始化队列头 wait_queue_head_t wqh; init_waitqueue_head(wait_queue_head_t *wqh); 2...
  • 在服务器开发中 通常的做法是 把 逻辑处理线程和I/O处理线程分离。  逻辑处理线程:对接收的包进行逻辑处理。  I/0处理线程:网络数据的发送和接收,连接的建立和维护。 ... 通常 逻辑处理线程和... 这个数据队列
  • 应用程序只是调用read或write并得到返回值,故应用程序不处理此类问题,此时驱动程序就应当阻塞进程,使它进入睡眠并等待条件满足,这就是阻塞型IO. 阻塞方式是文件读写操作的默认方式,但应用程序
  • 线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。...
  • DPDK 无锁环形队列(Ring)详解

    万次阅读 2020-11-08 22:18:57
    DPDK 无锁环形队列(Ring) 此篇文章主要用来学习和记录DPDK中无锁环形队列相关内容,结合了官方文档说明和源码中的实现,供大家交流和学习。 Author : Toney Email : vip_13031075266@163.com Date : 2020.11.8 ...
  • IO 的阻塞和非阻塞一:等待队列

    千次阅读 2016-09-05 19:14:04
    wake_up_interruptible将目前进程的状态置成 TASK_ INTERRUPTIBLE,并定义一个等待队列,之后把它附属到等待队列头q,直到资源可获得,q引导的等待队列被唤醒或者进程收到信号。 sleep_on()函数应该与 wake_up()...
  • //如果队列不存在任何元素 则一直等待。 FiexedThreadPool典型模式----------条件2 if (r != null) return r; if (workerCanExit()) { //--------------------------条件3 if (runState >= SHUTDOWN) // ...
  • 使用消息缓冲队列来实现client进程和server进程之间的通信。  问题描述如下:   server进程先建立一个关键字为SVKEY(如75)的消息队列,然后等待接收类型为REQ(如1)的消息;在收到请求消息后,它便显示...
  • 缓冲队列,生产者消费者模式

    千次阅读 2012-06-11 12:25:38
    等待接管另一个缓冲区的拥有权 WaitForSingleObject(pSendQueue->synEventHandle, INFINITE); // 接管另一个缓冲区 pReadQueue = pSendQueue; } // 插入数据包 pReadQueue->buffer.Push(item);...
  • Linux驱动阻塞与非阻塞IO之等待队列

    千次阅读 2012-03-22 20:42:17
    上次我和大家一起探讨了Linux驱动中的竞态问题,本环节为们来探讨一下...后再进行操作,被挂起的进程会进入休眠状态,被从调度器的运行队列中移除,直到条件被满足。 非阻塞操作:是指在执行设备操作时,若不能获得
  • 同时还有一组哈希队列使得内核可以快速找到所需数据的缓冲块是否在高速缓冲中。一个哑节点free_list标记了空闲链表的头,虽然名字是空闲链表,并不代表链表中的节点都是空闲没有有效数据的节点。当内核需要读取磁盘...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 132,938
精华内容 53,175
关键字:

无缓冲的等待队列