精华内容
下载资源
问答
  • 本文将介绍用于备受青睐的反激式转换器的最常用的RCD箝位电路,及其设计公式。  没有缓冲器,反激式变压器振铃的漏感会随电路中的杂散电容产生,生产大幅度高频波形,如图所示1。  许多应用笔记和设计没有解决...
  • 引入缓冲技术的原因: 1、 为了进一步缓和CPU和I/O设备之间速度不匹配矛盾。 2、 提高CPU与I/O设备之间并行性。 3、 为了减少中断次数和CPU中断处理时间。如果没有缓冲,慢速I/O设备每传一个字节就要产生一...

    缓冲是两种不同速度设备之间的传输信息时平滑传输过程的常用手段。

    引入缓冲技术的原因:

    1、  为了进一步缓和CPU和I/O设备之间速度不匹配的矛盾。

    2、  提高CPU与I/O设备之间的并行性。

    3、  为了减少中断次数和CPU的中断处理时间。如果没有缓冲,慢速I/O设备每传一个字节就要产生一个中断,CPU必须处理该中断。如果用了缓冲技术,则慢速的I/O设备将缓冲区填满时,才向CPU发出中断,从而减少了中断次数和CPU的中断处理时间。

    4、  为了解决DMA或通道方式下数据传输的瓶颈问题。DMA或通道方式都适用于成批数据传输,在无缓冲的情况下,慢速I/O设备只能一个字节一个字节的传输信息,这造成DMA方式或通道方式数据传输的瓶颈。缓冲区的设置适应了DMA或通道方式的成批数据传输方式,解决了数据传输的瓶颈问题。

    转载于:https://www.cnblogs.com/luo841997665/p/4669618.html

    展开全文
  • 其中所谓的块缓冲便是我们前面一直在讨论的缓冲技术常用来配备IO操作,用来减少IO读取次数,以提升系统效率。本文便是此前《换成技术》系列的两篇文章的基础上继续讨论缓冲池技术。对于块缓冲体系而言,需要提及...

    0. Linux下缓冲池技术的简单介绍

    Linux文件系统中,存在着著名的三大缓冲技术用以提升读写操作效率:inode缓冲区、dentry缓冲区、块缓冲。其中所谓的块缓冲便是我们前面一直在讨论的缓冲池技术,常用来配备IO操作,用来减少IO读取次数,以提升系统效率。

    本文便是此前《换成技术》系列的两篇文章的基础上继续讨论缓冲池技术。对于块缓冲体系而言,需要提及的两个概念分别是page cache和buffer cache,每个page cache包含若干buffer cache,即两者的粒度不同,page是在buffer缓冲块基础上封装出的更粗粒度的对象。

    内存管理系统和VFS(虚拟文件系统)只与page cache这一粗粒度级别的缓冲对象进行交互,内存管理系统负责维护page cache的分配和回收(如按照LRU策略进行淘汰)。在内核需要读写数据时,使用“内存映射”等复杂机制进可以和物理内存块进行正确的映射。而具体文件系统一般只与buffer cache这一更小粒度级别的缓冲对象交互,它们负责在存储设备和buffer cache之间交换数据,具体的文件系统直接操作的是磁盘等disk部分,而VFS则负责将数个buffer cache包装成page cache提供给用户。

    对于具体的Linux文件系统,磁盘等外设存储设备会以磁盘块为单位分配给文件用以存储,所以buffer cache大小正好对应着磁盘块block的大小。引入缓冲区的目的主要还是为了降低对文件存储的外部设备的IO操作次数。每个缓冲区由两个部分组成,第一部分称为缓冲区首部,用数据结果buffer_head表示,而第二部分是真正的存储的数据。(这里可以参考我的关于MiniCRT自定义简化版运行库中提供的堆管理

    typedef struct _heap_header
    {
        enum{
            HEAP_BLOCK_FREE = 0xABABABAB, //空闲块的魔数
            HEAP_BLOCK_USED = 0xCDCDCDCD, //占用块的魔数
        }type;
    
        unsigned size;  //当前块的尺寸,该size包括块的信息头的尺寸
        struct _heap_header* next;
        struct _heap_header* prev;
    }heap_header;

    1. Linux系统下IO操作使用Buffer缓冲块的过程

    首先给出page cache的定义

    typedef struct page {
        struct list_head list; //mapping has some page list
        struct address_space *mapping; //the inode we belong to 
        unsigned long index;   //our offset within mapping
        struct page *next_hash; //Next page sharing our hash bucket in the pagecache hash table
        //和为了快速管理buffer采用hash table一样,管理page同样擦用了hash table,这个next_hash表示和该
        //page的hash-key一样的下一个page的指针
    
        atomic_t count; //线程或进程使用计数,在该计数为0时意味着该page已经可以被清除了
        unsigned long flags;  //原子锁标志,有可能存在更新不同步的情况
        struct list_head lru;  //pageout list, eg. active_list; protected by pagemap_lru_lock !
    
        struct page *pprev_hash; //complement to next_hash和next_hash相对应的指向前面page的指针
        struct buffer_head *buffers; //buffer maps us to a disk block;
        /******
        *On machines where all RAM is mapped into kernel address space, we can simply 
        calculate the virtual address. On machines with highmem some memory  is mapped into 
        kernel virtual memory dynamically.So we need a place to store that address. Note 
        that this field could be 16 bits on x86...Architectures with slow multiplication can 
        define WANT_PAGE_VIRTUAL in asm/page.h
        *********/
    #if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL)
        void *virtual;
    #endif
    } mem_map_t;

    再给出buffer cache的定义形式,其中首先给出buffer_head结构体的定义内容

    struct buffer_head {
        struct buffer_head *b_next; //有效buffer是通过哈希表进行管理的,但哈希表中可能因为(block, dev)组合被映射到同一个key下,
                                //所以提供这个同一key下的buffer_head*链表指针,用以二次遍历锁定准确的buffer
        unsigned long b_blocknr; //block number 该buffer映射的磁盘块块号
        unsigned short b_size;    //block size 该buffer映射的磁盘块内容大小
        unsigned short b_list; //
        kdev_t  b_dev; //该buffer映射的磁盘块隶属的虚拟设备标示号
    
        atomic_t b_count; //缓冲区读写使用计数,如果为0,意味着该缓冲区内的内容已经没有线程声明要使用了,意味着该Buffer是可以被释放进入空闲队列了
        kdev_t  b_rdev;   //真实设备标识
        unsigned long b_state; //buffer状态标记位,各位对应不同的含义
        unsigned long b_flushtime; //延迟写的上限时间
    
        struct buffer_head *b_next_free; // lru.free list linkage 指向lru空闲链表中next元素
        struct buffer_head *b_prev_free; //doubly linked list of buffers 指向lru空闲链表中prev元素
        struct buffer_head *b_this_page; //circular list of buffers in one page若该buffer被使用,则该参数指向同一个page的buffer链表
        struct buffer_head *b_reqnext;   //request queue
    
        struct buffer_head **b_pprev; //doubly linked list of hash-queue hash队列双向链表
        char *b_data; //pointer to data block 指向数据块的指针
        struct page *b_page;  //the page this bh is mapped to 这个buffer映射的页面
        void (*b_end_io)(struct buffer_head *bh, int uptodate); //IO completion IO结束时的执行函数_end
        void *b_private;  //reserved for b_end_io 为IO结尾函数_end保留位
    
        unsigned long b_rsector;  //real buffer location on disk 缓冲区在磁盘上的实际位置
        wait_queue_head_t b_wait;  
        struct list_head  b_inode_buffers; //doubly linked list of 
    
        inode dirty buffers//iNode脏缓冲区循环链表
    
    };

    在buffer_head结构体中提到其状态标识位是由unsigned long b_state;来表示的,下面来进一步分析该状态参数各位的意义。

    enum bh_state_bits
    {
        BH_Uptodate, //譬如0x0001,如果缓冲区存在有效数据则置为1,否则为0x0000
        BH_Dirty, //0x0010,如果buffer脏了即数据被修改了,则置该位
        BH_Lock, //如果该缓冲区被锁定了,即存在某一进程或线程正在使用该buffer,则置该位
        BH_Req, //如果缓冲区无效了,则置为该位
        BH_Mapped, //如果缓冲区有一个磁盘映射置该位
        BH_New, //如果该缓冲区是fresh,新分配加入缓冲池的,并且还没有被使用,则置该位;
        BH_Async, //如果缓冲区是进行end_buffer_io_async IO同步则置该位
        BH_Wait_IO, //如果要将这个buffer写回到映射的磁盘中,则置该位
        BH_Launder, //需要重置该buffer,置该位
        BH_Attached, //if b_indoe_buffers is linked into a list则置该位
        BH_JBD, //如果和journal_head 关联置1
        BH_Sync, //如果buffer是同步读取置该位
        BH_Delay, //如果buffer空间是延迟分配置该位
        BH_PrivateStart, //not a state bit, but the first bit available for private allocation by other entities
    };

    从buffer_head和page结构体可以看出,操作系统为了快速定位具体的缓冲块,采用了hash-table进行管理,故而这里介绍下Linux操作系统中关于Buffer缓冲块的hash方式

    /*关于VFS如何管理这几个buffer cache的链表
    *1.其中关于存储着有效数据的buffer,是通过hash表管理的,key值是由数据块号+所在设备标识号计算得到
    */
    
    #define _hashfn (dev, block)       \
            ( (  ((dev) << (bh_hash_shift - 6)) ^ ((dev) << (bh_hash_shift - 9)) ) ^ \
                ( ((block) << (bh_hash_shift - 6)) ^ ((block) >> 13) ^ \
                  ((block) << (bh_hash_shift - 12)) \
                 ) \
            )

    介绍了诸多基础的东西,下面看下Linux下如何具体定位一个Buffer缓冲块的函数bread()。其根据虚拟设备号、缓冲块号以及容量参数进行具体定位。

    //在具体的文件系统中读取具体一块数据时,调用bread函数
    struct buffer_head * bread(kdev_t dev, int block, int size)
    {
        struct  buffer_head * bh;
    
        bh = getblk(dev, block, size);  //根据设备号、块号、和要读取的字节数目返回相应的buffer
    
        if ( buffer_uptodate(bh) ) //判断是否存在有效数据,如果存在那么直接返回即可
            return bh;
    
        set_bit(BH_Sync, &bh->b_state);  //如果不存在有效数据,将这个buffer设置为同步状态
        ll_rw_block(READ, l, &bh);  //如果没有有效数据,则需要现场从磁盘中将相应块号的内容读取到buffer中,这个是一个操作系统底层的操作
    
        wait_on_buffer(bh); //等待buffer的锁打开
        if ( buffer_uptodate(bh) )
            return bh;
        brelse(bh);
        return NULL;
    };

    getblk()函数的具体实现,其根据相应参数返回具体的缓冲块首地址

    struct buffer_head * getblk(kdev_t dev, int block, int size)
    {
        for (;;)
        {
            struct buffer_head * bh;
            bh = get_hash_table(dev, block, size); //关键函数,得到hash表中的buffer
            if (bh) {
                touch_buffer(bh);
                return bh; //返回这个buffer
            }
    
            //如果没有找到对应的buffer,那么试着去增加一个buffer,就是使用下面的grow_buffer函数
            if (!grow_buffers(dev, block, size))  //即调用该函数返回一个足够空间的fresh buffer,用以提供给后面从磁盘读取目标块的内容的缓冲区
                free_more_memory();//如果空间不足,则只能从LRU队列中选出buffer,先看是否已“脏”,若是,则写回磁盘,并清空内容,分配给新的数据块
        }
    };
    #define hash(dev, block) hash_table[ ( _hashfn(HASHDEV(dev), block) & bh_hash_mask ) ]
    #define get_bh(bh)  atomic_inc( &(bh)->b_count )
    
    struct  buffer_head * get_hash_table( kdev_t dev,  int block, int size)
    {
        struct buffer_head *bh;
        struct buffer_head **p = &hash(dev, block); //通过hash表查找到对应的 buffer
    
        read_lock (&hash_table_lock); 
    
        //判断得到的buffer数组中有没有我们需要的buffer
        for(;;) 
        { 
            bh = *p;
            if (!bh)
                break;
    
            p = &bh->b_next;
            if (bh->b_blocknr != block)
                continue;
            if (bh->b_size != size)
                continue;
            if (bh->b_dev != dev)
                continue;
            get_bh(bh); //如果有那么直接执行这个函数,这个函数其实已经通过宏给出
            break;
        }
    
        read_unlock(&hash_table_lock);
        return bh;
    };

    如果缓冲池不够用,则试着增加新的缓冲块,该操作便是通过grow_buffers()函数实现的。

    
    //如果没找到对应的buffer,那么使用grow_buffer函数增加一个新的buffer,该buffer的状态标记为BH_New
    // try to increase the number of buffers available: the size argument is used to determine
    //what kind of buffers we want
    stastic int grow_buffers (kdev_t dev, unsigned long block, int size)
    {
        struct page* page;
        struct block_device *bdev;
        unsigned long index;
        int sizebits;
    
        /**size must be multiple of hard sectorsize 给出的size必须是硬件扇区的整数倍*/
        if (size & (get_hardsect_size(dev) - 1) )
            BUG();
        if (size < 512 || size > PAGE_SIZE )
            BUG();
        //新加入的缓冲块大小必须在512到PAGE_SIZE
    
        sizebits = -1;
        do 
        {
            sizebits++;
        } while ((size << sizebits) < PAGE_SIZE);
    
        index = block >> sizebits;
        block = index << sizebits;
    
        bdev = bdget( kdev_t_to_nr(dev) );
        if (!bdev)
        {
            printfk("No block device for %s\n", kdevname(dev));
            BUG();
        }
    
        /*即根据需求的size新开辟一个缓冲页Page*/
        page = grow_dev_page( bdev, index, size );
    
        atominc_dec( &bdev->bd_count);
        if (!page)
            return 0;
    
        /*Hash in the buffers on the hash list*/
        hash_page_buffers( page, dev, block, size);
        UnlockPage( page );
        page_cache_release( page );
    
        /* we hashed up this page, so increment buffermem*/
        atomic_inc( &buffermem_pages );
        return 1;
    }

    前面说到我们是通过hash-table来管理已经被填入有效数据的缓冲区buffer的,但是其实缓冲区类型是由多种的

    #define BUF_CLEAN  0
    #define BUF_LOCKED 1 //正在等待被写回的uffer: Buffers scheduled for write
    #define BUF_DIRTY  2 //脏buffer,但还没有被安排写回,即延迟写策略不满足
    #define NR_DIRTY   3

    而事实上,缓冲池中更新策略一个重要的概念便是LRU(least recently used最近最少使用)。而缓冲池的具体实现中除了empty\input\output三种队列,还有便是LRU队列控制的淘汰缓冲队列以及hash-table提供的快速索引。

    综合来说,Linux系统为IO读取操作配备的buffer缓冲池是这样起作用的:

    1. 首先在Hash-table中寻找目标buffer,如果找到了该buffer,则直接返回该buffer的buffer_head指针,如果没有,那么意味着要读取的内容并不在buffer缓冲池中,故而要为这份新的数据内容分配一块新的匹配size的buffer;

    2. 先从内存中再直接划分出一块区域作为新添加的缓冲块,加入缓冲池体系中,供应本次使用;

    3. 如果内存空间不足或者已经达到缓冲池规模上限,则开始从LRU队列中取出链首元素,先看是否脏了,如果脏了,则先回写,然后清空内容,将它分配给新的数据块。

    展开全文
  • 本文将介绍用于备受青睐的反激式转换器的最常用的RCD箝位电路,及其设计公式。  没有缓冲器,反激式变压器振铃的漏感会随电路中的杂散电容产生,生产大幅度高频波形,如图所示1。  许多应用笔记和设计没有解决这...
  • 摘要:液晶是嵌入式设备最常用的显示器件。本文详细介绍了用双缓冲的方式实现图形液晶的快速显示。  1. 引言  在嵌入式设备中,液晶已经成为显示器件的首选。液晶类型有字符和图形之分,字符液晶便宜且显示速度...
  • QT_双缓冲技术1

    千次阅读 2013-03-05 13:51:55
    Rubber band(橡皮筋线,或者橡皮线), pixmap(图像,双缓冲中用到的图像,有时也直呼pixmap),off-screen pixmap(离线图像) ...双缓冲技术是GUI编程中常用的技术。所谓的双缓冲就是把把一个需要渲染的
    Rubber band(橡皮筋线,或者橡皮线), pixmap(图像,双缓冲中用到的图像,有时也直呼pixmap),off-screen pixmap(离线图像)
    Plot(plot,这一节实现的就是一个绘制曲线的控件Plotter,有时原文也叫plot,有点小名的意思,没有翻译,直接呼之)
    废话少说,以下是译文:
     
    双缓冲技术是GUI编程中常用的技术。所谓的双缓冲就是把把一个需要渲染的控件保存到一个离线图像(off-screen pixmap)中,然后再把图像拷贝到需要绘制的控件上。在Qt的早期版本中,为了用户界面更加清爽,经常用这个技术来消除闪烁。
    在Qt4中,QWidget能够自动处理闪烁,因此我们不用再担心这个问题。尽管如此,如果控件渲染复杂且需要经常刷新,双缓冲技术还是很有用的。我们可以把控件永久保存在一个图像中,随时准备下一次绘制事件的到来,一旦接到一个控件的绘制事件,就把图片拷贝到控件上。如果我们要做的只是小范围的修改,这个技术更是尤为有用,如要绘制一条橡皮筋线,就不必一次次刷新整个控件了。
    在本章的最后一节,我们实现的是一个叫做Plotter的自定义控件。这个控件使用了双缓冲技术,也涉及到了Qt编程的其他方面:如键盘的事件处理,布局和坐标系统。
    Plotter控件用来显示一条或者多条曲线,这些曲线由一组向量坐标表示。用户可以在图像上画一个橡皮筋线(Rubeber band),Plotter控件对橡皮筋线包围的区域进行放大。用户用鼠标左键在控件上选择一个点,然后拖动鼠标走到另一点,然后释放鼠标,就在控件上绘制一条橡皮筋线。
    Figure 5.7 Zooming in on the Plotter Widget
      
    用户可以多次用橡皮筋线进行放大,也可以用ZoomOut按钮缩小,然后用ZoomIn按钮再放大。ZoomOut和ZoomIn按钮只是在控件第一次放大或者缩小操作后变得可见,如果用户不缩放图形,则这两个按钮会一直不可见,这样可以使绘图区域不那么混乱。
    Plotter控件可以存储任何数量的曲线的数据。同时它还维护一个PlotSettings对象的栈区域,每一个PlotSettings对象都是对应一个特定的缩放值。
    首先看一下头文件的代码(对头文件的解析在代码中用注释的形式给出):
    #ifndef PLOTTER_H
    #define PLOTTER_H 
    #include 
    <QMap>//包含的Qt的头文件
    #include <QPixmap>
    #include 
    <QVector>
    #include 
    <QWidget> 
    class QToolButton; //两个前向声明
    class PlotSettings;  
    class Plotter : public QWidget
    {
        Q_OBJECT
    public:
        Plotter(QWidget 
    *parent = 0);
        
    void setPlotSettings(const PlotSettings &settings);
        
    void setCurveData(int id, const QVector<QPointF> &data);//QPointF表明是浮点类型的QPoint
        
    void clearCurve(int id);
        QSize minimumSizeHint() 
    const//重写QWidget::minimumSizeHint()
        QSize sizeHint() const;        //重写QWidget::sizeHint()
    public slots:
        
    void zoomIn();   //放大曲线
        void zoomOut();   //缩小显示曲线
    protected:  //重新实现的QWidget的事件处理函数
        void paintEvent(QPaintEvent *event);
        void resizeEvent(QResizeEvent *event);
        void mousePressEvent(QMouseEvent *event);
        void mouseMoveEvent(QMouseEvent *event);
        void mouseReleaseEvent(QMouseEvent *event);
        void keyPressEvent(QKeyEvent *event);
        void wheelEvent(QWheelEvent *event);
    private:
        
    void updateRubberBandRegion();
        
    void refreshPixmap();
        
    void drawGrid(QPainter *painter);
        
    void drawCurves(QPainter *painter);
        
    enum { Margin = 50 };//表明图象周围的空间
        QToolButton 
    *zoomInButton;
        QToolButton 
    *zoomOutButton;
        QMap
    <int, QVector<QPointF> > curveMap;  //曲线数据
        QVector<PlotSettings> zoomStack;   //PlotSettings栈区域
        int curZoom;
        
    bool rubberBandIsShown;
        QRect rubberBandRect;
        QPixmap pixmap; 
    //显示在屏幕的控件的一个拷贝,任何绘制总是先绘制在离线pixmap上,然后拷贝到控件上
    };
    //PlotSettings确定x,y轴的范围,和刻度的个数
    class PlotSettings
    {
    public:
        PlotSettings();

        
    void scroll(int dx, int dy);
        
    void adjust();
        
    double spanX() const { return maxX - minX; }
        
    double spanY() const { return maxY - minY; }

        
    double minX;
        
    double maxX;
        
    int numXTicks;
        
    double minY;
        
    double maxY;
        
    int numYTicks;

    private:
        
    static void adjustAxis(double &min, double &max, int &numTicks);
    };
    #endif
     
    图5-8表示了Plotter控件和PlotSettings的关系。
    通常,numXTicks和numYTicks是有一个的误差,如果numXTicks为5,实际上Plotter会在x轴上绘制6个刻度。这样可以简化以后的计算(至于怎么样简化的,就看程序和后文吧)。
    Figure 5-8 PlotSettings's member variables
    现在来看源文件(代码有些长,先用代码格式给出完整源文件代码):
    #include <QtGui>
    #include 
    <cmath>

    #include 
    "plotter.h"

    Plotter::Plotter(QWidget 
    *parent)
        : QWidget(parent)
    {
        setBackgroundRole(QPalette::Dark);
        setAutoFillBackground(
    true);
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
        setFocusPolicy(Qt::StrongFocus);
        rubberBandIsShown 
    = false;

        zoomInButton 
    = new QToolButton(this);
        zoomInButton
    ->setIcon(QIcon(":/images/zoomin.png"));
        zoomInButton
    ->adjustSize();
        connect(zoomInButton, SIGNAL(clicked()), 
    this, SLOT(zoomIn()));

        zoomOutButton 
    = new QToolButton(this);
        zoomOutButton
    ->setIcon(QIcon(":/images/zoomout.png"));
        zoomOutButton
    ->adjustSize();
        connect(zoomOutButton, SIGNAL(clicked()), 
    this, SLOT(zoomOut()));

        setPlotSettings(PlotSettings());
    }

    void Plotter::setPlotSettings(const PlotSettings &settings)
    {
        zoomStack.clear();
        zoomStack.append(settings);
        curZoom 
    = 0;
        zoomInButton
    ->hide();
        zoomOutButton
    ->hide();
        refreshPixmap();
    }

    void Plotter::zoomOut()
    {
        
    if (curZoom > 0) {
            
    --curZoom;
            zoomOutButton
    ->setEnabled(curZoom > 0);
            zoomInButton
    ->setEnabled(true);
            zoomInButton
    ->show();
            refreshPixmap();
        }
    }

    void Plotter::zoomIn()
    {
        
    if (curZoom < zoomStack.count() - 1) {
            
    ++curZoom;
            zoomInButton
    ->setEnabled(curZoom < zoomStack.count() - 1);
            zoomOutButton
    ->setEnabled(true);
            zoomOutButton
    ->show();
            refreshPixmap();
        }
    }

    void Plotter::setCurveData(int id, const QVector<QPointF> &data)
    {
        curveMap[id] 
    = data;
        refreshPixmap();
    }

    void Plotter::clearCurve(int id)
    {
        curveMap.remove(id);
        refreshPixmap();
    }

    QSize Plotter::minimumSizeHint() 
    const
    {
        
    return QSize(6 * Margin, 4 * Margin);
    }

    QSize Plotter::sizeHint() 
    const
    {
        
    return QSize(12 * Margin, 8 * Margin);
    }

    void Plotter::paintEvent(QPaintEvent * /* event */)
    {
        QStylePainter painter(
    this);
        painter.drawPixmap(
    00, pixmap);
        
    if (rubberBandIsShown) {
            painter.setPen(palette().light().color());
            painter.drawRect(rubberBandRect.normalized()
                                           .adjusted(
    00-1-1));
        }
        
    if (hasFocus()) {
            QStyleOptionFocusRect option;
            option.initFrom(
    this);
            option.backgroundColor 
    = palette().dark().color();
            painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
        }
    }

    void Plotter::resizeEvent(QResizeEvent * /* event */)
    {
        
    int x = width() - (zoomInButton->width()
                           
    + zoomOutButton->width() + 10);
        zoomInButton
    ->move(x, 5);
        zoomOutButton
    ->move(x + zoomInButton->width() + 55);
        refreshPixmap();
    }
    void Plotter::resizeEvent(QResizeEvent * /* event */)
    {
        
    int x = width() - (zoomInButton->width()
                           
    + zoomOutButton->width() + 10);
        zoomInButton
    ->move(x, 5);
        zoomOutButton
    ->move(x + zoomInButton->width() + 55);
        refreshPixmap();
    }
    void Plotter::resizeEvent(QResizeEvent * /* event */)
    {
        
    int x = width() - (zoomInButton->width()
                           
    + zoomOutButton->width() + 10);
        zoomInButton
    ->move(x, 5);
        zoomOutButton
    ->move(x + zoomInButton->width() + 55);
        refreshPixmap();
    }

    void Plotter::mousePressEvent(QMouseEvent *event)
    {
        QRect rect(Margin, Margin,
                   width() 
    - 2 * Margin, height() - 2 * Margin);
        
    if (event->button() == Qt::LeftButton) {
            
    if (rect.contains(event->pos())) {
                rubberBandIsShown 
    = true;
                rubberBandRect.setTopLeft(
    event->pos());
                rubberBandRect.setBottomRight(
    event->pos());
                updateRubberBandRegion();
                setCursor(Qt::CrossCursor);
            }
        }
    }
    void Plotter::mouseMoveEvent(QMouseEvent *event)
    {
        
    if (rubberBandIsShown) {
            updateRubberBandRegion();
            rubberBandRect.setBottomRight(
    event->pos());
            updateRubberBandRegion();
        }
    }
    void Plotter::mouseReleaseEvent(QMouseEvent *event)
    {
        
    if ((event->button() == Qt::LeftButton) && rubberBandIsShown) {
            rubberBandIsShown 
    = false;
            updateRubberBandRegion();
            unsetCursor();
            QRect rect 
    = rubberBandRect.normalized();
            
    if (rect.width() < 4 || rect.height() < 4)
                
    return;
            rect.translate(
    -Margin, -Margin);
            PlotSettings prevSettings 
    = zoomStack[curZoom];
            PlotSettings settings;
            
    double dx = prevSettings.spanX() / (width() - 2 * Margin);
            
    double dy = prevSettings.spanY() / (height() - 2 * Margin);
            settings.minX 
    = prevSettings.minX + dx * rect.left();
            settings.maxX 
    = prevSettings.minX + dx * rect.right();
            settings.minY 
    = prevSettings.maxY - dy * rect.bottom();
            settings.maxY 
    = prevSettings.maxY - dy * rect.top();
            settings.adjust();
            zoomStack.resize(curZoom 
    + 1);
            zoomStack.append(settings);
            zoomIn();
        }
    }

    void Plotter::keyPressEvent(QKeyEvent *event)
    {
        
    switch (event->key()) {
        
    case Qt::Key_Plus:
            zoomIn();
            
    break;
        
    case Qt::Key_Minus:
            zoomOut();
            
    break;
        
    case Qt::Key_Left:
            zoomStack[curZoom].scroll(
    -10);
            refreshPixmap();
            
    break;
        
    case Qt::Key_Right:
            zoomStack[curZoom].scroll(
    +10);
            refreshPixmap();
            
    break;
        
    case Qt::Key_Down:
            zoomStack[curZoom].scroll(
    0-1);
            refreshPixmap();
            
    break;
        
    case Qt::Key_Up:
            zoomStack[curZoom].scroll(
    0+1);
            refreshPixmap();
            
    break;
        
    default:
            QWidget::keyPressEvent(
    event);
        }
    }

    void Plotter::wheelEvent(QWheelEvent *event)
    {
        
    int numDegrees = event->delta() / 8;
        
    int numTicks = numDegrees / 15;
        
    if (event->orientation() == Qt::Horizontal) {
            zoomStack[curZoom].scroll(numTicks, 
    0);
        } 
    else {
            zoomStack[curZoom].scroll(
    0, numTicks);
        }
        refreshPixmap();
    }
    void Plotter::updateRubberBandRegion()
    {
        QRect rect 
    = rubberBandRect.normalized();
        update(rect.left(), rect.top(), rect.width(), 
    1);
        update(rect.left(), rect.top(), 
    1, rect.height());
        update(rect.left(), rect.bottom(), rect.width(), 
    1);
        update(rect.right(), rect.top(), 
    1, rect.height());
    }
    void Plotter::refreshPixmap()
    {
        pixmap 
    = QPixmap(size());
        pixmap.fill(
    this00);
        QPainter painter(
    &pixmap);
        painter.initFrom(
    this);
        drawGrid(
    &painter);
        drawCurves(
    &painter);
        update();
    }

    void Plotter::drawGrid(QPainter *painter)
    {
        QRect rect(Margin, Margin,
                   width() 
    - 2 * Margin, height() - 2 * Margin);
        
    if (!rect.isValid())
            
    return;
        PlotSettings settings 
    = zoomStack[curZoom];
        QPen quiteDark 
    = palette().dark().color().light();
        QPen light 
    = palette().light().color();
        
    for (int i = 0; i <= settings.numXTicks; ++i) {
            
    int x = rect.left() + (i * (rect.width() - 1)
                                     
    / settings.numXTicks);
            
    double label = settings.minX + (i * settings.spanX()
                                              
    / settings.numXTicks);
            painter
    ->setPen(quiteDark);
            painter
    ->drawLine(x, rect.top(), x, rect.bottom());
            painter
    ->setPen(light);
            painter
    ->drawLine(x, rect.bottom(), x, rect.bottom() + 5);
            painter
    ->drawText(x - 50, rect.bottom() + 510015,
                              Qt::AlignHCenter 
    | Qt::AlignTop,
                              QString::number(label));
        }
        
    for (int j = 0; j <= settings.numYTicks; ++j) {
            
    int y = rect.bottom() - (j * (rect.height() - 1)
                                       
    / settings.numYTicks);
            
    double label = settings.minY + (j * settings.spanY()
                                              
    / settings.numYTicks);
            painter
    ->setPen(quiteDark);
            painter
    ->drawLine(rect.left(), y, rect.right(), y);
            painter
    ->setPen(light);
            painter
    ->drawLine(rect.left() - 5, y, rect.left(), y);
            painter
    ->drawText(rect.left() - Margin, y - 10, Margin - 520,
                              Qt::AlignRight 
    | Qt::AlignVCenter,
                              QString::number(label));
        }
        painter
    ->drawRect(rect.adjusted(00-1-1));
    }

    void Plotter::drawCurves(QPainter *painter)
    {
        
    static const QColor colorForIds[6= {
            Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::yellow
        };
        PlotSettings settings 
    = zoomStack[curZoom];
        QRect rect(Margin, Margin,
                   width() 
    - 2 * Margin, height() - 2 * Margin);
        
    if (!rect.isValid())
            
    return;
        painter
    ->setClipRect(rect.adjusted(+1+1-1-1));
        QMapIterator
    <int, QVector<QPointF> > i(curveMap);
        
    while (i.hasNext()) {
            i.next();
            
    int id = i.key();
            
    const QVector<QPointF> &data = i.value();
            QPolygonF polyline(data.count());
            
    for (int j = 0; j < data.count(); ++j) {
                
    double dx = data[j].x() - settings.minX;
                
    double dy = data[j].y() - settings.minY;
                
    double x = rect.left() + (dx * (rect.width() - 1)
                                             
    / settings.spanX());
                
    double y = rect.bottom() - (dy * (rect.height() - 1)
                                               
    / settings.spanY());
                polyline[j] 
    = QPointF(x, y);
            }
            painter
    ->setPen(colorForIds[uint(id) % 6]);
            painter
    ->drawPolyline(polyline);
        }
    }


    PlotSettings::PlotSettings()
    {
        minX 
    = 0.0;
        maxX 
    = 10.0;
        numXTicks 
    = 5;
        minY 
    = 0.0;
        maxY 
    = 10.0;
        numYTicks 
    = 5;
    }

    void PlotSettings::scroll(int dx, int dy)
    {
        
    double stepX = spanX() / numXTicks;
        minX 
    += dx * stepX;
        maxX 
    += dx * stepX;
        
    double stepY = spanY() / numYTicks;
        minY 
    += dy * stepY;
        maxY 
    += dy * stepY;
    }

    void PlotSettings::adjust()
    {
        adjustAxis(minX, maxX, numXTicks);
        adjustAxis(minY, maxY, numYTicks);
    }

    void PlotSettings::adjustAxis(double &min, double &max,
                                  
    int &numTicks)
    {
        
    const int MinTicks = 4;
        
    double grossStep = (max - min) / MinTicks;
        
    double step = pow(10.0, floor(log10(grossStep)));
        
    if (5 * step < grossStep) {
            step 
    *= 5;
        } 
    else if (2 * step < grossStep) {
            step 
    *= 2;
        }
        numTicks 
    = int(ceil(max / step) - floor(min / step));
        
    if (numTicks < MinTicks)
            numTicks 
    = MinTicks;
        min 
    = floor(min / step) * step;
        max 
    = ceil(max / step) * step;
    }
    展开全文
  • 本文较深入地讨论了两种常用模式的RCD Snubber电路:抑制电压上升率模式与电压钳位模式,详细分析了其各自的工作原理,给出了相应的计算公式,最后...图1 常用的RCD Snubber电路  抑制电压上升率模式  对于功率MO
  • 1. LRU 调度算法 ...其实常用的LRU算法是LRU-K算法体系下LRU-1特例算法。LRU算法的核心归根结底还是在链表基础上改装而来。其运行逻辑如下: 1.新数据插入链表首部(最新被使用的数据,优先级最高,位于链首

    1. LRU 调度算法

    缓存淘汰算法:LRU(Least recently used,最近最少使用)算法是调度场景下(内存调度、缓存淘汰等)常用到的算法,其原理是根据数据的最近访问时间来安排数据淘汰的顺序。其实常用的LRU算法是LRU-K算法体系下LRU-1特例算法。

    LRU算法的核心归根结底还是在链表基础上改装而来。其运行逻辑如下:
    1.新数据插入链表首部(最新被使用的数据,优先级最高,位于链首)
    2.每当存在LRU缓存队列中的其他元素被命中,则将该元素提高链首;
    3.当链表满时,即意味着容量不足,淘汰链表尾部的元素,腾出空间。

    性能分析
    当整个系统存在热点(二八原则),LRU效率很好,可以充分利用缓存优势,缓存中数据命中率始终较高,但当系统存在周期性的批量操作(周期步长正好超过了链表长度)则会导致LRU缓存体系命中率很低,几乎需要一直更新缓存内容,偶发性密集操作也会导致缓存池体系污染严重。这也是因为K取1,导致数据惯性较低,很容易被偶发操作引入的新数据从缓冲池挤掉。

    2. LRU-K 调度算法

    LRU可以认为是LRU-1算法,但是可能存在“缓存污染”的问题,故而为了应对这种冲击,出现了LRU-K,其中K代表最近使用过K次,从而增强体系对偶发性操作的适应性。LRU-K除了需要维护一个缓冲队列,还要维护一个历史访问次数记录队列。即LRU-K是由:可快速检索定位的历史列表(如hash-table) + LRU缓冲队列

    1.数据历史访问计数: 当数据第一次被访问,加入到访问历史列表中,历史记录列表可能是使用hash-table或者红黑树管理的数据结构;
    2.升级进入LRU队列:如果数据在历史访问列表中的访问总次数达到K次,则将数据索引项从历史记录中删除,将数据迁移到缓存队列中,置为链首
    3.周期性检查:如果过了一段时间,历史访问列表中的某项数据依旧没有达到K次,则删除该数据项在历史列表中的记录(限期淘汰原则)
    4.LRU末尾淘汰: 需要从缓存队列中腾出空位时,则淘汰链表尾部的数据。

    SouthEast
    性能分析
    LRU-K相比LRU具有对于偶发性密集操作的对抗性,有效地降低“缓存污染”问题。但是LRU-K需要维护的数据记录较多(历史访问记录+缓存队列),显然历史访问记录列表存在时间复杂度和空间复杂度不可均得的情况,一般是采用hash-table这类数据结构实现历史访问列表,以空间换取时间。故而空间消耗更大,并且由于存在诸如周期性检查这类操作,导致时间复杂度也较LRU更复杂。

    实际应用中来说还是LRU-2是综合效果更优的选择,LRU-3可以识缓存命中率更高,但是适应性较差,K越大一旦面对使用场景切换过渡效果很差。

    3. LRU-2的改进型:双队列调度算法

    2Q算法类似于LRU-2算法,但是和LRU-2算法中的访问历史记录队列改成了FIFO队列,即2Q算法位两个缓存队列:FIFO历史次数记录队列+LRU队列。这一改进主要是针对LRU-K算法在历史访问列表的性能短板。

    2Q算法是针对LRU-2进行针对性的改善,对于其他LRU-K算法并不通用,2Q的原理是:数据第一次被访问时,数据时被缓存在FIFO队列中,当数据第二次被访问到时,则将数据从FIFO队列中迁移到LRU队列中。

    1.新访问的数据被插入到FIFO队列中;
    2.如果数据在FIFO队列中一直没有被再次访问,则最终按照FIFO规则淘汰(淘汰周期由FIFO队列的长度决定,符合缓冲淘汰的使用习惯);
    3.如果数据在FIFO队列中被第二次访问到,则将该数据迁移到LRU队列链首
    4.如果数据在LRU队列中被再次访问到,则将该数据迁移到LRU队列链首
    5.LRU队列容量不足时,则淘汰末尾的数据

    性能分析
    2Q算法和LRU算法类似,但唯一不同的是LRU-2算法的历史队列中存放的是数据索引,并非真正的数据块,故而2Q算法比LRU-2算法少一次从磁盘读取数据块到buffer的迁移操作(即LRU-2是数据项满足要求时,才正式将该数据块加入缓存,而历史队列中保存的是数据索引项,而2Q算法在历史记录队列中便是保存着数据块内容,一旦要从历史队列迁移到缓存LRU队列中,无需再次读取磁盘)

    4. Multiple Queue多级缓冲队列算法

    在2Q算法基础上衍生出更复杂的Multi Queue(MQ)。MQ算法的核心思想是访问次数越多的数据的优先级越高,其存储的缓存buffer所处的级别就应该越高,越不应该被偶发的密集操作给从缓存体系中挤出去。

    1 新插入的数据放入优先级最低的Q0;
    2 每个队列都是按照LRU管理数据
    3 当数据的访问次数达到一定次数,需要提升优先级,则将数据加入到高一级的队列头部;
    4 位了防止高优先级的数据永远不被淘汰,当数据在指定的时间内没有被访问时,则需要降低优先级,将数据从高优先级队列中降级到低一级的LRU队列首部;
    5 需要淘汰数据时,从最低一级队列按照LRU淘汰,每个队列淘汰数据时,将数据从缓存中删除,但是将数据索引项加入到Q-history队列头部。
    6 如果数据在Q-history中被重新访问,则重新计算其优先级,移到目标队列的头部
    7 Q-history也是按照LRU原则淘汰数据的索引项
    这里写图片描述
    性能分析
    MQ是尽可能地降低“缓存污染”问题,但是MQ需要维护多个队列,且还需要记录每个数据的访问时间,时间复杂度明显要比LRU高,并且存在着定期更新不同缓冲块的优先级的扫描任务,需要定时扫描所有队列,这一额外任务决定了MQ的管理代价比LRU要高,虽然MQ队列看起来要多,但是缓存容量是有上限的,故而和LRU-1队列容量一样。

    5. LRU算法实现

    摘自org_apache_tomcat项目中关于LRU的实现部分代码

    public class LRUCache 
    {
        //org.apache.tomcat.util.collections.LRUCache
        private int cacheSize; //LRU队列的最大容量
        private Hashtable nodes;//除了LRU链表管理各buffer块,为了加速找到个buffer的,将各buffer_head指针用哈希表来管理
        private int currentSize;//当前LRU队列中存在元素个数
        private CacheNode first;//LRU链首
        private CacheNode last; //LRU链尾
    
        class CacheNode
        {
            CacheNode prev; //buffer的前部指针
            CacheNode next; //buffer的后部指针
            Object value; //数据块中存储的数据
            Object key; //hash-table中的hash-key
            CacheNode() {}
        }
    
        public LRUCache(int i)
        {
            currentSize = 0;
            cacheSize = i;
            nodes = new Hashtable(i); //缓存容器
        }
    
        public  Object get(Object key)
        {
            CacheNode node = (CacheNode) nodes.get(key);
            if (node != null) 
            {
                moveToHead(node);
                return node.value;
            }
            else
                return null;
        }
    
        //添加新的缓存buffer
        public void put(Object key, Object value)
        {
            CacheNode node = (CacheNode)nodes.get(key);//按照提供的hash-key先去hash-table查找,看是否需要覆盖
    
            if (node == null) //如果hash-table中映射出来的索引值位空,则意味着该元素是第一次加入buffer
            {
                if (currentSize >= cacheSize)//意味着LRU队列已经满了,需要删除LRU末尾元素
                {
                    if (last != null) //将最少使用的删除
                        nodes.remove(last.key);//将hash-table中的想一个元素索引值给删除掉
                    removeLast();
                }
                else
                    currentSize++;
    
                node = new CacheNode();
            }
            node.value = value;
            node.key = key;
            moveToHead(node);//将该元素提到LRU队列的链首
            nodes.put(key, node); //将该元素加入到hash-table中
        }
    
        public Object remove(Object key)
        {
            CacheNode node = (CacheNode) nodes.get(key); //为了快速定位到目标buffer块,先去hash-table获取索引值
    
            if (node != null)
            {
                if (node.prev != null)
                    node.prev.next = node.next;
    
                if (node.next != null)
                    node.next.prev = node.prev;
    
                if (last == node)
                    last = node.prev;
    
                if (first == node)
                    first = node.next;
            }
    
            return node;
        }
    
        public void clear()
        {
            first = null;
            last = null;
        }
    
        private void removeLast() 
        {
            if (last != null)
            {
                if (last.prev != null)
                    last.prev.next = null;
                else
                    first = null;
    
                last = last.prev;
            }
        }
    
        private void moveToHead(CacheNode node)
        {
            if (node == null)
                return;
            if (node.prev != null)
                node.prev.next = node.next;
            if (node.next != null )
                node.next.prev = node.prev;
            if (last == node)
                last = node.prev;
            if (first != null)
            {
                node.next = first;
                first.prev = node;
            }
    
            first = node;
            node.prev = null;
            if (last = null)
                last = first;
        }
    }
    展开全文
  • http://www.jianshu.com/p/efc0bebfd22eAndroid绘图中缓冲再绘图中我们常用到两个重要工具。一个是Canvas(画布),一个是Paint(画笔)。 Canvas提供画各种图形方法,如画圆(drawCircle),画矩形...
  • (转)双缓冲技术1

    千次阅读 2008-05-20 20:32:00
    off-screen pixmap(离线图像)Plot(plot,这一节实现的就是一个绘制曲线的控件Plotter,有时原文也叫plot,有点小名的意思,没有翻译,直接呼之)废话少说,以下是译文: 双缓冲技术是GUI编程中常用的技术。...
  • 以下是MSComm控件的常用属性和方法:  ●Commport:设置或返回串口号。  ●Settings:以字符串形式设置或返回串口通信参数。  ●Portopen:设置或返回串口状态。  ●InputMode:设置或返回接收数据类型...
  • 为了迅速建立起零拷贝的概念,我们拿一个常用的场景进行引入:引文在写一个服务端程序时(Web Server或者文件服务器),文件下载是一个基本功能。这时候服务端的任务是:将服务端主机磁盘中的文件不做修改地从已连...
  • 减低内存销毁和防止内存泄露隐患是事件委托技术的两项好处,其基本要义是: 并不是集合内每一个元素都要登记上事件处理器,而是在集合其容器上登记 一次便可,这样产生了中央化一个事件处理器,然后就有不断...
  • 考虑到阅读方便和网络速度,打算把这节分为5个部分,第一部分为双缓冲技术的一个简介和所有代码,如果能够看懂代码,不用看译文也就可以了。第二部分为Plotter控件公有函数实现,第三部分为Plotter事件...
  • ------Java培训、Android...进行字节传输的流被称为字节流,其中,FileInputStrean和FileOutputStream是java中最常用的两个字节流。FileInputStream是字节读取流,FileOutputStream是字节输入流。 二、FileInputStre
  • C++编程语言互换流中标准输入流,需要iostream.h支持,程序输入都建有一个缓冲区,即输入缓冲区。一次输入过程是这样,当一次键盘输入结束时会将输入数据存入输入缓冲区,而cin函数直接从输入缓冲区中取数据...
  • 考虑到阅读方便和网络速度,打算把这节分为5个部分,第一部分为双缓冲技术的一个简介和所有代码,如果能够看懂代码,不用看译文也就可以了。第二部分为Plotter控件公有函数实现,第三部分为Plotter事件...
  • 当然,我们常用的PC都是支持双缓冲技术的。 要启动双缓冲功能,最简单的办法就是使用GLUT工具包。我们以前在main函数里面写: glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);  其中GLUT_SIN
  • 线程是进程内一个相对独立、可调度执行单元。一个应用可以有一个主线程,一个主线程可以有多个子线程,子线程还可以有自己子线程,这样就构成了多线程应用了。... 最近在做数据模块缓冲池中就需要
  • Qt 双缓冲绘图

    2017-10-11 11:21:07
    原文地址::... 相关文章 ... ...双缓冲技术是GUI 编程中常用的技术。所谓的双缓冲就是把需要绘制的控件保存到一个图像中,然后在把图像拷贝到需要绘制的控件上。在Qt 的早期
  • 缓冲与应用优化

    2017-11-16 20:16:00
    缓冲(buffering):用来弥补不同数据处理速率速度差距的存储技术缓冲在日常生活中无处不在,人的身体就有许多缓冲的例子,如吃东西的嘴巴,因咽喉大小有限,嘴巴会先将...我们常用的计算机硬盘,尽管通道技术...
  • 摘要:在分析无源无损缓冲电路的拓扑分类和硬开关转换过程中开关损耗的基础... 有源缓冲电路、RCD缓冲电路、谐振变换器、无源无损缓冲电路是常用的软开关技术。其中,有源缓冲电路通过增添辅助开关以减少开关损耗,但
  • 在这篇文章里,我想解释一些我们喜欢的Softice中最不常用的命令。此文的目的是想给出这个优秀的工具能为我们带来的更加强大的功能。原因很简单:大部分破解者(一般是新手)对于这个Debugger能做的事情不甚了解。这...
  • 连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。 解释:连接池就是数据库、JDBC使用的一种创建和管理连接的缓冲池的技术,就是把数据库与服务器的那些增删查改需要的连接放进...
  • 具体介绍压力传感器在使用中要注意事项 ,希望可以帮助到大家。压力传感器在使用中要 注意事项1....这个输出一般为mV级电压,如果超出了传感器的技术指标,就说明传 感器零点偏差超范围。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 483
精华内容 193
关键字:

常用的缓冲技术是