实时操作系统Freertos开坑学习笔记:(四):临界段保护、列表与列表项

news2024/12/25 12:22:05

前言

废话不多说,直接看主要要探究的问题:
在这里插入图片描述
在这里插入图片描述

一、临界段代码保护

1.什么是临界段?

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
图里面说,临界区的代码是不能被打断的,它运行时不能被中断打断,也不能由于非阻塞任务延时而切换到其他任务去。
比如说IIC进行通信时,软件模拟IIC会有一个4-8us的延时,说明通信时序非常重要,这里一旦被打断就会有影响。
如果我写项目,任务一需要利用IIC通信测量MPU6050加速度值,任务二需要每0.5秒上报一次数据,那我的任务一会被任务二的调度而打断,即使事后仍能回到打断处,但是对于时序要求极高的IIC通信而言,肯定会造成出错。
那应该怎样才能使临界区程序不被打断呢?

2.临界段代码不被中断打断的方法

在这里插入图片描述
对临界区代码的保护本质上就是关中断、开中断
在第三节中断管理中,谈到了开关中断的函数:
在这里插入图片描述
这与临界段保护的开关中断函数不一样,但是本质是一样的。

3.看一个实例

其实实例很简单,而且之前也提到过,就是写开始任务时,要创建任务,必须先进入临界区,在退出临界区。

taskENTER_CRITICAL() 宏用于进入临界区,它会将任务优先级设置为最高,禁止所有中断,保证在临界区的代码不会被其他任务或中断打断。

taskEXIT_CRITICAL() 宏用于退出临界区,它会将任务优先级恢复到原来的值,并允许中断,让其他任务或中断可以继续执行。

为什么这么做呢?第二节我写过,比如如果不这样做,你创建一个task1和task2,在刚创建好task1并且还没创建好task2时,task1就会因为优先级高而先被执行了,万一task1不分享时间片,那岂不是task2根本就不会被运行?为了防止这种任务提前抢占当前任务的情况,那就得用临界区代码保护功能,使得task1和2全被初始化后,在按照优先级老老实实运行。

二、任务调度器的挂起和恢复

1.前言

当创建好任务后,总是需要开启任务调度器来进行任务的切换:

vTaskStartScheduler();

那么,任务调度器同样也会有挂起和恢复的状态。

2.任务调度器挂起和恢复的API函数

在这里插入图片描述

首先要知道中断与任务(调度)切换的区别:

①触发方式:中断是由外部事件触发的,如硬件设备的输入、定时器溢出等;而任务切换是由操作系统内部的调度器触发的,它根据一定的调度策略来决定切换到哪个任务。
②执行环境:中断是在中断上下文中执行的,它会暂停当前运行的任务,保存中断现场,执行中断服务程序,最后恢复现场返回到原来的任务。而任务切换是在任务上下文中执行的,它会保存当前任务的上下文,切换到另一个任务的上下文继续执行。
③调度开销:中断的调度开销比任务切换要小,因为中断服务程序通常很短,只需要保存和恢复现场即可;而任务切换需要保存和恢复多个寄存器和堆栈,因此开销相对较大。
④调度优先级:中断的优先级通常比任务的优先级高,因为中断需要尽快响应外部事件;而任务的优先级则由操作系统内部的调度器决定,根据任务的重要性和紧急程度来分配优先级。

通俗地说,我创建了两个task,均是每隔0.5s闪烁一次led,同时,我又初始化了一个外部中断+按键,按下会使得task1挂起。那么,两个task之间的调度属于任务调度,而外部中断、定时器溢出中断、串口接收中断等等均属于中断,优先级更高。

挂起任务调度器, 调用此函数不需要关闭中断

这句话的意思是说:它仅仅是防止了任务之间的资源争夺,中断照样可以直接响应。挂起调度器的方式,适用于临界区位于任务与任务之间;既不用去延时中断,又可以做到临界区的安全。
它是怎么保护临界区程序的呢?很简单,把任务调度器ban了,使得可能占据程序资源的任务根本不会来抢占了。但是也有缺点,就是外部中断照样有可能打断临界区程序。
这就要看你的临界区程序具体的形式和所处的位置来决定采用开关中断或者ban任务调度器。

;临界区代码保护和任务调度器挂起的总结图:
在这里插入图片描述

三、列表与列表项

前面各种地方都在说列表,什么就绪任务列表、任务挂起列表等等,那列表到达是个什么东西呢?这个概念是非常重要的,如果只看重外部实现,那浅浅了解,但是要深入去看freertos内核源码是怎么实现的,列表是重中之重!对于理解freertos的运行机制很有帮助。

1.列表和列表项的定义

在这里插入图片描述

(1)列表:列表是 FreeRTOS 中的一个数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务。数据结构上说,链表(Linked List)是一种常见的线性数据结构,用于存储一系列数据元素。链表中的每个元素由一个节点(Node)表示,每个节点包含一个数据元素和指向下一个节点的指针。通过指针,可以将各个节点连接起来,形成一个链式结构。

(2)列表项:列表项就是存放在列表中的项目。

(3)示意图:
在这里插入图片描述
列表项就是列表中的节点。它是一个环形的列表,是一个双向的环形链表。
举个例子:
在这里插入图片描述
三个人组成了一个环形的链表结构,我们知道链表是用指针指向节点的,这里比如小明是一个节点,它的身体就是数据元素,而他的右手就是一个指向下一位节点小黑的指针,他的左手就是指向上一位节点小红的指针。
假设我这时突然要在小明和小红之间插进来一个小美,那就让小美的左右手跟小明、小红拉上即可,而要删除一个节点,同理。

所以这种列表结构为什么适合freertos呢?

(1)列表的特点:列表项间的地址非连续的,是人为的连接到一起的。列表项的数目是由后期添加的个数决定的,随时可以改变。我可以随意添加和删除其中的节点,每个节点表示一个任务,包含了任务的状态、优先级、堆栈等信息。通过遍历链表,可以快速访问所有的任务,并根据调度算法选择下一个要执行的任务。
(2)动态分配内存:链表可以根据需要动态分配内存,可以灵活地增加或删除任务,不需要事先知道任务列表的大小。
(3)高效的插入和删除:链表的插入和删除操作只需要修改指针,时间复杂度为 O(1)。
(4)空间利用率高:链表只需要存储任务的指针和状态信息,不需要像数组那样预留空间,因此空间利用率更高。

2.列表相关结构体

在这里插入图片描述
list.c就是freertos源码中对于列表的配置。

①列表结构体

在这里插入图片描述
这里进行解释:
(1)在该结构体中, 包含了两个宏,

listFIRST_LIST_INTEGRITY_CHECK_VALUE
listSECOND_LIST_INTEGRITY_CHECK_VALUE

这两个宏是确定的已知常量, FreeRTOS通过检查这两个常量的值,
来判断列表的数据在程序运行过程中,是否遭到破坏 ,该功能一般用于调试, 默认是不开启的,其实不重要,可以不看。

(2)

volatile UBaseType_t uxNumberOfItems;

成员uxNumberOfItems,用于记录列表中列表项的个数(不包含 xListEnd)

(3)成员 pxIndex 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项

ListItem_t * configLIST_VOLATILE pxIndex;

(4)成员变量 xListEnd 是一个迷你列表项,排在最末尾

MiniListItem_t xListEnd;

另外,看这个结构图:
在这里插入图片描述
列表项是列表中用于存放数据的地方,在 list.h 文件中,有列表项的相关结构体定义。每一个列表项代表一个任务。

②列表项结构体

而列表项的结构成员如下:
在这里插入图片描述
1、成员变量 xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序
2、成员变量 pxNext 和 pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项
3、成员变量 pxOwner 用于指向包含列表项的对象(通常是任务控制块)
4、成员变量 pxContainer 用于指向列表项所在列表,有了这个变量,就可以知道任务目前属于哪个状态的了。

③迷你列表项结构体

在这里插入图片描述

3.列表和列表项的关系

在这里插入图片描述
列表的初始状态:**在 FreeRTOS 中,列表的初始状态是空的,即列表中没有任何列表项。此时,列表的头部和尾部都是指向同一个特殊的标记 xListEnd 的指针,并且列表项数量为 0。**如图:
在这里插入图片描述

插入两个列表项:假设我们要向列表中插入两个列表项 A 和 B。插入操作一般包括以下几个步骤:

(1) 创建列表项:首先需要创建两个列表项 A 和 B,分别用 ListItem_t 结构体类型定义,可以使用 pvPortMalloc() 函数在堆上动态分配内存。

(2) 初始化列表项:对于每个列表项,需要设置其 xItemValue 值、pvOwner 指针、pxNext 指针和 pxPrevious 指针等信息。其中,xItemValue 可以理解为列表项的优先级,pvOwner 指向该列表项的拥有者,pxNext 和 pxPrevious 指向该列表项的下一个和上一个列表项。

(3) 插入列表项:将列表项 A 插入列表的头部,将列表项 B 插入列表的尾部。插入操作需要修改前后列表项的 pxNext 和 pxPrevious 指针,将其指向新的列表项。同时,需要修改列表的头部和尾部指针,将其指向插入后的新的头部和尾部。
在这里插入图片描述

(4) 更新列表项数量:每次插入或删除列表项后,需要更新列表中的列表项数量 uxNumberOfItems。

插入两个列表项后,列表的状态如下:

+------------------------------------------------+
|                   xListEnd                      |
|------------------------------------------------|
| pxPrevious = &B |             | pxPrevious = &A |
|------------------------------------------------|
| pxNext = &A     |             | pxNext = &B     |
|------------------------------------------------|
| xItemValue = 0  |             | xItemValue = 1  |
|------------------------------------------------|
|    pvOwner      |             |    pvOwner      |
+------------------------------------------------+
                           |
                           v
                      +--------+
                      |  List  |
                      +--------+
                   pxIndex = &A
             uxNumberOfItems = 2

其中,xListEnd 表示列表的结尾,A 和 B 分别为列表项,List 表示列表,pxIndex 表示用于遍历列表项的指针,uxNumberOfItems 表示列表中的列表项数量,pxNext 和 pxPrevious 分别表示列表项的下一个和上一个列表项。

4.列表相关API函数

在这里插入图片描述

①初始化列表

在这里插入图片描述
图中说的很清楚,就是对列表结构体的配置。初始化时,列表中只有 xListEnd,因此 pxIndex 指向 xListEnd。xListEnd 的值初始化为最大值,用于列表项升序排序时,排在最后。初始化时,列表中只有 xListEnd,因此上一个和下一个列表项都为 xListEnd 本身。初始化时,列表中的列表项数量为 0(不包含 xListEnd)。

②初始化列表项

在这里插入图片描述
函数参数为指向要初始化的列表项的指针 pxItem,函数没有返回值。

函数的作用是将列表项的 pxNext 和 pxPrevious 指针都设置为 NULL,将 xItemValue 和 pvOwner 值都设置为 0,表示将列表项初始化为空。

该函数在插入新的列表项时经常使用,因为在插入列表项之前需要先将其初始化为空。例如,可以使用以下代码初始化列表项:

ListItem_t xItem;
vListInitialiseItem( &xItem );

这样就可以将 xItem 的 pxNext 和 pxPrevious 指针都设置为 NULL,将 xItemValue 和 pvOwner 值都设置为 0,表示将列表项初始化为空。然后可以将 xItem 插入到列表中,如通过调用 vListInsert() 函数将其插入到列表的头部或尾部。

③列表项插入函数(升序插入)

在这里插入图片描述
升序插入是指按照列表项的 xItemValue 值进行排序,将新的列表项插入到正确的位置。**列表项数值越大,插入的顺序就越靠后。**升序插入可以通过调用 vListInsert() 函数实现,例如:

ListItem_t xItemA, xItemB;
xItemA.xItemValue = 1;
xItemB.xItemValue = 2;
vListInitialiseItem( &xItemA );
vListInsert( &xList, &xItemA );
vListInsert( &xList, &xItemB );

在此例子中,先创建了两个列表项 xItemA 和 xItemB,分别设置其 xItemValue 值为 1 和 2,并使用 vListInitialiseItem() 函数初始化这两个列表项。然后,将 xItemA 插入到列表中,接着将 xItemB 插入到列表中。由于 xItemB 的 xItemValue 值比 xItemA 大,因此会将 xItemB 插入到 xItemA 的后面,最终列表中的顺序为 A->B。

④列表项插入函数(末尾插入)

在这里插入图片描述
注意:函数vListInsertEnd(),是将待插入的列表项插入到列表 pxIndex 指针指向的列表项前面;看的就是pxIndex 指针,其他不要看。现在详细地进行讲解:
(1)它的参数为指向要插入列表的指针 pxList和待插入列表项。
(2)例一:
在这里插入图片描述
首先,index指针指向末尾列表项,这时插入值为30的列表项2,
那么画一个层次图:
在这里插入图片描述
要插入的列表项是在index指针指向的列表项的前一个地方。
(3)例二:
在这里插入图片描述
这时index指针指向值为40的列表项1,那么:
在这里插入图片描述

⑤移除列表项函数

在这里插入图片描述
函数的作用是从列表中删除指定的列表项,并返回删除的列表项数量。该函数是通过将要删除的列表项的 pxPrevious 指针的 pxNext 指向要删除的列表项的 pxNext 指针,将要删除的列表项的 pxNext 指针的 pxPrevious 指向要删除的列表项的 pxPrevious 指针来实现的。

5.列表项的插入和删除实战例程

目的:
在这里插入图片描述
这里其实只用看核心任务代码就行:

List_t          TestList;           /* 定义测试列表 */
ListItem_t      ListItem1;          /* 定义测试列表项1 */
ListItem_t      ListItem2;          /* 定义测试列表项2 */
ListItem_t      ListItem3;          /* 定义测试列表项3 */

/* 任务二,列表项的插入和删除实验 */
void task2( void * pvParameters )
{
    vListInitialise(&TestList);         /* 初始化列表 */
    vListInitialiseItem(&ListItem1);    /* 初始化列表项1 */
    vListInitialiseItem(&ListItem2);    /* 初始化列表项2 */
    vListInitialiseItem(&ListItem3);    /* 初始化列表项3 */
    ListItem1.xItemValue = 40;
    ListItem2.xItemValue = 60;
    ListItem3.xItemValue = 50;

    /* 第二步:打印列表和其他列表项的地址 */
    printf("/**************第二步:打印列表和列表项的地址**************/\r\n");
    printf("项目\t\t\t地址\r\n");
    printf("TestList\t\t0x%p\t\r\n", &TestList);
    printf("TestList->pxIndex\t0x%p\t\r\n", TestList.pxIndex);
    printf("TestList->xListEnd\t0x%p\t\r\n", (&TestList.xListEnd));
    printf("ListItem1\t\t0x%p\t\r\n", &ListItem1);
    printf("ListItem2\t\t0x%p\t\r\n", &ListItem2);
    printf("ListItem3\t\t0x%p\t\r\n", &ListItem3);
    printf("/**************************结束***************************/\r\n");
    
    printf("\r\n/*****************第三步:列表项1插入列表******************/\r\n");
    vListInsert((List_t*    )&TestList,         /* 列表 */
                (ListItem_t*)&ListItem1);       /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    
    /* 第四步:列表项2插入列表 */
    printf("\r\n/*****************第四步:列表项2插入列表******************/\r\n");
    vListInsert((List_t*    )&TestList,         /* 列表 */
                (ListItem_t*)&ListItem2);       /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    
    /* 第五步:列表项3插入列表 */
    printf("\r\n/*****************第五步:列表项3插入列表******************/\r\n");
    vListInsert((List_t*    )&TestList,         /* 列表 */
                (ListItem_t*)&ListItem3);       /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
    printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
    printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    
    /* 第六步:移除列表项2 */
    printf("\r\n/*******************第六步:移除列表项2********************/\r\n");
    uxListRemove((ListItem_t*   )&ListItem2);   /* 移除列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    
    /* 第七步:列表末尾添加列表项2 */
    printf("\r\n/****************第七步:列表末尾添加列表项2****************/\r\n");
    TestList.pxIndex = &ListItem1;
    vListInsertEnd((List_t*     )&TestList,     /* 列表 */
                   (ListItem_t* )&ListItem2);   /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->pxIndex\t\t0x%p\r\n", TestList.pxIndex);
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
    printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
    printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
    printf("/************************实验结束***************************/\r\n");
    while(1)
    {
        vTaskDelay(1000);
    }
}

这是一个使用 FreeRTOS 列表实现列表项的插入和删除的示例任务。该任务通过初始化列表、列表项和设置列表项的值,然后将列表项插入到列表中,并打印插入列表项前后的列表和列表项的地址,最后从列表中删除指定的列表项。

具体操作如下:

初始化列表和列表项:使用 vListInitialise() 和 vListInitialiseItem() 函数分别初始化列表和列表项。

打印列表和其他列表项的地址:使用 printf() 函数打印列表和其他列表项的地址,以便后面插入列表项时进行对比。

列表项1插入列表:使用 vListInsert() 函数将列表项1插入到列表中,打印插入列表项前后的列表和列表项的地址,以便对比。

列表项2插入列表:使用 vListInsert() 函数将列表项2插入到列表中,打印插入列表项前后的列表和列表项的地址,以便对比。

列表项3插入列表:使用 vListInsert() 函数将列表项3插入到列表中,打印插入列表项前后的列表和列表项的地址,以便对比。

移除列表项2:使用 uxListRemove() 函数将列表项2从列表中移除,打印移除列表项后的列表和列表项的地址,以便对比。

列表末尾添加列表项2:使用 vListInsertEnd() 函数将列表项2插入到列表的末尾,打印插入列表项前后的列表和列表项的地址,以便对比。

看运行结果:
首先是第二步:注意这时列表里面内容为空,只有末尾列表项。
在这里插入图片描述
然后第三步升序插入列表项1:
在这里插入图片描述
就像手牵手一样,列表项1的next指向末尾列表项,末尾列表项的previous指向前一个即列表项1.后续的思路都差不多。这里我不把结果截图出来了。

最后总结一下列表和列表项的作用:

列表和列表项的主要作用如下:

任务调度:FreeRTOS 中的任务调度器使用列表和列表项来实现任务的调度。每个任务都有一个列表项,任务调度器根据列表项的优先级和状态来决定下一个要执行的任务。

事件通知:FreeRTOS 中的事件通知机制也使用列表和列表项来实现。每个事件都有一个列表项,当事件发生时,可以将该事件对应的列表项插入到一个等待列表中,等待被唤醒。

存储数据:列表也可以用于存储数据,例如 FreeRTOS 中的消息队列就是使用列表来存储消息的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java转Go】Go中使用WebSocket实现聊天室(私聊+群聊)

目录 前言功能效果(一人分饰多角.jpg😎)用户上线、群聊私聊和留言下线 实现思路代码服务端 chat.go 完整代码客户端 html 完整代码 最后 前言 之前在Java中,用 springbootwebsocket 实现了一个聊天室:springbootwebso…

用BAPI创建销售订单条件价格有多个

说明: 在用BAPI_SALESORDER_CREATEFROMDAT2创建销售订单的时候,业务配置的是Z001自动出来的,我在BAPI条件那块赋值更改标识也是U,但是创建出来的单据还是有两条件类型。 解决方法: LOGIC_SWITCH-PRICING G .即可

AP9193 升压恒流驱动芯片 美容护肤仪 美容灯 锂电池升压驱动IC

AP9193 是一款高效率、高精度的升 压型大功率 LED 灯恒流驱动控制芯片。 应用领域 LED 灯杯 电池供电的 LED 灯串 平板显示 LED 背光 恒流充电器控制 大功率 LED 照明 AP9193 内置高精度误差放大器,固 定关断时间控制电路,恒流驱动电路等&#xff…

签到系统怎么设计

背景 相信签到系统大家都有接触过,更多的是使用。但是有思考过这种系统是怎么设计的吗?比方说我统计一下每个月中每天的签到情况,怎么设计呢?今天一篇文章告诉你。 首先,我们熟悉的思维是:我设计一个数据…

电商平台-业务中台-SPU,SKU,SN概念简介

什么是SPU (Standard Product Unit)? SPU标准属性是商品基本属性,基本属性中最核心两个属性是品牌和型号,电商平台一般采用 品牌和型号 来确定SPU(Standard Product Unit)标准化管理单元, 例如:小米 10 就…

Vue3 el-tooltip 根据内容控制宽度大小换行和并且内容太短不显示

el-tooltip 根据长度自适应换行以及显隐 环境 vue: "3.2.37" element-ui: "2.1.8"要求 tooltip 根据内容自动换行如果内容超出显示省略号显示&#xff0c;不超出不显示 tooltip 代码 组件 // ContentTip 组件 <template><el-tooltipv-bind&qu…

excel中的引用与查找函数篇1

1、COLUMN(reference)&#xff1a;返回与列号对应的数字 2、ROW(reference)&#xff1a;返回与行号对应的数字 参数reference表示引用/参考单元格&#xff0c;输入后引用单元格后colimn()和row()会返回这个单元格对应的列号和行号。若参数reference没有引用单元格&#xff0c;…

SOME/IP TTL 在各种Entry 中各是什么意思?有什么限制?

1 服务发现 SOME/IP SD 服务发现主要用于 定位服务实例检测服务实例状态是否在运行发布/订阅行为管理SOME/IP SD 也是 SOME/IP 消息,遵循 SOME/IP 消息格式,有固定的 Message ID、Request ID 以及 Message Type 等。并对 SOME/IP Payload 进行了详细的定义。 SOME/IP SD …

2023-9-4 快速幂求逆元

题目链接&#xff1a;快速幂求逆元 #include <iostream> #include <algorithm>using namespace std;typedef long long LL;LL qmi(int a, int k, int p) {LL res 1;while(k){if(k & 1) res (LL) res * a % p;k >> 1;a (LL) a * a % p;}return res; }i…

能力和结果之间的关系

大家好,这里是大话硬件。 今天这篇文章想和大家分享前段时间的一点工作体会,关于个人能力和工作结果之间的关系。 其实这些感悟是在上周三晚上下班,走在回家的路上,脑海中突然出现这样的体会,回到家里立马写了下来。因为是即时的灵感,完全是因为工作状态触发,立刻写下…

手写RPC框架--4.服务注册

RPC框架-Gitee代码(麻烦点个Starred, 支持一下吧) RPC框架-GitHub代码(麻烦点个Starred, 支持一下吧) 服务注册 服务注册a.添加服务节点和主机节点b.抽象注册中心c.本地服务列表 服务注册 a.添加服务节点和主机节点 主要完成服务注册和发现的功能&#xff0c;其具体流程如下&…

智能配电室运维云平台

智能配电室运维云平台依托电易云-智慧电力物联网&#xff0c;是通过物联网技术实现配电设备智能化管理和运维的云服务系统。该平台可以实时监测配电设备的运行状态、能耗情况、故障报警等信息&#xff0c;并通过云计算、大数据等技术进行分析和处理&#xff0c;提供精准的数据支…

数据结构前言

一、什么是数据结构&#xff1f; 数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。 上面是百度百科的定义&#xff0c;通俗的来讲数据结构就是数据元素集合与数据元素集合或者数据元素与数据元素之间的组成形式。 举个…

MyBatisPlus 基础实现(一)

说明 创建一个最基本的MyBatisPlus项目&#xff0c;参考官网。 依赖 MyBatisPlus 依赖&#xff0c;最新版是&#xff1a;3.5.3.2 &#xff08;截止2023-9-4&#xff09;。 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-bo…

Xcode打包ipa文件,查看app包内文件

1、Xcode发布ipa文件前&#xff0c;在info中打开如下两个选项&#xff0c;即可在手机上查看app包名文件夹下的文件及数据。

基于QWebEngine实现无头浏览器

无头浏览器 无头浏览器&#xff08;Headless Browser&#xff09;是一种没有图形用户界面&#xff08;GUI&#xff09;的浏览器。它通过在内存中渲染页面&#xff0c;然后将结果发送回请求它的用户或程序来实现对网页的访问&#xff0c;而不会在屏幕上显示网页。这种方式使得无…

编译OpenWrt内核驱动

编译OpenWrt内核驱动可以参考OpenWrt内部其它驱动的编写例程&#xff0c;来修改成自己需要的驱动 一、OpenWrt源代码获取与编译 1.1、搭建环境 下载OpenWrt的官方源码&#xff1a; git clone https://github.com/openwrt/openwrt.git1.2、安装编译依赖项 sudo apt update -…

机器人中的数值优化(九)——拟牛顿方法(下)、BB方法

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

CocosCreator3.8研究笔记(六)CocosCreator 脚本装饰器的理解

一、什么是装饰器&#xff1f; 装饰器是TypeScript脚本语言中的概念。 TypeScript的解释&#xff1a;在一些场景下&#xff0c;我们需要额外的特性来支持标注或修改类及其成员。装饰器&#xff08;Decorators&#xff09;为我们在类的声明及成员上通过元编程语法添加标注提供了…

Java23种设计模式之【单例模式】

目录 一.单例模式的起源&#xff0c;和应用场景 1.单例模式的前世今生&#xff01; 2.什么是单例模式&#xff1f; 2.1使用单例模式的注意事项 2.2如何理解单例模式&#xff1f; 2.3单例模式的优势以及不足&#xff01; 2.4使用场景 二.实现 1.实现思路 1.1创建一个 S…