FreeRTOS从入门到精通 第十六章(任务通知)
参考教程:【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili
一、任务通知简介
1、概述
(1)任务通知顾名思义是用来通知任务的,任务控制块中的结构体成员变量ulNotifiedValue就是这个通知值。
(2)队列、信号量、事件标志组与任务通知的区别:
①使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行任务间的通信。
②使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收其它任务发过来的“通知”。
(3)只要合理、灵活地利用任务通知的特点,可以在一些场合中替代队列、信号量、事件标志组。
(4)任务通知的优势及劣势:
①优势:
[1]使用任务通知向任务发送事件或数据,比使用队列、事件标志组或信号量快得多。
[2]使用其它方法进行任务间通讯时都要先创建对应的结构体,而使用任务通知则无需额外创建结构体。
②劣势:
[1]ISR没有任务结构体,所以无法给ISR发送数据,但是ISR可以使用任务通知的功能,发数据给任务。
[2]任务通知只能是被指定的一个任务接收并处理,无法广播给多个任务。
[3]任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保持一个数据,无法缓存多个数据。
[4]发送方无法进入阻塞状态等待。
2、任务通知值和通知状态
(1)任务都有一个结构体——任务控制块TCB,它里边有两个结构体成员变量与任务通知相关,一个是uint32_t类型,用来表示通知值,另一个是uint8_t类型,用来表示通知状态。
typedef struct tskTaskControlBlock
{… …#if ( configUSE_TASK_NOTIFICATIONS == 1 )volatile uint32_t ulNotifiedValue[configTASK_NOTIFICATION_ARRAY_ENTRIES];volatile uint8_t ucNotifyState[configTASK_NOTIFICATION_ARRAY_ENTRIES];#endif… …
}tskTCB;
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 //定义任务通知数组的大小,默认为1
(2)任务通知值的更新方式有多种类型:
①计数值(数值累加,类似信号量)。
②相应位置1(类似事件标志组)。
③写任意数值(支持覆写和不覆写,类似队列)。
(3)任务通知状态共有3种取值:
①任务未等待通知:任务通知默认的初始化状态。
②任务在等待通知:接收方已经准备好了(调用了接收任务通知函数),等待发送方给通知。
③任务在等待接收:发送方已经将通知发送出去(调用了发送任务通知函数),等待接收方接收。
#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) //任务未等待通知
#define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 ) //任务在等待通知
#define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 ) //任务在等待接收
二、任务通知相关API函数介绍
1、任务通知相关API函数概览
(1)任务通知API函数主要有两类——发送通知、接收通知,发送通知API函数可以用于任务和中断服务函数中,而接收通知API函数只能用在任务中。
(2)发送通知相关API函数:
函数 | 描述 |
xTaskNotify() | 发送通知,带有通知值 |
xTaskNotifyAndQuery() | 发送通知,带有通知值并且保留接收任务的原通知值 |
xTaskNotifyGive() | 发送通知,不带通知值 |
xTaskNotifyFromISR() | 在中断中发送任务通知 |
xTaskNotifyAndQueryFromISR() | |
vTaskNotifyGiveFromISR() |
(3)接收通知相关API函数:
函数 | 描述 |
ulTaskNotifyTake() | 获取任务通知,可以设置在退出此函数的时候将任务通知值清零或者减一; 当任务通知用作二值信号量或者计数信号量的时候,使用此函数来获取信号量 |
xTaskNotifyWait() | 获取任务通知,比 ulTaskNotifyTak()更为复杂,可获取通知值和清除通知值的指定位; 当任务通知用作于事件标志组或队列时,使用此函数来获取 |
2、发送通知相关API函数
(1)xTaskNotifyAndQuery函数、xTaskNotify函数和xTaskNotifyGive函数,它们的底层函数实际上都是同一个函数。
#define xTaskNotifyAndQuery(xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue) \ xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ),( pulPreviousNotifyValue ) )#define xTaskNotify(xTaskToNotify, ulValue, eAction) \ xTaskGenericNotify( ( xTaskToNotify ),( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL )#define xTaskNotifyGive(xTaskToNotify) \ xTaskGenericNotify( ( xTaskToNotify ),( tskDEFAULT_INDEX_TO_NOTIFY ),( 0 ),eIncrement,NULL )
(2)xTaskGenericNotify函数:
①函数入口定义:
BaseType_t xTaskGenericNotify
(TaskHandle_t xTaskToNotify, //接收任务通知的任务句柄UBaseType_t uxIndexToNotify, //任务的指定通知(任务通知相关数组成员)uint32_t ulValue, //任务通知值eNotifyAction eAction, //通知方式(通知值更新方式)uint32_t * pulPreviousNotificationValue //用于保存更新前的任务通知值(为NULL则不保存)
)
②任务通知方式共有以下几种:
typedef enum
{ eNoAction = 0, //无操作eSetBits //更新指定biteIncrement //通知值加1eSetValueWithOverwrite //覆写的方式更新通知值eSetValueWithoutOverwrite //不覆写通知值
}eNotifyAction;
③函数源码剖析:
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,UBaseType_t uxIndexToNotify,uint32_t ulValue,eNotifyAction eAction,uint32_t * pulPreviousNotificationValue )
{TCB_t * pxTCB;BaseType_t xReturn = pdPASS;uint8_t ucOriginalNotifyState;traceENTER_xTaskGenericNotify( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue );configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );configASSERT( xTaskToNotify );pxTCB = xTaskToNotify;taskENTER_CRITICAL();{if( pulPreviousNotificationValue != NULL ) //判断是否需要保存原先的任务通知值{*pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ]; //保存原先的任务通知值至缓冲区}//记录目标任务先前的通知状态ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ];//赋值当前的任务状态(等待接收状态,即发送方的数据等待接收方接收)pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED;switch( eAction ) //有四种通知值的更新方式,需要判断使用哪种{case eSetBits: //按位更新(类似事件标志组)pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue;break;case eIncrement: //用计数的方式更新通知值(类似信号量)( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++;break;case eSetValueWithOverwrite: //覆写的方式更新通知值(类似队列)pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;break;case eSetValueWithoutOverwrite: //不覆写的方式更新通知值(类似队列)if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ){pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;}else{xReturn = pdFAIL; //如果有通知值还未被接收,不能覆写}break;case eNoAction:break;default:configASSERT( xTickCount == ( TickType_t ) 0 );break;}traceTASK_NOTIFY( uxIndexToNotify );if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) //如果接收方已准备好接收通知值{listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); //将接收方任务从阻塞列表中移出prvAddTaskToReadyList( pxTCB ); //将接收方任务添加进就绪列表中configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );#if ( configUSE_TICKLESS_IDLE != 0 ){prvResetNextTaskUnblockTime();}#endiftaskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB ); //如因优先级问题需要任务切换,执行即可}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();traceRETURN_xTaskGenericNotify( xReturn );return xReturn;
}
3、接收通知相关API函数
(1)ulTaskNotifyTake函数:
①函数定义:
#define ulTaskNotifyTake(xClearCountOnExit, xTicksToWait) \ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ), \( xClearCountOnExit ), \( xTicksToWait ) )
②函数参数:
形参 | 描述 |
xClearCountOnExit | 指定在成功接收通知后,将通知值清零或减 1 pdTRUE:把通知值清零;pdFALSE:把通知值减1 |
xTicksToWait | 阻塞等待任务通知值的最大时间 |
③返回值:
返回值 | 描述 |
0 | 接收失败 |
非 0 | 接收成功,返回任务通知的通知值 |
④ulTaskGenericNotifyTake函数源码剖析:
uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn,BaseType_t xClearCountOnExit,TickType_t xTicksToWait )
{uint32_t ulReturn;BaseType_t xAlreadyYielded, xShouldBlock = pdFALSE;traceENTER_ulTaskGenericNotifyTake(uxIndexToWaitOn,xClearCountOnExit,xTicksToWait);configASSERT( uxIndexToWaitOn < configTASK_NOTIFICATION_ARRAY_ENTRIES );vTaskSuspendAll();{taskENTER_CRITICAL();{if( pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] == 0U ) //判断任务通知值是否为0,是则说明发送方还未发送非0通知值{pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION; //将状态更改为在等待通知if( xTicksToWait > ( TickType_t ) 0 ) //如果阻塞时间大于0xShouldBlock = pdTRUE; //任务需要阻塞elsemtCOVERAGE_TEST_MARKER();}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();if( xShouldBlock == pdTRUE ) //如果任务需要阻塞{traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWaitOn );prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); //将任务添加进阻塞列表中}else{mtCOVERAGE_TEST_MARKER();}}xAlreadyYielded = xTaskResumeAll();if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) ){taskYIELD_WITHIN_API(); //如果任务进入阻塞态,则需要执行任务切换(if判断的作用是防止重复切换)}else{mtCOVERAGE_TEST_MARKER();}taskENTER_CRITICAL();{traceTASK_NOTIFY_TAKE( uxIndexToWaitOn );ulReturn = pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ];if( ulReturn != 0U ) //如果任务通知值不为0,说明发送方已将非0通知值发送{if( xClearCountOnExit != pdFALSE ) //根据函数参数判断通知值如何处理{pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] = ( uint32_t ) 0U; //通知值清零(用于模拟二值信号量)}else{pxCurrentTCB->ulNotifiedValue[uxIndexToWaitOn] = ulReturn - (uint32_t)1; //通知值-1(用于模拟计数型信号量)}}else{mtCOVERAGE_TEST_MARKER();}pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskNOT_WAITING_NOTIFICATION; //状态更改为未等待通知}taskEXIT_CRITICAL();traceRETURN_ulTaskGenericNotifyTake( ulReturn );return ulReturn;
}
(2)xTaskNotifyWait函数:
①函数定义:
#define xTaskNotifyWait(ulBitsToClearOnEntry, \ulBitsToClearOnExit, \pulNotificationValue, \xTicksToWait) \xTaskGenericNotifyWait(tskDEFAULT_INDEX_TO_NOTIFY, \( ulBitsToClearOnEntry ), \( ulBitsToClearOnExit ), \( pulNotificationValue ), \( xTicksToWait ))
②函数参数:
形参 | 描述 |
uxIndexToWaitOn | 任务的指定通知(任务通知相关数组成员) |
ulBitesToClearOnEntry | 等待前清零指定任务通知值的比特位(旧值对应bit清0) |
ulBitesToClearOnExit | 成功等待后清零指定的任务通知值比特位(新值对应bit清0) |
pulNotificationValue | 用来取出通知值(如果不需要取出,可设为NULL) |
xTicksToWait | 阻塞等待任务通知值的最大时间 |
③返回值:
返回值 | 描述 |
pdTRUE | 等待任务通知成功 |
pdFALSE | 等待任务通知失败 |
④xTaskGenericNotifyWait函数源码剖析:
BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn,uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t * pulNotificationValue,TickType_t xTicksToWait )
{BaseType_t xReturn, xAlreadyYielded, xShouldBlock = pdFALSE;traceENTER_xTaskGenericNotifyWait( uxIndexToWaitOn, ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait );configASSERT( uxIndexToWaitOn < configTASK_NOTIFICATION_ARRAY_ENTRIES );vTaskSuspendAll();{taskENTER_CRITICAL();{if( pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] != taskNOTIFICATION_RECEIVED ) //判断任务是否不在等待接收{pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] &= ~ulBitsToClearOnEntry; //将原先通知值的指定位清零pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION; //将任务状态更改为在等待通知if( xTicksToWait > ( TickType_t ) 0 ) //如果阻塞时间大于0xShouldBlock = pdTRUE; //任务需要阻塞elsemtCOVERAGE_TEST_MARKER();}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();if( xShouldBlock == pdTRUE ) //如果任务需要阻塞{traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWaitOn );prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); //将任务添加进阻塞列表中}else{mtCOVERAGE_TEST_MARKER();}}xAlreadyYielded = xTaskResumeAll();if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) ){taskYIELD_WITHIN_API(); //如果任务进入阻塞态,则需要执行任务切换(if判断的作用是防止重复切换)}else{mtCOVERAGE_TEST_MARKER();}taskENTER_CRITICAL();{traceTASK_NOTIFY_WAIT( uxIndexToWaitOn );if( pulNotificationValue != NULL ) //判断是否需要保存原先的任务通知值{*pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ]; //保存原先的任务通知值}if( pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] != taskNOTIFICATION_RECEIVED ) //判断任务是否不在等待接收{xReturn = pdFALSE; //接收失败}else{pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] &= ~ulBitsToClearOnExit; //将通知值接收,并把指定位清零xReturn = pdTRUE; //接收成功}pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskNOT_WAITING_NOTIFICATION; //状态更改为未等待通知}taskEXIT_CRITICAL();traceRETURN_xTaskGenericNotifyWait( xReturn );return xReturn;
}
三、任务通知模拟二值信号量实验
1、原理图与实验目标
(1)原理图:
(2)实验目标:
①设计4个任务——start_task、task1、task2、task3:
[1]start_task:用于创建task1、task2和task3任务。
[2]task1:当获取到LED1的硬件资源后,控制LED1约每500ms完成亮暗翻转的状态切换,每完成一次即将资源释放。
[3]task2:当获取到LED2的硬件资源后,控制LED2约每1000ms完成亮暗翻转的状态切换,每完成一次即将资源释放。
[4]task3:按下按键1,获取(或者说霸占)LED1和LED2的硬件资源;按下按键2,释放LED1和LED2的硬件资源。
②预期实验现象:
[1]程序下载到板子上后,两个LED灯闪烁。
[2]按下按键1,LED1和LED2停止闪烁(允许有1秒左右的延迟)。
[3]按下按键2,LED1和LED2恢复闪烁。
2、实验步骤
(1)将“二值信号量实验”的工程文件夹复制一份,在拷贝版中进行实验。
(2)将FreeRTOS_experiment.c文件中关于信号量的代码全部移除,并更改task1、task2和task3函数的实现。
①task1函数的思路:需要使用LED的硬件资源时调用ulTaskNotifyTake函数等待任务通知,并且必须等待到通知(同时将通知值清零)才可进行下一步操作——翻转LED1的状态。
②task2函数的思路:需要使用LED的硬件资源时调用ulTaskNotifyTake函数等待任务通知,并且必须等待到通知(同时将通知值清零)才可进行下一步操作——翻转LED2的状态。
③task3函数的思路:
[1]task1和task2需要task3的通知才能使用LED硬件资源,与二值信号量实验不同,二值信号量实验有单独的两个“队列”分别管理两个LED硬件资源,而本实验则是把task3当作了二值信号量队列管理员,两个二值信号量分别由task1和task2的TCB的任务通知相关成员代替。
[2]task1和task2不断申请和归还硬件资源,那么task3也要不断处理task1和task2的申请,要不断地管理资源的分配,而不是等待按键事件到来的一刻才做一次资源分配操作,于是原本的控制算法需要做变更。
[3]在未按下任何按键时,task3可以一直做释放LED硬件资源给task1和task2的操作,即使手上没有LED资源也不会陷入阻塞,而是直接执行下一条语句(当然,在实际项目中通常不建议这么做,此处仅仅是为了功能演示),task1和task2哪个先结束阻塞,哪个就先调用ulTaskNotifyTake函数等待任务通知,或者已经调用ulTaskNotifyTake函数但之前未等待到通知而进入无限阻塞,但task3一旦分配资源以后就能立刻被唤醒。
[4]在按下按键1之后,task3不做资源分配操作,task1和task2只能无限等待任务通知,无法执行后续的任何操作。
[5]在按下按键2之后(以下执行流程图的情形为按下按键1后再按下按键2),task3可以继续一直做释放LED硬件资源给task1和task2的操作。
void task1(void)
{while(1){ulTaskNotifyTake(pdTRUE, portMAX_DELAY); //接收LED硬件资源的使用通知LED1_Turn(); //LED1状态翻转vTaskDelay(500); //延时(自我阻塞)500ms}
}void task2(void)
{while(1){ulTaskNotifyTake(pdTRUE, portMAX_DELAY); //接收LED硬件资源的使用通知LED2_Turn(); //LED2状态翻转vTaskDelay(1000); //延时(自我阻塞)1000ms}
}void task3(void)
{uint8_t Key_memory, key = 0;while(1){key = Key_GetNum(); //读取按键键值if(key != 0)Key_memory = key;if(Key_memory == 1);if(Key_memory == 2 || Key_memory == 0){xTaskNotifyGive(task1_handler); //释放LED硬件资源给task1xTaskNotifyGive(task2_handler); //释放LED硬件资源给task2}vTaskDelay(10); //延时(自我阻塞)10ms}
}
(3)程序完善好后点击“编译”,然后将程序下载到开发板上,根据程序注释进行调试。
相关文章:

FreeRTOS从入门到精通 第十六章(任务通知)
参考教程:【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili 一、任务通知简介 1、概述 (1)任务通知顾名思义是用来通知任务的,任务控制块中的结构体成员变量ulNotifiedValue就是这个通知值。 (2&#…...

TensorFlow 简单的二分类神经网络的训练和应用流程
展示了一个简单的二分类神经网络的训练和应用流程。主要步骤包括: 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与部署 加载和应用已训练的模型 1. 数据准备与预处理 在本例中,数据准备是通过两个 Numpy 数…...

无人机图传模块 wfb-ng openipc-fpv,4G
openipc 的定位是为各种模块提供底层的驱动和linux最小系统,openipc 是采用buildroot系统编译而成,因此二次开发能力有点麻烦。为啥openipc 会用于无人机图传呢?因为openipc可以将现有的网络摄像头ip-camera模块直接利用起来,从而…...
.cc扩展名是什么语言?C语言必须用.c为扩展名吗?主流编程语言扩展名?Java为什么不能用全数字的文件名?
.cc扩展名是什么语言? .cc是C语言使用的扩展名,一种说法是它是c with class的简写,当然C语言使用的扩展名不止.cc和.cpp, 还包含.cxx, .c, .C等,这些在不同编译器系统采用的默认设定不同,需要区分使用。当然,编译器提…...

【MyDB】4-VersionManager 之 3-死锁及超时检测
【MyDB】4-VersionManager 之 3-死锁及超时检测 死锁及超时检测案例背景LockTable锁请求与等待管理 addvm调用addputIntoList,isInList,removeFromList 死锁检测 hasDeadLock方法资源释放与重分配 参考资料 死锁及超时检测 本章涉及代码:top/…...

【Linux】使用管道实现一个简易版本的进程池
文章目录 使用管道实现一个简易版本的进程池流程图代码makefileTask.hppProcessPool.cc 程序流程: 使用管道实现一个简易版本的进程池 流程图 代码 makefile ProcessPool:ProcessPool.ccg -o $ $^ -g -stdc11 .PHONY:clean clean:rm -f ProcessPoolTask.hpp #pr…...
【OpenGL】OpenGL游戏案例(二)
文章目录 特殊效果数据结构生成逻辑更新逻辑 文本渲染类结构构造函数加载函数渲染函数 特殊效果 为提高游戏的趣味性,在游戏中提供了六种特殊效果。 数据结构 PowerUp 类只存储存活数据,实际逻辑在游戏代码中通过Type字段来区分执行 class PowerUp …...
28. 【.NET 8 实战--孢子记账--从单体到微服务】--简易报表--报表定时器与报表数据修正
这篇文章是《.NET 8 实战–孢子记账–从单体到微服务》系列专栏的《单体应用》专栏的最后一片和开发有关的文章。在这片文章中我们一起来实现一个数据统计的功能:报表数据汇总。这个功能为用户查看月度、年度、季度报表提供数据支持。 一、需求 数据统计方面&…...
Java 泛型<? extends Object>
在 Java 泛型中,<? extends Object> 和 <?> 都表示未知类型,但它们在某些情况下有细微的差异。泛型的引入是为了消除运行时错误并增强类型安全性,使代码更具可读性和可维护性。 在 JDK 5 中引入了泛型,以消除编译时…...

FPGA|使用quartus II通过AS下载POF固件
1、将开发板设置到AS下载挡位,或者把下载线插入到AS端口 2、打开quartus II,选择Tools→Programmer→ Mode选择Active Serial Programming 3、点击左侧Add file…,选择 .pof 文件 →start 4、勾选program和verify(可选࿰…...

“新月之智”智能战术头盔系统(CITHS)
新月人物传记:人物传记之新月篇-CSDN博客 相关文章链接(更新): 星际战争模拟系统:新月的编程之道-CSDN博客 新月智能护甲系统CMIA--未来战场的守护者-CSDN博客 目录 一、引言 二、智能头盔控制系统概述 三、系统架…...
php:代码中怎么搭建一个类似linux系统的crontab服务
一、前言 最近使用自己搭建的php框架写一些东西,需要用到异步脚本任务的执行,但是是因为自己搭建的框架没有现成的机制,所以想自己搭建一个类似linux系统的crontab服务的功能。 因为如果直接使用linux crontab的服务配置起来很麻烦࿰…...

【LeetCode: 958. 二叉树的完全性检验 + bfs + 二叉树】
🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…...
MinDoc 安装与部署
下载可执行文件 mindoc mindoc_linux_amd64.zip 上传并解压压缩包 cd /opt mkdir mindoc cd mindocunzip mindoc_linux_amd64.zip 创建数据库 CREATE DATABASE mindoc_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; 配置数据库 将解压目录下 conf/app.conf.exam…...
从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础组件实现)
目录 基础组件实现 如何将图像和文字显示到OLED上 如何绘制图像 如何绘制文字 如何获取字体? 如何正确的访问字体 如何抽象字体 如何绘制字符串 绘制方案 文本绘制 更加方便的绘制 字体附录 ascii 6x8字体 ascii 8 x 16字体 基础组件实现 我们现在离手…...
windows系统如何检查是否开启了mongodb服务
windows系统如何检查是否开启了mongodb服务!我们有很多软件开发,网站开发时候需要使用到这个mongodb数据库,下面我们看看,如何在windows系统内排查,是否已经启动了本地服务。 在 Windows 系统上,您可以通过…...

VS安卓仿真器下载失败怎么办?
如果网络不稳定,则VS的安卓仿真器很容易下载失败,如下 Downloaded file <USER_HOME>\AppData\Local\Temp\xamarin-android-sdk\x86_64-35_r08.zip not found for Android SDK archive https://dl.google.com/android/repository/sys-img/google_a…...
计算机网络一点事(24)
TCP可靠传输,流量控制 可靠传输:每字节对应一个序号 累计确认:收到ack则正确接收 返回ack推迟确认(不超过0.5s) 两种ack:专门确认(只有首部无数据) 捎带确认(带数据…...
视频拼接,拼接时长版本
目录 视频较长,分辨率较大,这个效果很好,不耗用内存 ffmpeg imageio,适合视频较短 视频较长,分辨率较大,这个效果很好,不耗用内存 ffmpeg import subprocess import glob import os from nats…...
制造企业的成本核算
一、生产成本与制造费用的区别 (1)生产成本,是直接用于产品生产,构成产品实体的材料成本。 包括企业在生产经营过程中实际消耗的原材料、辅助材料、备品备件、外购半成品、燃料、动力包装物以及其它直接材料,和直接参加产品生产的工人工资,以及按生产工人的工资总额和规…...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...