乐鑫代码中Ring Buffers (环形缓冲区)说明

2023-05-16

本文翻译自乐鑫文档,详见链接地址,结合用例自行测试,欢迎交流。源码链接ringbuf.c

项目中用乐鑫的wifi模组,esp8266,esp32中发现ringbuf的身影,效率应该不错,决定研究一下,网上没有发现相关资料,后来发现官方的一篇相关文档,在我研究相关文档和代码后,逐渐了解部分,在其struct Ringbuffer_t结构体中,五个相关指针,分别指向头,尾,读,写,free,开始看到free一脸懵,这个指针干啥用的,为啥要用呢,后来发现 prvReturnItemByteBuf 这个函数中,直接把read指针赋值给free,free之后才可以写入,我也不清楚这样的好处,可能向malloc free看齐吧(捂脸),知道的方便的话可以告知一下。


ringbuf.c中先看了 xRingbufferCreate 这个入口函数,传入需要长度和缓冲区类型,赋值相应的解析函数,可以先看RINGBUF_TYPE_BYTEBUF这个类型,就相当于一个环形队列吧,另外两个每次送入的数据都要加一个头记录数据长度,每次数据都隔离开了,根据write的tail的长度,可以选择允许或则不允许分割,对应的函数也是不同的。
再看一下发送函数 prvCopyItemByteBuf     读取数据函数 prvGetItemByteBuf 了解一下大概
 

struct Ringbuffer_t {
    size_t xSize;                               //Size of the data storage
    UBaseType_t uxRingbufferFlags;              //Flags to indicate the type and status of ring buffer
    size_t xMaxItemSize;                        //Maximum item size

    CheckItemFitsFunction_t xCheckItemFits;     //Function to check if item can currently fit in ring buffer
    CopyItemFunction_t vCopyItem;               //Function to copy item to ring buffer
    GetItemFunction_t pvGetItem;                //Function to get item from ring buffer
    ReturnItemFunction_t vReturnItem;           //Function to return item to ring buffer
    GetCurMaxSizeFunction_t xGetCurMaxSize;     //Function to get current free size

    uint8_t *pucWrite;                          //Write Pointer. Points to where the next item should be written
    uint8_t *pucRead;                           //Read Pointer. Points to where the next item should be read from
    uint8_t *pucFree;                           //Free Pointer. Points to the last item that has yet to be returned to the ring buffer
    uint8_t *pucHead;                           //Pointer to the start of the ring buffer storage area
    uint8_t *pucTail;                           //Pointer to the end of the ring buffer storage area

    BaseType_t xItemsWaiting;                   //Number of items/bytes(for byte buffers) currently in ring buffer that have not yet been read
    SemaphoreHandle_t xFreeSpaceSemaphore;      //Binary semaphore, wakes up writing threads when more free space becomes available or when another thread times out attempting to write
    SemaphoreHandle_t xItemsBufferedSemaphore;  //Binary semaphore, indicates there are new packets in the circular buffer. See remark.
};

Ring Buffers(环形缓冲区)

ESP-IDF FreeRTOS环形缓冲区是严格的FIFO缓冲区,支持任意大小的项目。在项目大小可变的情况下,环形缓冲区是FreeRTOS队列的一种内存效率更高的替代方法。环形缓冲区的容量不是由它可以存储的项目数来衡量的,而是由用于存储项目的内存量来衡量的。您可以在环形缓冲区上申请一块内存来发送项目,或者只是使用API​​复制数据并发送(根据您调用的send API)。出于效率原因, 总是通过引用从环形缓冲区中检索项目。因此,还必须返回所有检索到的项,以便将其从环形缓冲区中完全删除。环形缓冲区分为以下三种类型:

No-Split (无分割): 缓冲区将确保将项目存储在连续内存中,并且在任何情况下都不会尝试分割项目。当项目必须占用连续内存时,请使用不拆分缓冲区。仅此缓冲区类型允许您自己获取数据项地址并写入该项。

Allow-Split(允许分割)缓冲区将允许在包装时拆分项目,如果这样做将允许存储该项目。允许拆分缓冲区比无拆分缓冲区具有更高的内存效率,但是在检索时可以分两部分返回一个项目。

Byte buffers(字节缓冲区)不将数据存储为单独的项目。所有数据均按字节顺序存储,任意数量的字节都可以每次发送或检索。当不需要维护单独的项(例如字节流)时,请使用字节缓冲区。注解

注:
No-split/allow-split分割缓冲区将始终将项目存储在32位对齐的地址处。因此,在检索项目时,保证项目指针是32位对齐的。这在需要向DMA发送一些数据时特别有用。

存储在无拆分/允许拆分缓冲区中的每个项目都需要为标头额外增加8个字节。项目大小也将四舍五入为32位对齐的大小(4字节的倍数),但是真实的项目大小记录在标头中。创建时不拆分/允许拆分的缓冲区的大小也会四舍五入。

用法

下面的示例演示的用法xRingbufferCreate() 和xRingbufferSend()创建环形缓冲区,然后向其发送项目的过程。

#include "freertos/ringbuf.h"
static char tx_item[] = "test_item";

...

    //Create ring buffer
    RingbufHandle_t buf_handle;
    buf_handle = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT);
    if (buf_handle == NULL) {
        printf("Failed to create ring buffer\n");
    }

    //Send an item
    UBaseType_t res =  xRingbufferSend(buf_handle, tx_item, sizeof(tx_item), pdMS_TO_TICKS(1000));
    if (res != pdTRUE) {
        printf("Failed to send item\n");
    }

 

下面的例子说明使用的xRingbufferSendAcquire()和 xRingbufferSendComplete(),而不是xRingbufferSend()申请的(类型环形缓冲存储器 RINGBUF_TYPE_NOSPLIT ),然后发送项目给它。这种方法增加了一个步骤,但允许获取要写入的内存地址,以及自己写入内存。

#include "freertos/ringbuf.h"
#include "soc/lldesc.h"

typedef struct {
    lldesc_t dma_desc;
    uint8_t buf[1];
} dma_item_t;

#define DMA_ITEM_SIZE(N) (sizeof(lldesc_t)+(((N)+3)&(~3)))

...

    //Retrieve space for DMA descriptor and corresponding data buffer
    //This has to be done with SendAcquire, or the address may be different when copy
    dma_item_t item;
    UBaseType_t res =  xRingbufferSendAcquire(buf_handle,
                        &item, DMA_ITEM_SIZE(buffer_size), pdMS_TO_TICKS(1000));
    if (res != pdTRUE) {
        printf("Failed to acquire memory for item\n");
    }
    item->dma_desc = (lldesc_t) {
        .size = buffer_size,
        .length = buffer_size,
        .eof = 0,
        .owner = 1,
        .buf = &item->buf,
    };
    //Actually send to the ring buffer for consumer to use
    res = xRingbufferSendComplete(buf_handle, &item);
    if (res != pdTRUE) {
        printf("Failed to send item\n");
    }

 

以下示例演示了 使用和no-split ring buffer检索和返回项目。xRingbufferReceive()vRingbufferReturnItem()

...

    //Receive an item from no-split ring buffer
    size_t item_size;
    char *item = (char *)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(1000));

    //Check received item
    if (item != NULL) {
        //Print item
        for (int i = 0; i < item_size; i++) {
            printf("%c", item[i]);
        }
        printf("\n");
        //Return Item
        vRingbufferReturnItem(buf_handle, (void *)item);
    } else {
        //Failed to receive item
        printf("Failed to receive item\n");
    }

 

下面的示例演示了 使用和从allow-split ring buffer检索和返回项目。xRingbufferReceiveSplit()vRingbufferReturnItem()

...

    //Receive an item from allow-split ring buffer
    size_t item_size1, item_size2;
    char *item1, *item2;
    BaseType_t ret = xRingbufferReceiveSplit(buf_handle, (void **)&item1, (void **)&item2, &item_size1, &item_size2, pdMS_TO_TICKS(1000));

    //Check received item
    if (ret == pdTRUE && item1 != NULL) {
        for (int i = 0; i < item_size1; i++) {
            printf("%c", item1[i]);
        }
        vRingbufferReturnItem(buf_handle, (void *)item1);
        //Check if item was split
        if (item2 != NULL) {
            for (int i = 0; i < item_size2; i++) {
                printf("%c", item2[i]);
            }
            vRingbufferReturnItem(buf_handle, (void *)item2);
        }
        printf("\n");
    } else {
        //Failed to receive item
        printf("Failed to receive item\n");
    }

 

以下示例演示了 使用和从 byte buffer检索和返回项目xRingbufferReceiveUpTo()vRingbufferReturnItem()

...

    //Receive data from byte buffer
    size_t item_size;
    char *item = (char *)xRingbufferReceiveUpTo(buf_handle, &item_size, pdMS_TO_TICKS(1000), sizeof(tx_item));

    //Check received data
    if (item != NULL) {
        //Print item
        for (int i = 0; i < item_size; i++) {
            printf("%c", item[i]);
        }
        printf("\n");
        //Return Item
        vRingbufferReturnItem(buf_handle, (void *)item);
    } else {
        //Failed to receive item
        printf("Failed to receive item\n");
    }

 

对于上面使用的功能ISR安全版本,通话xRingbufferSendFromISR()xRingbufferReceiveFromISR(), xRingbufferReceiveSplitFromISR()xRingbufferReceiveUpToFromISR(),和vRingbufferReturnItemFromISR()

 

Sending to Ring Buffer

下图说明了在发送项目/数据方面 no-split/allow-split (无拆分/允许拆分)缓冲区和字节缓冲区之间的区别。这些图假定大小分别为18、3和27字节的三个项目分别发送到128个字节缓冲区中

 

                                                将项目发送到 no-split/allow-split ring buffers

对于不拆分/允许拆分的缓冲区,每个数据项之前都有一个8字节的标头。此外,每个项目占用的空间都将四舍五入到最接近的32位对齐大小,以保持整体32位对齐。但是,项目的真实大小会记录在标头中,当检索到项目时将返回标头。

参考上图,将18、3和27个字节的项分别舍入20、4和28个字节 。然后在每个项目的前面添加一个8字节的标头。

                                                                   将项目发送到  byte buffers

字节缓冲区将数据视为字节序列,并且不会产生任何开销(没有标头)。所有发送到字节缓冲区的数据都合并为一个项目。

参考上图,将18、3和27个字节项依次写入字节缓冲区,并合并为48个字节的单个项

 

SendAcquire和SendComplete

不拆分缓冲区中的项目按严格的FIFO顺序获取(通过SendAcquire),并且必须由SendComplete发送到缓冲区中,以便消费者可以访问数据。可以在不调用SendComplete的情况下发送或获取多个项目,并且不一定需要按照获取顺序完成这些项目。但是,数据项的接收必须以FIFO顺序进行,因此,不调用最早获取的项来调用SendComplete将阻止后续项的接收。

下图说明了SendAcquire / SendComplete不按相同顺序发生时将发生的情况。在开始时,已经有16字节的数据项发送到环形缓冲区。然后调用SendAcquire以在环形缓冲区上获取20、8、24字节的空间。

                                            无拆分环形缓冲区中的SendAcquire / SendComplete项目

之后,我们填充(使用)缓冲区,并通过SendComplete以8、24、20的顺序将它们发送到环形缓冲区。当发送8字节和24字节数据时,使用者仍然只能获得16字节数据项目。由于使用情况,如果20个字节的项目不完整,则该项目不可用,以下数据项目也不可用。

当最后完成20个字节的项目后,就可以立即接收20个,8个,24个字节的所有3个数据项,紧接在开始时存在于缓冲区中的16个字节的项目之后。

允许分割/字节缓冲区不允许使用SendAcquire / SendComplete,因为需要完整(不包装)获取的缓冲区。

 

Wrap around (环绕)

下图说明了当发送的项目需要跨过尾部和头部组成环形时, no-split, allow-split, byte buffers之间的区别。这些图假定有一个128字节的缓冲区, 其中有56字节的可用空间可以环绕,发送的项目为28字节

                                              Wrap around in no-split buffers(在无拆分缓冲区中环绕)

无拆分缓冲区将仅将项目存储在连续的可用空间中,并且在任何情况下都不会拆分项目。当缓冲区尾部的可用空间不足以完全存储项目及其标头时,尾部的可用空间将被标记为虚拟数据。然后,缓冲区将包裹起来并将项目存储在缓冲区顶部的可用空间中。

参考上图,缓冲区尾部的16个字节的可用空间不足以存储28个字节的项。因此,将这16个字节标记为虚拟数据,并将该项目写入缓冲区开头的可用空间。

                                                               在允许分割的缓冲区中

当缓冲区尾部的可用空间不足以存储项目数据及其标题时,允许拆分缓冲区将尝试将项目分为两部分。拆分项目的两个部分都有自己的标头(因此会产生额外的8个字节的开销)。

参考上图,缓冲区尾部的16个字节的可用空间不足以存储28个字节的项。因此,该项目分为两部分(8和20字节),并分为两部分写入缓冲区。

注解

Allow-split buffers(允许拆分缓冲区)将拆分项的两个部分都视为两个单独的项,因此调用 xRingbufferReceiveSplit()而不xRingbufferReceive()是以线程安全的方式接收拆分项的两个部分。

 

                                                      在字节缓冲区中

字节缓冲区会将尽可能多的数据存储到缓冲区尾部的可用空间中。然后,剩余的数据将存储在缓冲区开头的可用空间中。在字节缓冲区中回绕时不会产生开销。

参考上图,缓冲区尾部的16个字节的可用空间不足以完全存储28个字节的数据。因此,16个字节的可用空间被数据填充,其余12个字节被写入缓冲区开头的可用空间。现在,缓冲区包含两个独立的连续部分中的数据,并且字节缓冲区会将连续的每个部分视为一个单独的项目。

Retrieving/Returning(检索/返回)

下图说明了在获取和返回数据时无拆分/允许拆分和字节缓冲区之间的区别。

 

                                                 在不拆分/允许拆分的环形缓冲区中检索/返回项目

以严格的FIFO顺序检索无拆分/允许拆分缓冲区中的项目,必须将其返回 以释放占用的空间。可以在返回之前检索多个项目,并且不一定需要按照检索顺序返回这些项目。但是,释放空间必须按FIFO顺序进行,因此不返回最早检索到的项目将阻止释放后续项目的空间。

参考上图,以FIFO顺序检索16、20和8字节的项目。但是,在检索项目时不会返回它们(20、8、16)。这样,直到返回第一项(16字节)后,空间才会释放。

                                                              在字节缓冲区中检索/返回数据

字节缓冲区不允许在返回之前进行多次检索(每次检索之后都必须先返回,然后才允许再次进行检索)。使用xRingbufferReceive()或时 xRingbufferReceiveFromISR(),将检索所有连续存储的数据。xRingbufferReceiveUpTo() 或xRingbufferReceiveUpToFromISR()可用于限制检索到的最大字节数。由于每次检索后都必须有返回值,因此一旦返回数据,空间将被释放。

参照上图,检索,返回和释放缓冲区尾部的38个字节的连续存储数据。下一次调用xRingbufferReceive()xRingbufferReceiveFromISR() 环绕,然后对缓冲区开头的30个字节的连续存储数据进行相同的处理。

具有队列集的环形缓冲区

可以使用以下方式将环形缓冲区添加到FreeRTOS队列集中xRingbufferAddToQueueSetRead(),使得每次环形缓冲区接收到项或数据时,都会通知队列集。一旦添加到队列集中,每次从环形缓冲区中检索项目的尝试都应先调用xQueueSelectFromSet()。要检查所选队列集成员是否为环形缓冲区,请调用xRingbufferCanRead()

下面的示例演示了环形缓冲区与队列集的用法。

#include "freertos/queue.h"
#include "freertos/ringbuf.h"

...

    //Create ring buffer and queue set
    RingbufHandle_t buf_handle = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT);
    QueueSetHandle_t queue_set = xQueueCreateSet(3);

    //Add ring buffer to queue set
    if (xRingbufferAddToQueueSetRead(buf_handle, queue_set) != pdTRUE) {
        printf("Failed to add to queue set\n");
    }

...

    //Block on queue set
    xQueueSetMemberHandle member = xQueueSelectFromSet(queue_set, pdMS_TO_TICKS(1000));

    //Check if member is ring buffer
    if (member != NULL && xRingbufferCanRead(buf_handle, member) == pdTRUE) {
        //Member is ring buffer, receive item from ring buffer
        size_t item_size;
        char *item = (char *)xRingbufferReceive(buf_handle, &item_size, 0);

        //Handle item
        ...

    } else {
        ...
    }

具有静态分配的环形缓冲区

所述xRingbufferCreateStatic()可用于产生具有特定的内存要求(如环形缓冲器在外部RAM中被分配)环缓冲器。环形缓冲区使用的所有内存块必须事先手动分配,然后传递xRingbufferCreateStatic()给进行初始化,以作为环形缓冲区。这些块包括:

  • 环形缓冲区的数据结构类型 StaticRingbuffer_t
  • 环形缓冲区的存储区域为size xBufferSize。请注意,xBufferSize对于非拆分/允许拆分的缓冲区,必须为32位对齐。

分配这些块的方式将取决于用户要求(例如,静态声明所有块,或使用特定功能(例如外部RAM)动态分配所有块)。

注解

该CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION选项必须启用menuconfig的对静态分配的缓冲区环是可用的。

删除通过创建的环形缓冲区时xRingbufferCreateStatic(),该函数vRingbufferDelete()将不会释放任何存储块。vRingbufferDelete()调用后必须由用户手动完成。

 

下面的代码片段演示了环形缓冲区完全在外部RAM中分配。

#include "freertos/ringbuf.h"
#include "freertos/semphr.h"
#include "esp_heap_caps.h"

#define BUFFER_SIZE     400      //32-bit aligned size
#define BUFFER_TYPE     RINGBUF_TYPE_NOSPLIT
...

//Allocate ring buffer data structure and storage area into external RAM
StaticRingbuffer_t *buffer_struct = (StaticRingbuffer_t *)heap_caps_malloc(sizeof(StaticRingbuffer_t), MALLOC_CAP_SPIRAM);
uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(sizeof(uint8_t)*BUFFER_SIZE, MALLOC_CAP_SPIRAM);

//Create a ring buffer with manually allocated memory
RingbufHandle_t handle = xRingbufferCreateStatic(BUFFER_SIZE, BUFFER_TYPE, buffer_storage, buffer_struct);

...

//Delete the ring buffer after used
vRingbufferDelete(handle);

//Manually free all blocks of memory
free(buffer_struct);
free(buffer_storage);

 

环形缓冲区API参考

注解

理想情况下,环形缓冲区可以以SMP方式与多个任务一起使用,其中始终优先为优先级最高的任务提供服务。但是,由于在环形缓冲区的基础实现中使用了二进制信号量,因此在非常特殊的情况下可能发生优先级倒置。

环形缓冲区控制着二进制信号量的发送,二进制信号量是在环形缓冲区上的空间释放时给出的。等待发送的最高优先级任务将反复获取信号量,直到有足够的可用空间可用或超时为止。理想情况下,这应避免为任何优先级较低的任务提供服务,因为信号量应始终分配给优先级最高的任务。

但是,在获取信号量的两次迭代之间,关键部分中存在间隙,这可能允许另一个任务(在另一个内核上或具有更高的优先级)释放环形缓冲区上的某些空间,从而提供信号量。因此,将在优先级最高的任务可以重新获取信号之前给出信号。这将导致信号量被等待发送的第二高优先级任务获取,从而导致优先级倒置。

如果同时使用环形缓冲区的任务数量很少,并且环形缓冲区未在最大容量附近运行,则这种副作用不会严重影响环形缓冲区的性能。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

乐鑫代码中Ring Buffers (环形缓冲区)说明 的相关文章

  • 我的2013-从GIS学生到GIS职业人的飞跃

    我的 2013 从 GIS 学生GIS 职业人的飞跃 前言 xff1a 从末日中度过了 2012 年 xff0c 我们伟大的人类把这个世界末日的谎言给揭穿了 xff0c 但是不知不觉中 xff0c 2013 年又即将悄悄从我们身边溜走 xf
  • 矩阵的特征值和特征向量的雅克比算法C/C++实现

    矩阵的特征值和特征向量是线性代数以及矩阵论中非常重要的一个概念 在遥感领域也是经常用到 xff0c 比如多光谱以及高光谱图像的主成分分析要求解波段间协方差矩阵或者相关系数矩阵的特征值和特征向量 根据普通线性代数中的概念 xff0c 特征值和
  • windows多线程详解

    在一个牛人的博客上看到了这篇文章 xff0c 所以就转过来了 xff0c 地址是http blog csdn net morewindows article details 7421759 本文将带领你与多线程作第一次亲密接触 xff0c
  • tiff文件读取

    以下是VC下读取TIFF文件的代码 char szFileName 61 34 K 地图 fujian DEM fujian1 tif 34 TIFF tiff 61 TIFFOpen szFileName 34 r 34 打开Tiff文件
  • GIS开发人员需要掌握的知识和技能

    对于GIS行业 xff0c 可能很多人不是很了解 xff0c 对我来说也不是很了解 xff0c 在此呢 xff0c 我就我自己的看法发表一下简单的看法 xff0c 有什么不同的意见可以一起交流 GIS虽说是属于地理科学或者说测绘科学与技术的
  • GIS算法的一点理解

    在GIS这个专业也混了好几年了 xff0c 但是始终没有对GIS算法有过真正的研究 xff0c 可以说大部分不懂 目前关于GIS算法的书籍不是特别多 xff0c 数来数去也就那么几本 xff0c 南师大几个老师编写的地理信息系统算法基础 x
  • char*转LPCWSTR解决方案

    在Windows编程中 xff0c 经常会碰到字符串之间的转换 xff0c char 转LPCWSTR也是其中一个比较常见的转换 下面就列出几种比较常用的转换方法 1 通过MultiByteToWideChar函数转换 MultiByteT
  • 一阶互补滤波,二阶互补滤波,卡尔曼滤波

    一阶互补 a 61 tau tau 43 loop time newAngle 61 angle measured with atan2 using the accelerometer 加速度传感器输出值 newRate 61 angle
  • 项目实用makefile

    在上一篇文章 小项目实用makefile 中 xff0c 已经说明了单个makefile管理层次目录的局限性 本文 xff0c 主要总结一下项目中的一种实用makefile树写法 xff0c 为10来个人协作的中小型项目makefile编写
  • MPU6050工作原理及STM32控制MPU6050

    一 简介 1 要想知道MPU6050工作原理 xff0c 得先了解下面俩个传感器 xff1a 陀螺仪传感器 xff1a 陀螺仪的原理就是 xff0c 一个旋转物体的 旋转轴所指的方向在不受外力影响时 xff0c 是不会改变的 人们根据这个道
  • 自动驾驶(四十九)---------Kavser二次开发

    我们知道CAN总线是连接车身各个模块之间的桥梁 xff0c 通过协议通讯 xff0c 在车辆标定和测试中很多情况是用上位机和车身相连 xff0c 收发满足CAN总线的信号 这中间如何通讯呢 xff1f 这就需要用到Kavser Kvaser
  • Linux嵌入式设备时钟同步到硬件

    时间修改命令 date s 34 2022 06 27 11 51 02 34 同步到硬件 hwclock w 显示硬件时钟 hwclock r
  • linux opendir和readdir的使用

    1 opendir include lt sys types h gt include lt dirent h gt DIR opendir const char name 传入name路径 xff0c 成功则返回非空DIR指针 xff0c
  • 【Mybatis】No enum constant org.apache.ibatis.type.JdbcType.LONG

    问题描述 xff1a 今天编写定时任务管理模块 xff0c 提交定时任务实体信息时 xff0c 提示如下错误 nested exception is org apache ibatis builder BuilderException Er
  • ego-planner论文阅读笔记

    ESDF Euclidean Signed Distance Field EGO ESDF free Gradient based lOcal planning framework 摘要 通过比较碰撞轨迹与无碰撞引导路径 xff0c 得到惩
  • 冒泡排序,选择排序,插入排序的比较

    冒泡排序与选择排序相比 xff0c 一个从局部入手减少逆序元素 xff0c 一个放眼大局逐个选择最小值 xff0c 二者思路大不相同 但是 xff0c 它们又都有着 通过i次外层循环 xff0c 从数据中顺次求出i个最小值 的相同特征 相对
  • 【操作系统】2.3 进程同步与互斥

    这一节大概是操作系统中最难的一节了 2 3 1 进程的同步与互斥 2 3 1 进程的同步与互斥 StudyWinter的博客 CSDN博客 进程同步思维导图 进程同步 xff1a 在多道程序环境下 xff0c 进程是并发执行的 xff0c
  • 【算法】递增子序列

    总结一下三道求子序列长度的题 1 最长递增子序列 300 最长递增子序列 给你一个整数数组 nums xff0c 找到其中最长严格递增子序列的长度 子序列 是由数组派生而来的序列 xff0c 删除 xff08 或不删除 xff09 数组中的
  • 【算法】单调栈的题

    记一次笔试题 描述 给定一个长度为 nn 的可能含有重复值的数组 numsnums xff0c 找到每一个位置 ii 左边最近的位置 ll 和右边最近的位置 rr xff0c nums lnumsl 和 nums rnumsr 比 nums
  • Skip List--跳表(全网最详细的跳表文章没有之一)

    笔者目前是CPP方向 xff0c 今年 xff08 2023届 xff09 秋招时在简历中写的就是跳表的项目 xff0c 当时是啃源码啃下的 xff0c 把跳表整体的思路是理顺了 但是在面试过程中 xff0c 有不少面试官都对这个项目很感兴

随机推荐

  • 报错:Caused by: org.xml.sax.SAXParseException

    Caused by org xml sax SAXParseException 文档根元素 34 project 34 必须匹配 DOCTYPE 根 34 null 34 错误提示 xff1a gframework beans factor
  • 用malloc动态申请一个二维数组

    利用二级指针申请一个二维数组 define CRT SECURE NO WARNINGS include lt iostream gt include lt vector gt include lt algorithm gt using n
  • 有符号/无符号整数相加溢出的判断方法

    1 有符号数相加溢出判断 1 1 两个有符号的数是正数 当两个有符号整数x y同为正数 xff0c 且x 43 y的结果为非正时 xff0c 发生了正溢出 define CRT SECURE NO WARNINGS include lt i
  • 【算法】求最小子集的和被5整除

    昨天面试了一家公式 xff0c 面试上来问我 xff0c 使用过哪些STL容器 xff0c 我说了一下 xff0c 然后又问从最简单的开始说 面试官 xff1a 说说使用vector是需要注意什么 xff1f 我 xff1a 注意什么 迭代
  • TCP报文结构

    TCP报文结构 TCP报文由俩部分组成 xff1a TCP报头和TCP数据 TCP报文是TCP传输的数据单元 端口号 xff1a 用来标识一台主机的不同进程 1 xff09 源端端口号 xff1a 源端口和IP层解析出来的IP地址标识报文的
  • 【算法】洗牌算法

    碎碎念 xff1a 面试的时候 xff0c 让写这道题 xff0c 快写完了 xff0c 面试官告诉我 xff0c 这是洗牌算法 xff0c 1 介绍 洗牌算法是将原来的数组进行打散 xff0c 使原数组的某个数在打散后的数组中的每个位置上
  • 【操作系统】第一章总结

    1 1 操作系统介绍 1 1 1 操作系统的概念 功能和目标 1 1 1 操作系统的概念 功能和目标 xff08 系统资源的管理者 提供接口 作为扩充机器 虚拟机 xff09 StudyWinter的博客 CSDN博客 1 1 2 操作系统
  • 【操作系统】2.1 进程与线程总结

    2 1 1 操作系统之进程的定义 特征 组成 组织 2 1 1 操作系统之进程的定义 特征 组成 组织 StudyWinter的博客 CSDN博客 进程由程序段 数据段 进程控制块 xff08 PCB xff09 三部分组成 进程是进程实体
  • 【操作系统】2.2 操作系统的调度

    2 2 1 操作系统之处理机调度的概念及层次 2 2 1操作系统之处理机调度的概念及层次 StudyWinter的博客 CSDN博客 操作系统调度的层次 高级调度 xff08 作业调度 xff09 xff1a 外存 内存 中级调度 xff0
  • 【操作系统】2.4 死锁

    这一节也非常重要 2 4 1 死锁的概念 2 4 1 死锁的概念 StudyWinter的博客 CSDN博客 在并发环境下 xff0c 各种进程因竞争资源而造成的一种互相等待对方手里的资源 xff0c 导致各进程都阻塞 xff0c 都无法向
  • 3.1.1 什么是内存?进程的基本原理,深入指令理解其过程

    0 思维导图 1 什么是内存 xff1f 有何作用 xff1f xff08 1 xff09 存储单元 内存 Memory 是计算机的重要部件 xff0c 也称内存储器和主存储器 xff0c 它用于暂时存放CPU中的运算数据 xff0c 以及
  • 怎样用AT指令查询本机手机号码

    分享一下我老师大神的人工智能教程 xff01 零基础 xff0c 通俗易懂 xff01 http blog csdn net jiangjunshow 也欢迎大家转载本篇文章 分享知识 xff0c 造福人民 xff0c 实现我们中华民族伟大
  • 3.1.2 操作系统内存管理管些什么?

    0 思维导图 1 内存空间的分配与回收 2 内存空间的扩展 xff08 实现虚拟性 xff09 3 地址转换 4 内存保护 1 操作系统负责内存空间的分配和回收 xff1b 2 操作系统需要提供某种技术从逻辑上对内存空间进行扩充 xff1b
  • C++11特性

    最近打算照着源文档学习C 43 43 特性 xff0c 先从C 43 43 11的特性开始 学习 xff1a C 43 43 11 cppreference com C 43 43 11 C 43 43 11是继C 43 43 98之后的最
  • C++11特性之auto

    占位符类型说明 对变量而言 xff0c 指定要声明的变量的类型将从其初始值中自动推导出来 对于函数而言 xff0c 指定的返回类型将从其返回语句中推导出来 xff08 从C 43 43 14开始 xff09 对于非模版类型的参数而言 xff
  • C++11特性之decltype

    检查一个实体的声明类型或者检查表达式类型和值类别 语法 strong decltype strong entity strong strong 1 since C 43 43 11 strong decltype strong expres
  • 【算法】151. 反转字符串中的单词

    链接 xff1a https leetcode cn problems reverse words in a string 给你一个字符串 s xff0c 请你反转字符串中 单词 的顺序 单词 是由非空格字符组成的字符串 s 中使用至少一个
  • TensorFlow中numpy与tensor数据相互转化

    numpy与tensor数据相互转化 xff1a Numpy2Tensor 虽然TensorFlow网络在输入Numpy数据时会自动转换为Tensor来处理 xff0c 但是我们自己也可以去显式的转换 xff1a data tensor 6
  • ESP32环境搭建 HomeKit 平台搭建 苹果智能家居HomeKit

    本文所有操作环境在linux下 xff0c 参考README文档 xff0c 官方sdk获取连接 https github com espressif esp apple homekit adk HomeKit是Apple开发的框架 xff
  • 乐鑫代码中Ring Buffers (环形缓冲区)说明

    本文翻译自乐鑫文档 xff0c 详见链接地址 xff0c 结合用例自行测试 xff0c 欢迎交流 源码链接ringbuf c 项目中用乐鑫的wifi模组 xff0c esp8266 xff0c esp32中发现ringbuf的身影 xff0