FreeRTOSFreeRTOS列表和列表项

FreeRTOS列表和列表项

今天继续跟着正点原子学习FreeRTOS列表和列表项的内容。列表和列表项这个知识点用到了C语言链表的知识点。所以必须对C语言中的链表这个数据结构才能更好的理解这部分内容。TIPS:正点原子这节课内容讲的特别好,强烈推荐:第20讲列表和列表项简介

什么是列表和列表项

列表就是FreeRTOS中的数据结构,它被用来跟踪FreeRTOS中的任务。主要的列表包括:

  1. 就绪列表(Ready Lists):对于每个优先级,FreeRTOS都维护一个就绪列表。当任务处于就绪状态,能够运行,但由于CPU正被其他任务使用而无法立即执行时,该任务会被放入相应优先级的就绪列表中。当CPU可用时,调度器会从最高优先级的就绪列表中选择任务来执行。

  2. 阻塞列表(Blocked Lists):当任务等待某个事件(如信号量、互斥量或定时器)时,它会被放入阻塞列表。阻塞列表用于跟踪哪些任务正在等待资源变得可用。当等待的事件发生时,任务可以从阻塞列表移回就绪列表。

  3. 挂起列表(Suspended List):当任务被显式挂起(例如,调用了vTaskSuspend函数)时,它会被放入挂起列表。挂起的任务不会被调度器考虑运行,直到它们被显式地恢复(例如,调用了vTaskResume)。

  4. 延时列表(Delay Lists):当任务需要延迟执行或等待一段时间时,它会被放入延时列表。这通常用于实现非阻塞延时或定时功能。延时列表实际上分为两个:一个用于短期延时(当前延时列表),另一个用于长期延时(溢出延时列表)。这种分法帮助管理时间的回绕问题。

  5. 终止列表(Terminated List):某些FreeRTOS配置允许已删除或已结束的任务被放入终止列表,直到它们的任务控制块(TCB)被回收。这不是所有FreeRTOS配置都支持的功能。
    每个列表都是通过链表实现的,每个链表节点都包含了指向任务控制块(Task Control Block, TCB)的指针。TCB是FreeRTOS用来存储任务状态、堆栈指针和其他必要信息的结构。
    FreeRTOS的调度器会根据这些列表及其内任务的状态和优先级来决定哪个任务应该获得CPU时间。通过这种方式,FreeRTOS实现了任务的优先级调度和时间共享调度。与列表有关的内容都在list.c和list.h中。在list.h中定义一个叫做List_t的结构体。下面我们来看一下:

typedef struct xLIST
{listFIRST_LIST_INTEGRITY_CHECK_VALUE      /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */volatile UBaseType_t uxNumberOfItems;ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list.  Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */MiniListItem_t xListEnd;                  /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */listSECOND_LIST_INTEGRITY_CHECK_VALUE     /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;

在FreeRTOS中,List_t结构体是用于实现各种任务列表(如就绪列表、阻塞列表等)的核心数据结构。下面是List_t结构体各个成员的作用解释:

  1. listFIRST_LIST_INTEGRITY_CHECK_VALUE:这是一个可选的完整性检查值,仅在configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时使用。它的目的是帮助开发者检测内存损坏或不正确的列表操作。这个值通常被设置为一个特定的常量,以便在运行时检查列表的完整性。

  2. volatile UBaseType_t uxNumberOfItems:这个成员变量用来记录列表中项目的数量。它是volatile类型的,因为它可能会在中断服务例程和普通任务代码之间共享,确保编译器在每次访问时都会从内存中重新读取其值,而不是使用可能已经过时的寄存器副本。

  3. ListItem_t * configLIST_VOLATILE pxIndex:这是一个指向列表中某个项目的指针,用作遍历列表的索引。configLIST_VOLATILE是一个宏,用于确保在特定的编译器或配置下,pxIndex以正确的方式被视为volatilepxIndex通常用于通过listGET_OWNER_OF_NEXT_ENTRY()宏来遍历列表,每次调用都会返回列表中的下一个元素。

  4. MiniListItem_t xListEnd:这个成员是列表的一个哨兵(或标记)元素,表示列表的末尾。xListEndxItemValue通常被设置为最大可能的值,以确保它总是位于列表的末尾。这个哨兵元素使得列表操作(如插入和删除)可以在不需要特殊情况处理的情况下进行,因为列表始终至少包含一个元素(即xListEnd)。

  5. listSECOND_LIST_INTEGRITY_CHECK_VALUE:这是第二个可选的完整性检查值,其作用与listFIRST_LIST_INTEGRITY_CHECK_VALUE相同,提供额外的完整性检查。这也只有在configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时才会启用。
    至于第四点为啥要有一个哨兵元素,如果有链表操作知识的基础,在没有哨兵元素的链表中,插入和删除操作需要检查列表是否为空,以及操作是否发生在列表的开头或末尾。这增加了实现的复杂性。使用哨兵元素后,这些特殊情况的处理被统一化,因为列表永远不会为空(至少有一个哨兵元素),且不需要单独处理列表的末尾。统一插入和删除操作:哨兵元素确保每个插入和删除操作都有一个统一的前驱节点和后继节点,无论操作发生在列表的哪个位置。这意味着代码可以使用相同的逻辑来处理所有情况。

在这里插入图片描述
所以列表中忽略那两个检查元素,所有的具体的成员如上图所示。
接下来让我们来看下列表项和迷你列表项。其中迷你列表项就是xlistend,也就是列表标记的哨兵元素。首先来看下列表项和迷你列表项所拥有成员的差异,具体差异如下图所示。
在这里插入图片描述
可以看出迷你列表项只有三个成员。xMINI_LIST_ITEM结构体是FreeRTOS中用来表示列表中的一个元素或节点的数据结构。它是ListItem_t的简化版本,通常用于实现哨兵元素(如xListEnd),以简化列表操作。下面是xMINI_LIST_ITEM结构体每个成员的详细解释:

. configLIST_VOLATILE TickType_t xItemValue:这个成员变量用于存储元素的排序值或优先级。TickType_t通常是一个无符号整型,用于表示时间或者计数。在FreeRTOS中,列表经常根据这个值进行排序,例如,用于管理延时任务的列表就是按照xItemValue的值(表示唤醒时间)进行排序的。configLIST_VOLATILE是一个宏,用于确保在特定的编译器或配置下,xItemValue以正确的方式被视为volatile。这是因为xItemValue可能会在中断服务例程中被修改。

struct xLIST_ITEM * configLIST_VOLATILE pxNext:这是一个指向列表中下一个元素的指针。它使得列表可以以链表的形式进行遍历。configLIST_VOLATILE确保pxNext在需要时被正确处理为volatile,以适应可能的并发修改。

struct xLIST_ITEM * configLIST_VOLATILE pxPrevious:这是一个指向列表中前一个元素的指针。它的存在使得列表成为一个双向链表,允许从任一方向遍历列表。这对于某些操作,如从列表中删除一个元素,是非常有用的,因为可以直接访问前一个元素,从而简化了指针的重新链接过程。同样,configLIST_VOLATILE确保在特定配置下,pxPrevious被正确地视为volatile
至于为啥非要整出来一个迷你列表项,而不直接全部都用列表项。这可能会让人疑惑它们之间的区别以及为什么需要两种不同的结构体。主要区别通常在于它们的用途和设计意图,而不仅仅是结构体成员本身。下面是创建迷你列表项的原因。

1. 简化和优化

xMINI_LIST_ITEMListItem_t的简化版本。虽然它们的成员变量可能相似,xMINI_LIST_ITEM通常用于特定场景,如作为哨兵节点(xListEnd)或在不需要完整ListItem_t功能的场合。这种简化有助于减少内存占用和提高代码效率,尤其是在资源受限的嵌入式系统中。

2. 特定用途

xMINI_LIST_ITEM通常用于实现列表的哨兵节点,这是一个始终存在于列表末尾的特殊节点,用以简化列表操作逻辑。而ListItem_t则用于表示实际的列表数据项。这种区分使得代码更加清晰,逻辑更加简单。

3. 减少复杂性

在某些情况下,列表项可能不需要ListItem_t提供的所有功能。例如,哨兵节点不需要存储额外的数据(如所属任务的指针或其他用户定义数据)。在这种情况下,使用更简单的xMINI_LIST_ITEM可以减少实现的复杂性,同时保持足够的功能性。

4. 内存效率

在资源受限的嵌入式系统中,每个字节的内存都很宝贵。xMINI_LIST_ITEM由于其简化的设计,占用的内存可能比ListItem_t少。在只需要基本链表功能(如哨兵节点)时,使用xMINI_LIST_ITEM可以节省宝贵的系统资源。
那了解了迷你列表项和成员变量和其作用后下面我们来了解下,列表项中各个成员的作用。
具体代码如下:

struct xLIST_ITEM
{listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE           /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */configLIST_VOLATILE TickType_t xItemValue;          /*< The value being listed.  In most cases this is used to sort the list in ascending order. */struct xLIST_ITEM * configLIST_VOLATILE pxNext;     /*< Pointer to the next ListItem_t in the list. */struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */void * pvOwner;                                     /*< Pointer to the object (normally a TCB) that contains the list item.  There is therefore a two way link between the object containing the list item and the list item itself. */struct xLIST * configLIST_VOLATILE pxContainer;     /*< Pointer to the list in which this list item is placed (if any). */listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE          /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t;                   /* For some reason lint wants this as two separate definitions. */

ListItem_t(或struct xLIST_ITEM)是FreeRTOS中用于构建双向链表的核心数据结构之一。它比xMINI_LIST_ITEM更为复杂,提供了更多的功能和灵活性。

  1. listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE: 当configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时,此成员被设置为一个已知的值,用于运行时检查列表项的完整性。这有助于开发者识别内存损坏或不当的列表操作,增加了代码的健壮性。

  2. configLIST_VOLATILE TickType_t xItemValue: 这是列表项的值,通常用于排序列表。在大多数情况下,列表是按照xItemValue的升序排列的。例如,FreeRTOS的延时任务列表就是根据任务唤醒时间(用xItemValue表示)进行排序的。TickType_t是一个基于配置的类型,通常为无符号整型,代表时间或计数。configLIST_VOLATILE确保此成员在需要时被视为volatile,适用于可能在中断服务例程中修改的场景。

  3. struct xLIST_ITEM * configLIST_VOLATILE pxNext: 指向链表中下一个ListItem_t的指针。这使得可以从当前列表项向后遍历整个列表。

  4. struct xLIST_ITEM * configLIST_VOLATILE pxPrevious: 指向链表中前一个ListItem_t的指针。这使得可以从当前列表项向前遍历整个列表,是双向链表结构的关键组成部分。

  5. void * pvOwner: 指向包含此列表项的对象的指针。通常,这个指针指向一个任务控制块(TCB),但也可以指向其他使用列表项的数据结构。这实现了对象和其所属列表项之间的双向链接。

  6. struct xLIST * configLIST_VOLATILE pxContainer: 指向包含此列表项的列表的指针。这个成员变量使得可以从列表项访问其所属的列表,进而实现诸如移除列表项或调整列表结构等操作。

  7. listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE: 类似于listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE,这是另一个完整性检查值,当configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时使用。它位于结构体的末尾,为列表项提供额外的内存完整性验证。
    上面已经介绍完了列表、列表项、迷你列表项的结构以及成员变量,接下来来看下如何形成双向链表。这一点视频里面讲的很好。直接来看视频这个地方:
    在这里插入图片描述
    其实双向环形链表就是三个人拉成一个圈一样,小明的右手指向小黑的左手,小明的左手指向小红的右手。小红、小黑的过程类似。依次形成了双向环形链表。
    比如这里列表有两个列表项成员,如下图所示:详细PPT可以看正点原子开源网中课件与源码:

在这里插入图片描述
我们从这个图可以看出来,列表项1相当于为头,列表项2为第二项,末尾列表项就是在末尾。所以末尾列表项前一个就是列表项2,末尾列表项下一个就是头,因为是双向环形链表。也就是列表项1,列表项2前一个就是就是列表项1,所以指向列表项1,列表项2下一个是末尾列表项,而列表一前一个就是末尾列表项,下一个就是列表项2.具体指向关系就是上图所示。

列表和列表项API函数

列表和列表项API函数
我们去FreeRTOS的具体工程去看上述函数的具体实现代码:

void vListInitialise( List_t * const pxList )

void vListInitialise( List_t * const pxList )
{/* The list structure contains a list item which is used to mark the* end of the list.  To initialise the list the list end is inserted* as the only list entry. */pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*这行代码将链表的索引(pxIndex)设置为链表结束标志(xListEnd)的地址。在FreeRTOS中,每个链表都有一个特殊的列表项作为链表的结束标志,这个特殊的列表项不存储任何用户数据,它的主要目的是标记链表的末尾。 */listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );/* 这行代码设置列表结束标志的xItemValue为portMAX_DELAY,这是一个非常大的值,确保结束标志始终位于链表的最末端。 */pxList->xListEnd.xItemValue = portMAX_DELAY;/* The list end next and previous pointers point to itself so we know* when the list is empty. */pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );     /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM.  This is checked and valid. */pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd ); /*这两行代码使列表结束标志的pxNext和pxPrevious指针都指向自身,这样做的目的是在链表为空时,能够通过检查这些指针来快速识别。 *//* 如果配置没有使用迷你列表项(configUSE_MINI_LIST_ITEM为0),则将xListEnd的pvOwner和pxContainer字段初始化为NULL,并设置完整性检查值。这是为了确保列表结束标志作为一个完整的列表项符合预期的数据完整性要求 */#if ( configUSE_MINI_LIST_ITEM == 0 ){pxList->xListEnd.pvOwner = NULL;pxList->xListEnd.pxContainer = NULL;listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );}#endifpxList->uxNumberOfItems = ( UBaseType_t ) 0U;//这行代码将链表中的项数初始化为0,表示链表是空的。/* Write known values into the list if* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}

void vListInitialiseItem( ListItem_t * const pxItem )

void vListInitialiseItem( ListItem_t * const pxItem )
{/* Make sure the list item is not recorded as being on a list. */pxItem->pxContainer = NULL;/* Write known values into the list item if* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )

void vListInsertEnd( List_t * const pxList,ListItem_t * const pxNewListItem )
{ListItem_t * const pxIndex = pxList->pxIndex;/*这行代码定义了一个指向ListItem_t的常量指针pxIndex,并将其初始化为指向pxList中的pxIndex成员。pxIndex通常用于标记链表的起始位置或用于遍历链表。*//* Only effective when configASSERT() is also defined, these tests may catch* the list data structures being overwritten in memory.  They will not catch* data errors caused by incorrect configuration or use of FreeRTOS. */listTEST_LIST_INTEGRITY( pxList );listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );/* Insert a new list item into pxList, but rather than sort the list,* makes the new list item the last item to be removed by a call to* listGET_OWNER_OF_NEXT_ENTRY(). */pxNewListItem->pxNext = pxIndex;pxNewListItem->pxPrevious = pxIndex->pxPrevious;/*由于要将这个列表项插入到pxindex的前一个。由于是双向环形链表,所以相当于该列表项伸出来2只手,一个拉前面,一个拉后面*//* Only used during decision coverage testing. */mtCOVERAGE_TEST_DELAY();pxIndex->pxPrevious->pxNext = pxNewListItem;pxIndex->pxPrevious = pxNewListItem;/*新列表项已经伸出来2只手,那么前面的是不是也要伸出来一只手连接到新的列表项。那新的列表项后面那个是不是也要往前面伸出来一只手与之连接才能形成双向环形链表*//* Remember which list the item is in. */pxNewListItem->pxContainer = pxList;( pxList->uxNumberOfItems )++;
}

上面这段函数如果对链表操作熟悉的话,就其实是把pxNewListItem 插入pxIndex前面的链表操作。

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )

/*-----------------------------------------------------------*/void vListInsert( List_t * const pxList,ListItem_t * const pxNewListItem )
{ListItem_t * pxIterator;const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;/* Only effective when configASSERT() is also defined, these tests may catch* the list data structures being overwritten in memory.  They will not catch* data errors caused by incorrect configuration or use of FreeRTOS. */listTEST_LIST_INTEGRITY( pxList );listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );/* 如果xValueOfInsertion的值等于portMAX_DELAY,这通常表示一个特殊的值,用于将新的列表项插入到列表的末尾。因此,pxIterator被设置为指向列表末尾前的项(pxList->xListEnd.pxPrevious)。如果xValueOfInsertion不等于portMAX_DELAY,则通过一个循环找到新列表项应该插入的位置。循环遍历列表,直到找到一个其xItemValue大于xValueOfInsertion的项。pxIterator在这个过程中用于遍历列表。 */if( xValueOfInsertion == portMAX_DELAY ){pxIterator = pxList->xListEnd.pxPrevious;}else{/*该循环用来寻找插入的正确位置,这个函数插入是按照数值升序去插入的。首先循环的初始值就是哨兵节点的地址,由于要找到合适的插入位置,也就是终止循环的条件是pxIterator->pxNext->xItemValue > xValueOfInsertion。所以此时的pxIterator就是要插入位置的前一个节点。*/for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM.  This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */{/* There is nothing to do here, just iterating to the wanted* insertion position. */}}/*这里更好的理解就是理解为手拉手操作,要更新插入新的人前后指向位置,以及新的人前面的人指向后的位置。还有新的人后面的人指向前面的位置,这样才能形成双向链表。这里用图其实更好理解*/pxNewListItem->pxNext = pxIterator->pxNext;pxNewListItem->pxNext->pxPrevious = pxNewListItem;pxNewListItem->pxPrevious = pxIterator;pxIterator->pxNext = pxNewListItem;/* Remember which list the item is in.  This allows fast removal of the* item later. */pxNewListItem->pxContainer = pxList;( pxList->uxNumberOfItems )++;
}
/*-----------------------------------------------------------*/

这个函数完成链表操作我用图大概画了下:
在这里插入图片描述
这样理解为牵手更容易去理解。
下面来看最后一个函数也就是删除链表中某个节点。删除要进行的操作就是

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in.  Obtain the list from the list* item. */List_t * const pxList = pxItemToRemove->pxContainer;pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;/* Only used during decision coverage testing. */mtCOVERAGE_TEST_DELAY();/* Make sure the index is left pointing to a valid item. */if( pxList->pxIndex == pxItemToRemove ){pxList->pxIndex = pxItemToRemove->pxPrevious;}else{mtCOVERAGE_TEST_MARKER();}pxItemToRemove->pxContainer = NULL;( pxList->uxNumberOfItems )--;return pxList->uxNumberOfItems;
}

在这里插入图片描述

实际要完成的操作就是把1,2,3,4都给断掉,变成蓝色的。
所以要完成代码的操作就是下面这个:
1:pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
2:pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/300330.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于SSM的基于个人需求和地域特色的外卖推荐系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的基于个人需求和地域特色的外卖推荐系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

关于 Python 的 import,你了解多少?

多线程和多进程是 Python 中两种实现多任务的不同策略&#xff0c;二者都可以在特定的场景下在一定程度上提高程序的运行速度、性能以及吞吐&#xff0c;但二者的运行机制却有很大的差别。 在 Python 中&#xff0c;多线程以_并发&#xff08;concurrent&#xff09;的方式运行…

Chapter 1 Basic Concepts of Communication and Communication Systems

1.1 The Concept of Communication communication【通信】:It is the process of using signals to transmit messages containing information in space. To put it simply, communication is the spatial transmission of information【信息的空间传递】Information【信息】…

C语言数组:数据的集合艺术(续)

前言 在上一篇文章中&#xff0c;我们深入探讨了C语言数组的基本概念、操作以及多维数组的应用。今天&#xff0c;我们将继续探索数组的更多高级特性&#xff0c;包括动态内存分配、指针与数组的关系以及数组在实际编程中的应用案例。 一、动态内存分配与数组 在C语言中&…

Unity开发一个FPS游戏之三

在前面的两篇博客中&#xff0c;我已实现了一个FPS游戏的大部分功能&#xff0c;包括了第一人称的主角运动控制&#xff0c;武器射击以及敌人的智能行为。这里我将继续完善这个游戏&#xff0c;包括以下几个方面&#xff1a; 增加一个真实的游戏场景&#xff0c;模拟一个废弃的…

数据加密的两种方案

说明&#xff1a;本文介绍对项目中的数据加密的两种方案&#xff1b; 场景 源自真实的项目需求&#xff0c;需要我们对系统中的敏感数据&#xff08;如手机号、证件号等&#xff09;进行加密处理&#xff0c;即存入到数据库中的是加密后的密文数据。加密本身是不难的&#xf…

Spring Security——11,自定义权限校验方法

自定义权限校验方法 一键三连有没有捏~~ 我们也可以定义自己的权限校验方法&#xff0c;在PreAuthorize注解中使用我们的方法。 自定义一个权限检验方法&#xff1a; 在SPEL表达式中使用 ex相当于获取容器中bean的名字未ex的对象。然后再调用这个对象的 hasAuthority方法&am…

软考高级架构师:嵌入式系统的内核架构

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

ssm028蜀都天香酒楼的网站设计与实现+jsp

基于JSP的蜀都天香酒楼管理系统的设计与实现 摘要 近年来&#xff0c;信息化管理行业的不断兴起&#xff0c;使得人们的日常生活越来越离不开计算机和互联网技术。首先&#xff0c;根据收集到的用户需求分析&#xff0c;对设计系统有一个初步的认识与了解&#xff0c;确定蜀都…

带头双向循环链表实现

1.结构及特性 前面我们实现了无头单向非循环链表&#xff0c;它的结构是这样的&#xff1a; 在这里的head只是一个指向头结点的指针&#xff0c;而不是带头链表的头节点。 而带头双向循环链表的逻辑结构则是这样的 这就是链表的结构&#xff0c;链表的每一个节点都有两个指针…

docker 安装redis报错:can not init background jbos

启动redis&#xff0c;发现一直再重启 docker run -d --name redis -p 6379:6379 --restartalways redis:6.2.6 --requirepass "123456" 查看日志&#xff0c;发现job没启动 docker logs 47f6572a779c 尝试了一堆解决办法。。。最后发现尝试安装了redis6.2.6版本&a…

从《布瓦尔与佩库歇》实践中学习社会科学概论

从《布瓦尔与佩库歇》实践中学习社会科学概论 前情提要《布瓦尔与佩库歇》实践笔记云藏山鹰社会科学概论报告核心--信息形数身知™意合™意气实体过程意气实体过程宇宙学诠释™ 社会科学概论花间流风版导读&#xff0c;马斯克风格演讲[ 一尚韬竹团队供稿&#xff1b;] 内容展开…

实验4 层次图和HIPO图

一、实验目的 通过绘制层次图和HIPO图&#xff0c;熟练掌握层次图和HIPO图的基本原理。 能对简单问题进行层次图和HIPO图的分析&#xff0c;独立地完成层次图和HIPO图设计。 二、实验项目内容&#xff08;实验题目&#xff09; 1、用Microsoft Visio绘制出图书馆管理系统的层…

Flume 拦截器概念及自定义拦截器的运用

文章目录 Flume 拦截器拦截器的作用拦截器运用1.创建项目2.实现拦截器接口3.编写事件处理逻辑4.拦截器构建5.打包与上传6.编写配置文件7.测试运行 Flume 拦截器 在 Flume 中&#xff0c;拦截器&#xff08;Interceptors&#xff09;是一种可以在事件传输过程中拦截、处理和修改…

Spring定义Bean对象笔记(二)

前言&#xff1a;上一篇记录了通过XML文件来定义Bean对象&#xff0c;这一篇将记录通过注解和配置类的方式来定义Bean对象。 核心注解&#xff1a; 定义对象&#xff1a;Component,Service,Repository,Controller 依赖注入&#xff1a; 按类型&#xff1a;Autowired 按名称&am…

【JavaScript】作用域 ③ ( JavaScript 作用域链 | 作用域链变量查找机制 )

文章目录 一、JavaScript 作用域链1、作用域2、作用域链3、作用域链变量查找机制 二、代码示例 - 作用域链 一、JavaScript 作用域链 1、作用域 在 JavaScript 中 , 任何代码都有 作用域 , 全局作用域 : 在 <script> 标签中 或者 js 脚本中 定义的变量 属于 全局作用域 …

Vue3【进阶】

简介 https://cn.vuejs.org/guide/introduction.html 创建vue3工程 【基于 vue-cli创建】 基本和vue-cli的过程类似&#xff0c;只是选择的时候用vue3创建 【基于vite创建】【推荐】 【官网】https://vitejs.cn/ 【可以先去学一下webpack】 步骤 【https://cn.vitejs.…

kubernetes集群添加到jumpserver堡垒机里管理

第一步、在kubernetes集群中获取一个永久的token。 jumpserver堡垒机用api的来管理kubernetes&#xff0c;所以需要kubernetes的token&#xff0c;这个token还需要是一个永久的token&#xff0c;版本变更&#xff1a;Kubernetes 1.24基于安全方面的考虑&#xff08;特性门控Le…

LeetCode-热题100:118. 杨辉三角

题目描述 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2: 输入: numRows 1 输出: [[1]]…

代码随想录第32天|455.分发饼干 376. 摆动序列

理论基础 贪心算法核心&#xff1a;选择每一阶段的局部最优&#xff0c;从而达到全局最优。 455.分发饼干 455. 分发饼干 - 力扣&#xff08;LeetCode&#xff09;代码随想录 (programmercarl.com)455. 分发饼干 - 力扣&#xff08;LeetCode&#xff09; 贪心算法理论基础&am…