利用STM32CubeMX和Keil模拟器,3天入门FreeRTOS(4.0) —— 动态创建队列

前言

(1)FreeRTOS是我一天过完的,由此回忆并且记录一下。个人认为,如果只是入门,利用STM32CubeMX是一个非常好的选择。学习完本系列课程之后,再去学习网上的一些其他课程也许会简单很多。
(2)本系列课程是使用的keil软件仿真平台,所以对于没有开发板的同学也可也进行学习。
(3)叠甲,再次强调,本系列课程仅仅用于入门。学习完之后建议还要再去寻找其他课程加深理解。
(4)本系列博客对应代码仓库:gitee仓库

为什么需要队列

(1)在前文的 同步与互斥的缺陷中,我们讲解了当两个任务都需要调用printf()函数时候,会存在内容覆盖的情况。为了防止出现这类问题,FreeRTOS提出了一个解决办法,就是队列。
(2)我们举个例子,假如老板树懒有两个员工,一个兔子和一个乌龟。
<1>兔子和乌龟都需要在网络上提交自己的工作内容,而老板树懒工作速度慢,而且他们公司的网络系统只能接受一个工作内容。那么因为兔子的工作效率高,频繁的提交工作内容,这就会导致兔子提交的内容会覆盖掉乌龟的工作内容。因此,全公司似乎就只有兔子在干活,乌龟是一个可有可无的员工。
<2>为了解决这个问题,公司的网络系统升级了,可以接受多个工作内容提交。那么当乌龟提交完内容之后的瞬间,兔子再去提交内容,就会自动的放在乌龟提交的内容后面。可以理解为一个传送带,每次提交数据都是放在传送带上。最终传送带将内容运送给树懒。
(3)从上面的例子中,我们就可以明白为什么需要存在队列,以及队列的作用了。

前期准备

(1)将3.0章节的工程复制一份。

在这里插入图片描述

(2)在freertos.c中包含头文件queue.h

在这里插入图片描述

(3)本次实验将会使用到两个按键,因此将PA0PA1设置为下拉输入。

在这里插入图片描述

实战

使用STM32CubeMX创建队列

(1)按照下图方式创建动态创建一个队列

在这里插入图片描述

使用keil端创建队列

(1)按Ctrl+F搜索BEGIN Variables即可找到如下代码块,进行补充。

/* USER CODE BEGIN Variables */
QueueHandle_t KeilQueueHandle; //Keil端创建的队列句柄
/* USER CODE END Variables */

(2)按Ctrl+F搜索BEGIN RTOS_QUEUES即可找到如下代码块,进行补充。

/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
KeilQueueHandle   = xQueueCreate(10, sizeof(uint16_t));
/* USER CODE END RTOS_QUEUES */

具体任务代码补充

(1)因为STM32CubeMXKeil所创建的队列最终都是只需要操作一个队列的句柄,所以我这里就只以Keil所创建的队列句柄举例子了。(注意,如果像以STM32CubeMX所创建的队列举例,只需要将KeilQueueHandle修改为CubemxQueueHandle
(2)按Ctrl+F搜索StartCubemxTask即可找到如下代码块,进行补充。
(3)如下代码需要做的是,介绍:
<1>当K0按下,插入数据。当K1按下,读取数据。
<2>我们通过修改宏,设置要进行的实验操作

/* USER CODE END Header_StartCubemxTask */
#define Test_xQueueSendToFront   1
#define Test_xQueueSendToBack    0
#define Test_xQueueOverwrite     0
#define Test_xQueueReset         0
void StartCubemxTask(void *argument)
{
  /* USER CODE BEGIN StartCubemxTask */
	char *CubemxTaskPrintf = (char *)argument;
	uint16_t Buf = 0;
	BaseType_t status;
  /* Infinite loop */
  for(;;)
  {
		if (HAL_GPIO_ReadPin(Key_0_GPIO_Port, Key_0_Pin) == GPIO_PIN_SET)
		{
			Buf++;
#if Test_xQueueSendToFront
			// 写实验1:测试头插xQueueSendToFront()函数
			status = xQueueSendToFront(KeilQueueHandle, &Buf, 0);
			if (status == pdTRUE)
			{
				printf("xQueueSendToFront writes data successfully : %d\r\n", Buf);
			}
			else
			{
				printf("xQueueSendToFront failed to write data\r\n");
			}
#elif Test_xQueueSendToBack
			// 写实验2:测试尾插xQueueSendToBack()函数
			status = xQueueSendToBack(KeilQueueHandle, &Buf, 0);
			if (status == pdTRUE)
			{
				printf("xQueueSendToBack writes data successfully : %d\r\n", Buf);
			}
			else
			{
				printf("xQueueSendToBack failed to write data\r\n");
			}
#elif Test_xQueueOverwrite
			// 写实验3:测试xQueueOverwrite()函数,这个只能用于队列大小为1的情况。不为1的队列使用这个,程序会崩溃
			xQueueOverwrite(KeilQueueHandle, &Buf);
			printf("xQueueOverwrite writes data successfully : %d\r\n", Buf);
#elif Test_xQueueReset
			// 写实验4:测试尾插xQueueReset()函数
			status = xQueueSendToBack(KeilQueueHandle, &Buf, 0);
			if (status == pdTRUE)
			{
				printf("xQueueSendToBack writes data successfully : %d\r\n", Buf);
			}
			else
			{
				printf("xQueueSendToBack failed to write data\r\n");
			}
			xQueueReset(KeilQueueHandle);
#endif
			//查询队列中存储的消息数
			status = uxQueueMessagesWaiting(KeilQueueHandle);
			printf("The number of data stored in the queue : %d\r\n",status);
			//注意,在RTOS中,还使用阻塞的方式判断事件,无疑是非常愚蠢的。但是因为这个涉及后面内容,因此暂时使用这个做例子
			while (HAL_GPIO_ReadPin(Key_0_GPIO_Port, Key_0_Pin) == GPIO_PIN_SET ); 
		}
  }
  /* USER CODE END StartCubemxTask */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
int fputc(int ch, FILE *f)
{
	unsigned char temp[1]={ch};
	HAL_UART_Transmit(&huart1,temp,1,0xffff);
	return ch;
}
#define Test_xQueueReceive       1
#define Test_xQueueSendToBack    0
void StartKeilTask(void *argument)
{
	uint16_t Buf = 0;
	BaseType_t status;
	while(1)
	{
#if Test_xQueueReceive
		// 按下K1读取数据
		if (HAL_GPIO_ReadPin(Key_1_GPIO_Port, Key_1_Pin) == GPIO_PIN_SET )
		{
			// 读实验1:从队列头部读取消息,并删除消息
			status = xQueueReceive(KeilQueueHandle, &Buf, 0);
			if (status == pdTRUE)
			{
				printf("KeilQueueHandle data read successfully :%d\r\n", Buf);
			}
			else
			{
				printf("KeilQueueHandle data read failed\r\n");
			}
#elif Test_xQueuePeek
			// 读实验2:从队列头部读取消息,但是不删除消息
			status = xQueuePeek(KeilQueueHandle, &Buf, 0);
			if (status == pdTRUE)
			{
				printf("xQueuePeek data read successfully :%d\r\n", Buf);
			}
			else
			{
				printf("xQueuePeek data read failed\r\n");
			}
#endif
			//查询队列中的可用空间数
			status = uxQueueSpacesAvailable(KeilQueueHandle);
			printf("There is space left in the queue : %d\r\n",status);
			//注意,在RTOS中,还使用阻塞的方式判断事件,无疑是非常愚蠢的。但是因为这个涉及后面内容,因此暂时使用这个做例子
			while (HAL_GPIO_ReadPin(Key_1_GPIO_Port, Key_1_Pin) == GPIO_PIN_SET ); 
		}
	}
}
/* USER CODE END Application */

测试结果

(1)因为这个读函数就有2个,写函数就有4个,一共8种组合。全写出来太费劲了。对具体任务感兴趣的,可以直接拿我写好的工程代码实测。
(2)如下为仿真器相关配置
<1>打开微库,因为我们需要使用printf()函数。

在这里插入图片描述

<2>配置为软件仿真
DARMSTM.DLL
pSTM32F103C8

在这里插入图片描述

(3)按照如下方法进行模拟按键按下和松开。然后你根据你想测试的实验,打开相关注释即可。

在这里插入图片描述

函数介绍

队列创建

xQueueCreate()函数介绍

(1)动态创建队列。
(2)参数介绍:
<1>队列的长度。通俗来说,就是这条队伍最大你想要排多少人。
<2>队列的数据所占字节。通俗点说,就是这个队伍中,每个人之间的间距是多少。
(3)从上面的解释我们就可以得出,队列最终的所占字节为uxQueueLength * uxItemSize
(4)uxItemSize参数注意事项:
<1>这里需要注意一点的是,uxItemSize是数据的所占字节数,因此当你传入sizof(float)sizof(int)本质上是一个效果。(假设是32位系统)至于最终队列解析为float还是int,不由队列决定。
<2>这就理解为,这个队列中有两个体形硕大的人,队列中的uxItemSize只需要让这两个人能够站进来,至于这两个人,是男是女,长多高,年龄多大,都和队列无关。只是和最终处理这个队列数据的人有关系。

/**
 * @brief  动态创建队列
 *
 * @param  uxQueueLength    队列的长度
 *        -uxItemSize       队列中每个数据的所占字节
 *
 * @return  队列创建成功,返回所创建队列的句柄。创建队列创建失败,返回 NULL
 */
QueueHandle_t xQueueCreate( uxQueueLength, uxItemSize );

队列删除

vQueueDelete()函数介绍

(1) 释放分配用于存储放置在队列中的项目的所有内存

/**
 * @brief   释放分配用于存储放置在队列中的项目的所有内存
 *
 * @param   xQueue   要删除的队列的句柄
 *
 * @return  无
 */
void vQueueDelete( QueueHandle_t xQueue );

向队列写数据

xQueueSend()和xQueueSendToBack()函数介绍

(1)xQueueSend()xQueueSendToBack()是同一个东西,至于为什么要弄两个一模一样的函数,可能是一开始只有尾插xQueueSend()。然后后来又推出了头插xQueueSendToFront()函数,为了做对比就再弄了一个尾插xQueueSendToBack()
(2)这两个函数,就是在队列中末尾加入数据。这个可以理解为一个遵守纪律的人,自觉排到队尾。
(3)传入参数解释:
<1>xQueue:队列的句柄。
<2>pvItemToQueue:指向待入队数据项的指针。

  • 这里需要注意,传入的数据项内容大小,不应当超出创建队列时候指定的数据所占字节大小。否则会出现数据截断情况。
  • 如果你创建队列时候,使用的是sizof(float),而你这里传入一个int型数据。对于xQueueSend()xQueueSendToBack()函数而言,是可行的,因为他们都是4字节。
  • 但是对于整个工程项目来说,是危险的。因为最终读取数据的时候,int型数据和float型数据混搭在队列中,我们很难知道这个取出来的到底是int型数据还是float型数据。
  • 如果不理解,举个医院看病的例子。我们排队的时候,人与人之间的间隔距离(队列的数据所占字节)只和人的体形大小(传入队列的数据所占字节)有关。至于这个人是男是女(int还是float),和队列是没有关系的。那么,这样就会出现一个问题,一个男性胖子,排队的时候排到了妇产科。坐在里面的医生是不知道的,因此就会出问题。

<3>xTicksToWait: 如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。

  • 如果设置为 0,调用将立即返回。如果队列已满,将直接返回errQUEUE_FULL,并不会将任务进如阻塞态。
  • 时间以滴答周期为单位定义,因此一般用常量 portTICK_PERIOD_MS 转换为实时。例如,队列已满,我们等待100ms,那么就传入100/portTICK_PERIOD_MS
  • 如果想要一直阻塞,直到队列有空位置才结束,那么传入portMAX_DELAY。这里需要注意,需要在FreeRTOSConfig.h中将INCLUDE_vTaskSuspend 设置为 “1”
/**
 * @brief  队列数据尾插
 *
 * @param  xQueue         队列的句柄
 *        -pvItemToQueue  指向待入队数据项的指针,每次只能插入一个数据
 *        -xTicksToWait   如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果设置为 0,调用将立即返回。时间以滴答周期为单位定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。
 *
 * @return  如果数据尾插成功,返回 pdTRUE,否则返回 errQUEUE_FULL。
 */
BaseType_t xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) ;

xQueueSendToFront()函数介绍

(1)这个函数与xQueueSendToBack()相反的,xQueueSendToBack()是当数据需要进入队列的时候,自觉的排到最后面。而xQueueSendToFront()却是不知羞耻的插队到最前面。
(2)传入的参数解释同上,返回值同上。

/**
 * @brief  队列数据头插
 *
 * @param  xQueue         队列的句柄
 *        -pvItemToQueue  指向待入队数据项的指针,每次只能插入一个数据
 *        -xTicksToWait   如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果设置为 0,调用将立即返回。时间以滴答周期为单位定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。
 *
 * @return  如果数据头插成功,返回 pdTRUE,否则返回 errQUEUE_FULL。
 */
BaseType_t xQueueSendToFront( QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t xTicksToWait );

xQueueOverwrite()函数介绍

(1)这个函数旨在用于长度为 1 的队列, 这意味着队列要么为空,要么为满。这个长度为1的队列还有一种专业术语,叫做邮箱
(2)如果队列长度大于1,程序就会卡死。因为我现在手上没有开发板,所以无法上机实测具体是卡死在哪里了。软件仿真的结果只是知道程序崩了。

/**
 * @brief  以覆盖的方式将数据传入队列,旨在用于长度为 1 的队列, 这意味着队列要么为空,要么为满
 *
 * @param  xQueue         队列的句柄
 *        -pvItemToQueue  指向待入队数据项的指针
 *
 * @return  如果数据头插成功,返回 pdTRUE,否则返回 errQUEUE_FULL
 */
 BaseType_t xQueueOverwrite(QueueHandle_t xQueue,const void * pvItemToQueue);

xQueueReset()函数介绍

(1)将队列重置为其原始的空状态,队列中的所有数据都将会被清除。

/**
 * @brief  将队列重置为其原始的空状态
 *
 * @param  xQueue         队列的句柄
 *
 * @return  总是返回 pdPASS
 */
BaseType_t xQueueReset( QueueHandle_t xQueue );

向队列读数据

xQueueReceive()函数介绍

(1)从队列头部读取消息,并删除消息。
(2)需要注意的是,pvItemToQueue的数据类型要和队列中的数据类型可以不一致,只要数据大小一致即可,否则可能会出现数据截断情况。但是不建议这么用,因为xQueueReceive()就是上面xQueueSendToBack()函数介绍中举例的医生。你一个妇产科医生,想整花活,诊断男科。主打一个猝不及防。

/**
 * @brief  从队列头部读取消息,并删除消息
 *
 * @param  xQueue         队列的句柄
 *        -pvItemToQueue  指向缓冲区的指针
 *        -xTicksToWait   如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果设置为 0,调用将立即返回。时间以滴答周期为单位定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)
 *
 * @return  如果从队列成功接收到项目,返回 pdTRUE,否则返回 pdFALSE
 */
 BaseType_t xQueueReceive(QueueHandle_t xQueue,void *pvBuffer,TickType_t xTicksToWait);

xQueuePeek()函数介绍

(1)从队列头部读取消息,但是不删除消息。这个函数和xQueueReceive()唯一区别在于,xQueueReceive()读取数据的同时,还会把数据从队列中删除。而xQueuePeek()仅仅只会读数据,数据并不会被删除。

/**
 * @brief  从队列头部读取消息,但是不删除消息
 *
 * @param  xQueue         队列的句柄
 *        -pvItemToQueue  指向缓冲区的指针
 *        -xTicksToWait   如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果设置为 0,调用将立即返回。时间以滴答周期为单位定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)
 *
 * @return  如果从队列成功接收到项目,返回 pdTRUE,否则返回 pdFALSE
 */
 BaseType_t xQueuePeek(QueueHandle_t xQueue,void *pvBuffer,TickType_t xTicksToWait);

获取队列数据数量信息

uxQueueSpacesAvailable()函数介绍

(1)查询队列中的可用空间数。

/**
 * @brief  查询队列中的可用空间数
 *
 * @param  xQueue         队列的句柄
 *
 * @return  返回队列中的可用空间数
 */
UBaseType_t uxQueueSpacesAvailable( QueueHandle_t xQueue );

uxQueueMessagesWaiting()函数介绍

(1)查询队列中存储的消息数

/**
 * @brief  查询队列中存储的消息数
 *
 * @param  xQueue         队列的句柄
 *
 * @return  返回队列中存储的消息数
 */
UBaseType_t uxQueueSpacesAvailable( QueueHandle_t xQueue );

参考

(1)FreeRTOS官方文档:队列管理

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于YOLOv8的摔倒行为检测系统(Python源码+Pyqt6界面+数据集)

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文主要内容:通过实战基于YOLOv8的摔倒行为检测算法&#xff0c;从数据集制作到模型训练&#xff0c;最后设计成为检测UI界面 人体行为分析AI算法&#xff0c;是一种利用人工智能技术对人体行为进行检测、跟踪和分析的方法。通过计算…

MATLAB知识点:mode :计算众数

​讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章 3.4.1节 mode &#xff1a;计算众数 众数是指一…

log4j2配置文件命名及优先级

log4j 2.x版本不再支持像1.x中的.properties后缀的文件配置方式&#xff0c;2.x版本配置文件后缀名只能为".xml",“.json"或者”.jsn"。 命名规则 默认配置文件名&#xff1a; log4j2.xml 或 log4j2.json 测试或特定环境配置文件名&#xff1a;可以以 -t…

(blender学习)blender和vscode联合编写代码

文章目录 一、建立联合二、代码1.做一个小panel2. 给选定的物体更换好多好多材质 一、建立联合 按照这个文档做&#xff1a; 【Blender】使用 Microsoft Visual Studio Code 作为外部 IDE 来编写 Blender 脚本/附加组件 二、代码 下面几个代码的学习视频来自&#xff1a;ht…

Linux中并发程序设计(进程的创建和回收、exec函数使用)

进程的创建和回收 进程概念 概念 程序 存放在磁盘上的指令和数据的有序集合&#xff08;文件&#xff09; 静态的 进程 执行一个程序所分配的资源的总称 动态的进程和程序比较 注&#xff1a;进程是存在RAM中&#xff0c;程序是存放在ROM(flash)中的进程内容 BSS段&#xff…

Rocky8 顺利安装 Airflow 并解决数据库报错问题

rocky是替代centos的服务器系统&#xff0c;稳定可靠。rocky8会比centos7新&#xff0c;可以支持更多服务软件的安装&#xff0c;免去升级各种库的麻烦&#xff0c;本文运行airflow服务就用rocky8系统。airflow是一个定时任务管理系统&#xff0c;功能强大&#xff0c;目前是ap…

【QT+QGIS跨平台编译】之七:【libjpeg+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、libjpeg介绍二、文件下载三、文件分析四、pro文件五、编译实践一、libjpeg介绍 libjpeg是一个广泛使用的jpeg图像压缩和解压的函数库,采用 C 语言开发。 2013年1月,Independent JPEG Group发布了版本9,对新引入的无损编码模式进行了改进。2022年1月,发布了版…

Android U 配置 WiFiCalling 场景下PLMN/SPN 显示的代码逻辑介绍

功能介绍 根据设备的网络连接情况更新状态栏显示的运营商及网络状态。 注册上WFC&#xff08;WiFi Calling&#xff09;后&#xff0c;支持客制化显示左上角状态栏中的运营商网络状态信息 。具体的代码逻辑在CarrierDisplayNameResolver.java。 ServiceStateTracker 网络状态…

C# Bitmap类学习1

Bitmap对象封装了GDI中的一个位图&#xff0c;此位图由图形图像及其属性的像素数据组成.因此Bitmap是用于处理由像素数据定义的图像的对象。 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using …

程序媛的mac修炼手册-- 如何用Python节省WPS会员费

上篇分享了如何用微博爬虫&#xff0c;咱举例爬了女明星江疏影的微博数据。今天就用这些数据&#xff0c;给大家安利一下怎么用Python实现WPS中部分Excel付费功能。 MacOS系统自带的工具&#xff0c;绝大多数都非常顶&#xff0c;除Numbers外。当然&#xff0c;page比起word来&…

图像分类】【深度学习】【轻量级网络】【Pytorch版本】EfficientNet_V2模型算法详解

【图像分类】【深度学习】【轻量级网络】【Pytorch版本】EfficientNet_V2模型算法详解 文章目录 【图像分类】【深度学习】【轻量级网络】【Pytorch版本】EfficientNet_V2模型算法详解前言EfficientNet_V2讲解自适应正则化的渐进学习(Progressive Learning with adaptive Regul…

魔众题库系统v9.3.0版本:升级与新功能亮点,让学习更高效!

大家好&#xff01;今天我们激动地向大家宣布&#xff0c;魔众题库系统已经升级到了v9.3.0版本&#xff01;这个版本带来了许多令人兴奋的改进和新功能&#xff0c;让用户的使用体验更上一层楼。 首先&#xff0c;让我们来看看这个版本的VIP界面升级。无论是PC端还是移动端&am…

C/C++ - 编程语法特性

目录 标准控制台框架 输入输出对象 命名空间 标准控制台框架 头文件 ​#include <iostream>​​ 告诉编译器我们要使用iostream库尖括号中的名字指定了某个头文件(header) 入口函数 ​int main(void)​​ 返回 ​return 0;​​ 输出语句 ​std::cout << "H…

python基础——锁

进程锁 (互斥锁) 进程锁的引入&#xff1a; 模拟抢票程序&#xff1a; from multiprocessing import Process import json import time def show_ticket(i):with open("./tickets.txt",mode"r",encoding"utf-8") as file:ticket json.load(f…

k8s图形化管理工具之rancher

前言 在前面的k8s基础学习中,我们学习了各种资源的搭配运用,以及命令行,声明式文件创建。这些都是为了k8s管理员体会k8s的框架,内容基础。在真正的生产环境中,大部分的公司还是会选用图形化管理工具来管理k8s集群,大大提高工作效率。 在二进制搭建k8集群时,我们就知道了…

Spring依赖注入之setter注入与构造器注入以及applicationContext.xml配置文件特殊值处理

依赖注入之setter注入 在管理bean对象的组件的时候同时给他赋值&#xff0c;就是setter注入&#xff0c;通过setter注入&#xff0c;可以将某些依赖项标记为可选的&#xff0c;因为它们不是在构造对象时立即需要的。这种方式可以减少构造函数的参数数量&#xff0c;使得类的构…

程序员的自我修养:链接、装载与库 6 可执行文件的装载与进程

1 进程虚拟地址空间 PAE 2 装载的方式 2.1 覆盖装入 省略 178 2.2 页映射 3 从操作系统角度看可执行文件的装载 3.1 进程的建立 182

【必剪】鬼畜rap和鬼畜剧场的区别?

在【选择素材】中&#xff0c;每个素材下会有一个标签显示支持哪种的鬼畜形式&#xff0c;在点击一个两种格式的有【鬼畜剧场】和【鬼畜rap】这两中的主要区别在于 【鬼畜剧场】&#xff1a;对素材进行人工编排&#xff0c;创作自己原创的剧情作 【鬼畜rap】&#xff1a;对于素…

IO多路复用-poll(附通信代码)

IO多路复用-poll 1. poll函数 和select函数的比较 内核对应文件描述符的检测也是以线性的方式进行轮询&#xff0c;根据描述符的状态进行处理poll和select检测的文件描述符集合会在检测过程中频繁的进行用户区和内核区的拷贝&#xff0c;它的开销随着文件描述符数量的增加而…

恒峰配网行波型故障预警定位装置特点及优势

随着电力系统的不断发展&#xff0c;电网运行的安全性和稳定性对于国家经济和人民生活至关重要。为了提高电网运行的可靠性&#xff0c;减少故障发生的可能性&#xff0c;我国电力行业不断引进新技术、新设备&#xff0c;其中配网行波型故障预警定位装置在电网安全领域发挥着越…