FreeRTOS源码分析-9 互斥信号量

目录

1 优先级翻转问题

2 互斥信号量概念及其应用

2.2FreeRTOS互斥信号量介绍

2.3FreeRTOS互斥信号量工作原理

3 互斥信号量函数应用

3.1功能分析

3.2API详解

3.3功能实现

4 递归互斥信号量函数应用

4.1死锁现象

​编辑

4.2API详解

4.3解决死锁

5 互斥信号量实现原理

5.1互斥信号量创建

5.2互斥信号量获取&释放

5.3优先级继承原理

5.4递归互斥信号量获取&释放


1 优先级翻转问题

二值信号量中一个bug

低优先级任务可以被高优先级任务抢占,但是如果这个时候低优先级任务占用信号量,那么高优先级任务会阻塞。这时候中优先级抢占了低优先级的任务,再等低优先级任务运行完释放信号量,这时候高优先级任务才运行。

上述情况中优先级任务抢占了高优先级任务,系统会出现问题,保证不了了任务的实时性。

功能需求

  • 新建三个任务,优先级分别为中高低
  • 新建二值信号量,用于模拟优先级翻转
  • 低优先级任务获取信号量后,被中优先级打断,中优先级任务执行时间较长,因为低优先级任务还未释放信号量,高优先级任务就无法获取信号量继续运行

使用接口在低优先级任务中模拟调度 ,内部其实就是PendSV

//创建二值信号量
osSemaphoreDef(PrBinarySem);
PrBinarySemHandle = osSemaphoreCreate(osSemaphore(PrBinarySem), 1);//创建low、nomal、high任务osThreadDef(DelayTask, Delay_Task, osPriorityLow, 0, 128);DelayTaskHandle = osThreadCreate(osThread(DelayTask), NULL);osThreadDef(LedTask, Led_Task, osPriorityNormal, 0, 128);LedTaskHandle = osThreadCreate(osThread(LedTask), NULL);osThreadDef(HighTask, High_Task, osPriorityHigh, 0, 128);HighTaskHandle = osThreadCreate(osThread(HighTask), NULL);uint32_tvoid Delay_Task(void const * argument)
{for(;;){printf("Low Task Take Sem\r\n");  if(xSemaphoreTake(PrBinarySemHandle,portMAX_DELAY) == pdPASS){printf("Low Task is Runing\r\n");}//为了保证中优先级任务有足够时间抢占抢占for(i=0;i<2000000;i++){taskYIELD();}printf("Low Task Give Sem\r\n");  xSemaphoreGive(PrBinarySemHandle);osDelay(500);}
}void Led_Task(void const * argument)
{for(;;){printf("Normal Task is Runing\r\n");osDelay(500);}
}void High_Task(void const * argument)
{for(;;){printf("High Task Take Sem\r\n");  if(xSemaphoreTake(PrBinarySemHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}printf("High Task Give Sem\r\n");  xSemaphoreGive(PrBinarySemHandle);osDelay(500);}
}

2 互斥信号量概念及其应用

2.1互斥信号量概念

用于解决优先级反转问题

当高优先级任务运行时,如果信号量被低优先级任务获取,那么临时提高低优先级任务。

 但是二值信号量是共有的,所有任务都可以获取。 

互斥信号量特性:

  • 优先级继承
  • 任务独享公共资源

2.2FreeRTOS互斥信号量介绍

Mutex 互斥信号量

RecursiveMuxtex 互斥信号量(解决普通信号量的死锁问题)

2.3FreeRTOS互斥信号量工作原理

 互斥信号量工作原理即特性

  • 优先级继承
  • 任务独享公共资源

 

上述伪代码,再调用bar申请了互斥信号,当再去调用foo的时候又去申请了互斥信号量,但是bar的互斥信号量还未解锁,造成了死锁。即如果调用2次即死锁。

递归信号量即每次lock一次+1,每次解锁-1,解决死锁问题。当lock次数为0,即恢复原有状态。

3 互斥信号量函数应用

3.1功能分析

  • 1、修改优先级翻转实验
  • 2、使用互斥信号量,解决优先级翻转问题

3.2API详解

GetmutexHolder是独有的api,查看当前是谁独占了资源。如果没有被占用,我们即可获取信号量,使用take(take和give都是标准的api)

3.3功能实现

使能互斥锁

 

生产工程代码

void MX_FREERTOS_Init(void) {osMutexDef(PrMutex);PrMutexHandle = osMutexCreate(osMutex(PrMutex));//略 ...}void Led_Task(void const * argument)
{for(;;){printf("Normal Task is Runing\r\n");osDelay(500);}
}void Delay_Task(void const * argument)
{for(;;){printf("Low Task Take Mutex\r\n");  if(xSemaphoreTake(PrMutexHandle,portMAX_DELAY) == pdPASS){printf("Low Task is Runing\r\n");}for(i=0;i<2000000;i++){taskYIELD();}printf("Low Task Give Mutex\r\n");  xSemaphoreGive(PrMutexHandle);osDelay(500);}
}void High_Task(void const * argument)
{for(;;){printf("High Task Take Mutex\r\n");  //高优先级任务获取互斥锁if(xSemaphoreTake(PrMutexHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}printf("High Task Give Mutex\r\n");  xSemaphoreGive(PrMutexHandle);osDelay(500);}
}

效果

4 递归互斥信号量函数应用

4.1死锁现象

普通信号量只能获取一次,修改一下高优先级任务。

void High_Task(void const * argument)
{for(;;){printf("High Task Take RecursiveMutex 1\r\n");  if(xSemaphoreTake(PrMutexHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}//再次获取信号量if(xSemaphoreTake(PrMutexHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}printf("High Task Give RecursiveMutex 2\r\n");  xSemaphoreGiveRecursive(myRecursiveMutexHandle);osDelay(500);}
}

4.2API详解

获取接口(不能再临界区内调用,即模拟的中断中)

释放接口(FAIL,代表持有者不是任务本身)

4.3解决死锁

  • 1、模拟死锁现象
  • 2、使用递归互斥信号量解决死锁问题

 

osMutexId myRecursiveMutexHandle;void MX_FREERTOS_Init(void) 
{osMutexDef(myRecursiveMutex);myRecursiveMutexHandle = osRecursiveMutexCreate(osMutex(myRecursiveMutex));//略 ...
}void Led_Task(void const * argument)
{for(;;){printf("Normal Task is Runing\r\n");osDelay(500);//1msʱ»ù}
}void High_Task(void const * argument)
{for(;;){printf("High Task Take RecursiveMutex 1\r\n");  if(xSemaphoreTakeRecursive(myRecursiveMutexHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}printf("High Task Take RecursiveMutex 2\r\n");  if(xSemaphoreTakeRecursive(myRecursiveMutexHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}printf("High Task Give RecursiveMutex 1\r\n");  xSemaphoreGiveRecursive(myRecursiveMutexHandle);printf("High Task Give RecursiveMutex 2\r\n");  xSemaphoreGiveRecursive(myRecursiveMutexHandle);osDelay(500);}
}void Delay_Task(void const * argument)
{for(;;){printf("Low Task Take RecursiveMutex\r\n");  if(xSemaphoreTakeRecursive(myRecursiveMutexHandle,portMAX_DELAY) == pdPASS){printf("Low Task is Runing\r\n");}for(i=0;i<2000000;i++){taskYIELD();}printf("Low Task Give RecursiveMutex\r\n");  xSemaphoreGiveRecursive(myRecursiveMutexHandle);osDelay(500);}
}

 效果

5 互斥信号量实现原理

5.1互斥信号量创建

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )||||#endifQueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ){Queue_t *pxNewQueue;const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;//创建消息队列/*队列长度:1队列大小:0队列类型:queueQUEUE_TYPE_MUTEX*/pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );//初始化互斥信号量->其实就是初始化消息队列的控制块prvInitialiseMutex( pxNewQueue );return pxNewQueue;}static void prvInitialiseMutex( Queue_t *pxNewQueue ){if( pxNewQueue != NULL ){/*1、信号的持有者为空2、消息队列的类型为互斥信号量3、递归记录初始为04、往消息队列发送一个消息->其实赋值互斥信号量为1*/pxNewQueue->pxMutexHolder = NULL;pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;/* In case this is a recursive mutex. */pxNewQueue->u.uxRecursiveCallCount = 0;traceCREATE_MUTEX( pxNewQueue );/* Start with the semaphore in the expected state. */( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );}else{traceCREATE_MUTEX_FAILED();}}

5.2互斥信号量获取&释放

#define xSemaphoreTake( xSemaphore, xBlockTime )		xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE )/*队列不为空处理>01、判断是否为互斥信号量2、记录当前任务为信号持有者*/#if ( configUSE_MUTEXES == 1 ){if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ){/* Record the information required to implementpriority inheritance should it become necessary. */pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */}else{mtCOVERAGE_TEST_MARKER();}}/*队列为空处理==01、判断是否为互斥信号量2、进入临界段3、优先级继承*/				#if ( configUSE_MUTEXES == 1 ){if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ){taskENTER_CRITICAL();{vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );}taskEXIT_CRITICAL();}else{mtCOVERAGE_TEST_MARKER();}}#endif

#define xSemaphoreGive( xSemaphore )		
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )//互斥信号量处理在数据拷贝接口中xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );/*1、判断是否为互斥信号量2、恢复任务优先级3、信号持有者赋值为空,也就是说其他任务可以获取了*/#if ( configUSE_MUTEXES == 1 ){if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ){/* The mutex is no longer being held. */xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );pxQueue->pxMutexHolder = NULL;}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_MUTEXES */

5.3优先级继承原理

//持有者记录增加
#if ( configUSE_MUTEXES == 1 )void *pvTaskIncrementMutexHeldCount( void ){/* If xSemaphoreCreateMutex() is called before any tasks have been createdthen pxCurrentTCB will be NULL. */if( pxCurrentTCB != NULL ){//持有者任务控制块里 持有记录加一( pxCurrentTCB->uxMutexesHeld )++;}//返回当前任务控制块return pxCurrentTCB;}#endif /* configUSE_MUTEXES *///优先级继承
//参数:持有互斥信号量的任务控制块
#if ( configUSE_MUTEXES == 1 )void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ){TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;//1、任务控制块不为空if( pxMutexHolder != NULL ){//2、优先级小于当前获取信号量的有限,才会去处理继承if( pxTCB->uxPriority < pxCurrentTCB->uxPriority ){/*  */if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ){//3、修改持有者事件列表中,列表项的属性值 为当前任务优先级listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */}else{mtCOVERAGE_TEST_MARKER();}/* 4、判断持有者任务是否在就绪列表中 4.1、移除4.2、修改任务优先级,这个修改是任务控制块里的信息4.3、添加到新的就绪列表中*/if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ){if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){taskRESET_READY_PRIORITY( pxTCB->uxPriority );}else{mtCOVERAGE_TEST_MARKER();}/* Inherit the priority before being moved into the new list. */pxTCB->uxPriority = pxCurrentTCB->uxPriority;prvAddTaskToReadyList( pxTCB );}else{/* Just inherit the priority. */pxTCB->uxPriority = pxCurrentTCB->uxPriority;}traceTASK_PRIORITY_INHERIT( pxTCB, pxCurrentTCB->uxPriority );}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_MUTEXES *///优先级恢复
/*1、
*/
BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ){TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;BaseType_t xReturn = pdFALSE;if( pxMutexHolder != NULL ){//1、持有者任务持有记录减一( pxTCB->uxMutexesHeld )--;/* Has the holder of the mutex inherited the priority of anothertask? *///2、优先级是否修改过if( pxTCB->uxPriority != pxTCB->uxBasePriority ){//3、递归记录为0的时候/* Only disinherit if no other mutexes are held. */if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ){/* 4、从当前就绪列表中移除*/if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){taskRESET_READY_PRIORITY( pxTCB->uxPriority );}else{mtCOVERAGE_TEST_MARKER();}/* 5、恢复任务优先级 */traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority );pxTCB->uxPriority = pxTCB->uxBasePriority;/*6、已经不是持有者,把任务添加到新的就绪列表中去*/listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */prvAddTaskToReadyList( pxTCB );/* 触发上下文切换,释放CPU使用权 */xReturn = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}return xReturn;}

5.4递归互斥信号量获取&释放

#define xSemaphoreGiveRecursive( xMutex )	
xQueueGiveMutexRecursive( ( xMutex ) )/*参数:信号量句柄步骤:1、判断当前任务是否为持有者1.1、递归记录减一1.2、判断记录是否为01.3、发送一个消息2、不为持有者返回错误*/
BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ){BaseType_t xReturn;Queue_t * const pxMutex = ( Queue_t * ) xMutex;if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Not a redundant cast as TaskHandle_t is a typedef. */{( pxMutex->u.uxRecursiveCallCount )--;if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 ){( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );}else{mtCOVERAGE_TEST_MARKER();}xReturn = pdPASS;}else{xReturn = pdFAIL;}return xReturn;}

#if( configUSE_RECURSIVE_MUTEXES == 1 )#define xSemaphoreTakeRecursive( xMutex, xBlockTime )xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
#endif/*参数:互斥信号句柄,超时等待时间步骤:1、判断是否为持有者1.1、递归记录加一1.2、返回成功2、不为持有者2.1、接收消息---获取信号量2.2、获取成功----递归记录加一2.3、获取失败---- 返回失败*/
BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ){BaseType_t xReturn;Queue_t * const pxMutex = ( Queue_t * ) xMutex;if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */{( pxMutex->u.uxRecursiveCallCount )++;xReturn = pdPASS;}else{xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE );/* pdPASS will only be returned if the mutex was successfullyobtained.  The calling task may have entered the Blocked statebefore reaching here. */if( xReturn != pdFAIL ){( pxMutex->u.uxRecursiveCallCount )++;}else{traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );}}return xReturn;}

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

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

相关文章

C++数据结构之平衡二叉搜索树(一)——AVL的实现(zig与zag/左右双旋/3+4重构)

本文目录 00.BBST——平衡二叉搜索树01.AVL树02.AVL的插入2.1单旋——zig 与 zag2.2插入节点后的单旋实例2.3手玩小样例2.4双旋实例2.5小结 03.AVL的删除3.1单旋删除3.2双旋删除3.3小结 04.34重构05.综合评价AVL5.1优点5.2缺点 06.代码注意插入算法删除算法完整代码&#xff1a…

关于接口测试用例设计的一些思考

接口测试发现的典型问题 传入参数处理不当&#xff0c;引起程序错误类型溢出&#xff0c;导致数据读取和写入不一致对象权限校验出错&#xff0c;可获取其他角色信息状态出错&#xff0c;导致逻辑处理出现问题逻辑校验不完善定时任务执行出错 接口测试用例设计 接口测试用例…

redis入门3-在java中操作redis

Redis的java客户端 Jedis、Lettuce、Redisson、以及spring提供的spring data redis Jedis操作redis //添加依赖 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.8.0</version> </dep…

JJWT快速入门

本篇介绍使用 JJWT&#xff08;Java JWT&#xff09;库来生成 JWT Token&#xff0c;步骤如下&#xff1a; 添加依赖&#xff1a; 在项目中添加 JJWT 依赖项。对于 Maven 项目&#xff0c;可以在 pom.xml 文件中添加以下依赖项&#xff1a; <dependency><groupId>…

python解析帆软cpt及frm文件(xml)获取源数据表及下游依赖表

#!/user/bin/evn python import os,re,openpyxl 输入&#xff1a;帆软脚本文件路径输出&#xff1a;帆软文件检查结果Excel#获取来源表 def table_scan(sql_str):# remove the /* */ commentsq re.sub(r"/\*[^*]*\*(?:[^*/][^*]*\*)*/", "", sql_str)# r…

c++学习(特殊类设计)[30]

只能在堆上创建对象的类 如果你想要确保对象只能在堆上创建&#xff0c;可以通过将析构函数声明为私有&#xff0c;并提供一个静态成员函数来创建对象。这样&#xff0c;类的实例化只能通过调用静态成员函数来完成&#xff0c;而无法直接在栈上创建对象。 以下是一个示例&…

【开源项目--稻草】Day03

【开源项目--稻草】Day03 1. 续Spring-Security1.1 自定义登录界面 2. 用户注册2.1 将注册页面显示2.2 编写控制器进行测试2.3 编写注册业务逻辑2.4 注册功能的收尾 3. VUE3.1 VUE的基本使用3.1.1 什么是VUE 3.2 使用VUEAjax完善稻草问答的注册功能 1. 续Spring-Security 1.1 …

Kubespray-offline v2.21.0-1 下载 Kubespray v2.22.1 离线部署 kubernetes v1.25.6

文章目录 1. 目标2. 预备条件3. vcenter 创建虚拟机4. 系统初始化4.1 配置网卡4.2 配置主机名4.3 内核参数 5. 打快照6. 安装 git7. 配置科学8. 安装 docker9. 下载介质9.1 下载安装 docker 介质9.2 下载 kubespray-offline-ansible 介质9.3 下载 kubernetes 介质 10. 搬运介质…

6.物联网操作系统信号量

一。信号量的概念与应用 信号量定义 FreeRTOS信号量介绍 FreeRTOS信号量工作原理 1.信号量的定义 多任务环境下使用&#xff0c;用来协调多个任务正确合理使用临界资源。 2.FreeRTOS信号量介绍 Semaphore包括Binary&#xff0c;Count&#xff0c;Mutex&#xff1b; Mutex包…

【2种方法,jmeter用一个正则提取器提取多个值!】

jmeter中&#xff0c;用json提取器&#xff0c;一次提取多个值&#xff0c;这个很多人都会。但是&#xff0c;用正则提取器一次提取多个&#xff0c;是否可以呢&#xff1f; 肯定&#xff0c;很多人都自信满满的说&#xff0c;可以&#xff01;形如&#xff1a;token":&q…

Jenkins触发器时间、次数设定

触发器触发条件介绍 触发器触发条件公式&#xff1a;由5颗星组成 * * * * * 分别代表&#xff1a;分钟(0-59) 小时(0-23) 日期(1-31) 月份(1-12) 星期(0-6) 企业项目中常用场景介绍 场景1&#xff1a;接口脚本部分测试通过&#xff0c;部分还在进行&#xff0c;回归测试脚本执行…

Windows上安装 jdk 环境并配置环境变量 (超详细教程)

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支…

torchvision.datasets数据加载失败

torchvision.datasets数据加载失败 如何使用torchvision.datasets进行自动下载数据失败&#xff0c;可以使用手动下载数据 Ctrl点击可以进入相关包文件&#xff0c;查找下载地址&#xff1a;https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz 手动下载之后解压&#x…

企业微信小程序在调用wx.qy.login时返回错误信息qy.login:fail

原因是大概是绑定了多个企业但是在开发者工具中没有选择正确的企业 解决方法&#xff1a; 重新选择企业后即可成功获取code

RabbitMQ(一) - 基本结构、SpringBoot整合RabbitMQ、工作队列、发布订阅、直接、主题交换机模式

RabbitMQ结构 Publisher &#xff1a; 生产者 Queue: 存储消息的容器队列&#xff1b; Consumer:消费者 Connection&#xff1a;消费者与消息服务的TCP连接 Channel:信道&#xff0c;是TCP里面的虚拟连接。例如&#xff1a;电缆相当于TCP&#xff0c;信道是一条独立光纤束&…

web开发中的安全和防御入门——csp (content-security-policy内容安全策略)

偶然碰到iframe跨域加载被拒绝的问题&#xff0c;原因是父页面默认不允许加载跨域的子页面&#xff0c;也就是的content-security-policy中没有设置允许跨域加载。 简单地说&#xff0c;content-security-policy能限制页面允许和不允许加载的所有资源&#xff0c;常见的包括&a…

原型链污染

文章目录 1. javascript 原型链2. 原型链变量的搜索3. prototype 原型链污染4. 原型链污染例题4.1 题1&#xff1a;4.2.题2&#xff1a; 1. javascript 原型链 js在ECS6之前没有类的概念&#xff0c;之前的类都是用funtion来声明的。如下 可以看到b在实例化为test对象以后&…

【C语言进阶】指针的高级应用(下)

文章目录 一、指针数组与数组指针1.1 指针数组与数组指针的表达式 二、函数指针2.1 函数指针的书写方式 三、二重指针与一重指针3.1 二重指针的本质3.2 二重指针的用法3.3 二重指针与数组指针 总结 一、指针数组与数组指针 (1)指针数组的实质是一个数组&#xff0c;这个数组中存…

Linux进程(二)

文章目录 进程&#xff08;二&#xff09;Linux的进程状态R &#xff08;running&#xff09;运行态S &#xff08;sleeping&#xff09;阻塞状态D &#xff08;disk sleep&#xff09;深度睡眠T&#xff08;stopped&#xff09;状态X&#xff08;dead&#xff09;状态Z&#x…

SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--搭建Vue 前端工程[一]

文章目录 SSM--搭建Vue 前端工程--项目基础界面实现功能01-搭建Vue 前端工程需求分析/图解代码实现搭建Vue 前端工程下载node.js LTS 并安装: node.js 的npm创建Vue 项目使用idea 打开ssm_vue 项目, 并配置项目启动 Vue3 项目目录结构梳理Vue3 项目结构介绍 配置Vue 服务端口El…