UCOSIII中的消息传递

2023-05-16

文章目录

  • 序言
  • 什么是消息队列
  • 消息队列相关函数
    • OSQCreate()
    • OSQPend()
    • OSQPost()
  • 消息队列实验
  • 总结

序言

前面我们介绍了信号量,通过信号量我们能够解决优先级反转,资源共享冲突等问题,但是我们不难发现,信号量无法携带具体的数据信息。消息队列就是这样一种具体数据信息传递的有效工具。本篇文章的主题也就是消息队列。

本篇文章的结构大概是这样的,我先介绍一下什么是消息队列,然后介绍消息队列在UCOSIII中的函数,最后就是一个实验,我们通过这个实验可以直观地感受一下消息队列在UCOSIII中的具体使用方法及其效用。

注:实验程序来自正点原子

什么是消息队列

在认识消息队列之前,我们要先明白什么是消息,在UCOSIII中,一个消息包含指向数据的指针、该数据的大小、时间戳变量。其中的指针可以指向数据区域或者是一个函数。

消息队列是一个可以包含多个消息的内核对象,就像信号量一样,我们需要定义它,创建它,请求它,释放它。API的使用是很简单的,我们学习的重点是它的工作思想。下面的图片展示了在创建完成消息队列之后,我们可以对消息队列进行的操作。
在这里插入图片描述
从图中我们可以看出,中断服务程序只能使用OSQPost()函数!在UCOSIII中对于消息队列的读取既可以采用FIFO方式,也可以采用LIFO方式,当任务或者中断服务程序需要向任务发送一条紧急消息时,LIFO机制就非常有用。采用后进先出的方式,发布的消息会绕过其它所有的已经位于消息队列中的消息而最先传递给任务。

在上面的图中,OSQPend()函数下面有一个沙漏,它的意思是如果任务在这段时间内没有接收到消息的话就不再继续等待,并且返回一个错误码告诉UCOSIII超时。如果将这个超时时间指定为0的话,任务就一直等待下去,直到接收到消息。

消息队列中有一个列表,记录了所有正在等待获得消息的任务,如下图所示为多个任务在同一个消息队列中等待,当一则消息被发布到队列中时,最高优先级的等待任务将获得该消息,发布方式也可以向消息队列中所有等待的任务广播一则消息
在这里插入图片描述

消息队列相关函数

在开始函数探究之前,我们先来看一下相关的数据结构,用来定义消息队列的数据结构时OS_Q,我们进去看看它的内部结构如下所示

struct  os_q {                                              /* Message Queue                                          */
                                                            /* ------------------ GENERIC  MEMBERS ------------------ */
    OS_OBJ_TYPE          Type;                              /* Should be set to OS_OBJ_TYPE_Q                         */
    CPU_CHAR            *NamePtr;                           /* Pointer to Message Queue Name (NUL terminated ASCII)   */
    OS_PEND_LIST         PendList;                          /* List of tasks waiting on message queue                 */
#if OS_CFG_DBG_EN > 0u
    OS_Q                *DbgPrevPtr;
    OS_Q                *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
                                                            /* ------------------ SPECIFIC MEMBERS ------------------ */
    OS_MSG_Q             MsgQ;                              /* List of messages                                       */
};
  • Type:注释里也没说这是什么意思
  • *NamePtr:消息队列的名字
  • PendList:用来记录正在这个消息队列上等待的任务

下面的三个参数是和调试有关的,我们暂时不去关心,我们的重点放在最后一个参数:MsgQ,我们进入它的定义看看

struct  os_msg_q {                                          /* OS_MSG_Q                                               */
    OS_MSG              *InPtr;                             /* Pointer to next OS_MSG to be inserted  in   the queue  */
    OS_MSG              *OutPtr;                            /* Pointer to next OS_MSG to be extracted from the queue  */
    OS_MSG_QTY           NbrEntriesSize;                    /* Maximum allowable number of entries in the queue       */
    OS_MSG_QTY           NbrEntries;                        /* Current number of entries in the queue                 */
    OS_MSG_QTY           NbrEntriesMax;                     /* Peak number of entries in the queue                    */
};

这是才是真正的用来维护消息队列的数据结构,具体每个数据成员的含义,注释中都很清楚了,这个时候,有心的同学可能会发现,不是说维护消息吗,消息通过什么数据成员来维护呢,难道不应该有一个指针用来指向数据区域吗?这个我看了一下,是一个相当复杂的机制,等我先把UCOSIII的基本用法学完再来慢慢地啃这些深奥的地方。下面我们就来看看与消息队列相关的函数有哪些。

函数描述
OSQCreate()创建一个消息队列
OSQDel()删除一个消息队列
OSQFlush()清空一个消息队列
OSQPend()等待消息队列
OSQPendAbort()取消等待消息队列
OSQPost()向消息队列发送一条消息

其中常用的三个函数:OSQCreate(),OSQPost()和OSQPend()。

OSQCreate()

void  OSQCreate (OS_Q        *p_q,
                 CPU_CHAR    *p_name,
                 OS_MSG_QTY   max_qty,
                 OS_ERR      *p_err)
  • p_q:指向一个消息队列,消息队列的存储空间必须由应用程序分配
  • p_name:消息队列的名字
  • max_qty:消息队列的长度,必须大于0
  • p_err:保存调用此函数后返回的错误码

OSQPend()

void  *OSQPend (OS_Q         *p_q,
                OS_TICK       timeout,
                OS_OPT        opt,
                OS_MSG_SIZE  *p_msg_size,
                CPU_TS       *p_ts,
                OS_ERR       *p_err)
  • p_q:指向一个消息队列
  • timeout:等待消息的超时时间
  • opt:用来选择是否使用阻塞模式,具体选项含义见注释
  • p_msg_size:指向一个变量用来表示接受到的消息的长度
  • p_ts:指向一个时间戳,表明什么时候接收到消息
  • p_err:用来保存调用此函数后返回的错误码

OSQPost()

void  OSQPost (OS_Q         *p_q,
               void         *p_void,
               OS_MSG_SIZE   msg_size,
               OS_OPT        opt,
               OS_ERR       *p_err)
  • p_q:指向一个消息队列
  • p_void:指向实际发送的内容,p_void是一个指向void类型的指针
  • msg_size:设定消息的大小,单位为字节数
  • opt:用来选择消息发送操作的类型,具体有哪些类型,可以看源码注释
  • p_err:用来保存调用此函数后返回的错误码

消息队列实验

本次实验用到了4个任务、两个消息队列、一个定时器。先介绍四个任务的功能,开始任务start_task用来创建其它三个任务,

  • main_task任务:用来进行按键检测,并且将按键的值通过消息队列KEY_Msg发送给任务Keyprocess_task,初次之外还会根据任务的执行次数来控制LED0的闪烁
  • Keyprocess_task任务:向KEY_Msg消息队列请求消息,并根据请求到的消息来进行相应的处理
  • msgdis_task任务:向DATA_Msg消息队列请求消息,并且将请求到的消息显示在LCD上
  • tmr1_callback回调函数:向DATA_Msg消息队列发送消息,如果DATA_Msg满了就停止定时器1

下面我们进入具体代码解析

首先定义四个任务

/****************定义任务******************************/
//任务优先级
#define START_TASK_PRIO		3
//任务堆栈大小	
#define START_STK_SIZE 		128
//任务控制块
OS_TCB StartTaskTCB;
//任务堆栈	
CPU_STK START_TASK_STK[START_STK_SIZE];
//任务函数
void start_task(void *p_arg);

//任务优先级
#define MAIN_TASK_PRIO		4
//任务堆栈大小	
#define MAIN_STK_SIZE 		128
//任务控制块
OS_TCB Main_TaskTCB;
//任务堆栈	
CPU_STK MAIN_TASK_STK[MAIN_STK_SIZE];
void main_task(void *p_arg);

//任务优先级
#define KEYPROCESS_TASK_PRIO		5
//任务堆栈大小	
#define KEYPROCESS_STK_SIZE 		128
//任务控制块
OS_TCB Keyprocess_TaskTCB;
//任务堆栈	
CPU_STK KEYPROCESS_TASK_STK[MAIN_STK_SIZE];
void Keyprocess_task(void *p_arg);

//任务优先级
#define MSGDIS_TASK_PRIO	6
//任务堆栈
#define MSGDIS_STK_SIZE		128
//任务控制块
OS_TCB	Msgdis_TaskTCB;
//任务堆栈
CPU_STK	MSGDIS_TASK_STK[MSGDIS_STK_SIZE];
//任务函数
void msgdis_task(void *p_arg);

定义定时器

/**********************定时器*************************************/
u8 tmr1sta = 0;
OS_TMR tmr1;

定时器回调函数

/*******************定时器回调函数*******************************/
void tmr1_callback(void *p_tmr, void *p_arg);

下面是两个函数,一个用来加载LCD初始界面,一个用来查询DATA_Msg消息队列中的总队列数量和剩余队列数量

//加载主界面
void ucos_load_main_ui(void)
{
	POINT_COLOR = RED;
	LCD_ShowString(10,10,200,16,16,"ALIENTEK STM32F1");	
	LCD_ShowString(10,30,200,16,16,"UCOSIII Examp 11-1");
	LCD_ShowString(10,50,200,16,16,"Message Queue");
	LCD_ShowString(10,70,220,16,16,"KEY0:Refresh LCD");
	LCD_ShowString(10,90,200,16,16,"KEY_UP:LED1 KEY1:Tmr1");
	
	POINT_COLOR = BLACK;
	LCD_DrawLine(0,107,239,107);		//画线
	LCD_DrawLine(119,107,119,319);		//画线
	LCD_DrawRectangle(125,110,234,314);	//画矩形
	POINT_COLOR = RED;
	LCD_ShowString(0,130,100,16,16,"tmr1 state:");
	LCD_ShowString(0,170,120,16,16,"DATA_Msg Size:");
	LCD_ShowString(0,210,120,16,16,"DATA_Msg rema:");
	LCD_ShowString(0,250,100,16,16,"DATA_Msg:");
	POINT_COLOR = BLUE;
	LCD_ShowString(10,150,100,16,16,"TMR1 STOP! ");
}

//查询DATA_Msg消息队列中的总队列数量和剩余队列数量
void check_msg_queue(u8 *p)
{
	CPU_SR_ALLOC();
	u8 msgq_remain_size;	//消息队列剩余大小
	OS_CRITICAL_ENTER();	//进入临界段
	msgq_remain_size = DATA_Msg.MsgQ.NbrEntriesSize-DATA_Msg.MsgQ.NbrEntries;
	p = malloc(20);	//申请内存
	sprintf((char*)p,"Total Size:%d",DATA_Msg.MsgQ.NbrEntriesSize);	//显示DATA_Msg消息队列总的大小
	LCD_ShowString(10,190,100,16,16,p);
	sprintf((char*)p,"Remain Size:%d",msgq_remain_size);	//显示DATA_Msg剩余大小
	LCD_ShowString(10,230,100,16,16,p);
	free(p);		//释放内存
	OS_CRITICAL_EXIT();		//退出临界段
}

main函数和start_task函数就不写了,下面是main_task任务

//主任务的任务函数
void main_task(void *p_arg)
{
	u8 key,num;
	OS_ERR err;
	u8 *p;
	while(1)
	{
		key = KEY_Scan(0);  //扫描按键
		if(key)
		{
			//发送消息
			OSQPost((OS_Q*		)&KEY_Msg,		
					(void*		)&key,
					(OS_MSG_SIZE)1,
					(OS_OPT		)OS_OPT_POST_FIFO,
					(OS_ERR*	)&err);
		}
		num++;
		if(num%10==0) check_msg_queue(p);//检查DATA_Msg消息队列的容量
		if(num==50)
		{
			num=0;
			LED0 = ~LED0;
		}
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);   //延时10ms
	}
}

按键处理任务

//按键处理任务的任务函数
void Keyprocess_task(void *p_arg)
{	
	u8 num;
	u8 *key;
	OS_MSG_SIZE size;
	OS_ERR err;
	while(1)
	{
		//请求消息KEY_Msg
		key=OSQPend((OS_Q*			)&KEY_Msg,   
					(OS_TICK		)0,
                    (OS_OPT			)OS_OPT_PEND_BLOCKING,
                    (OS_MSG_SIZE*	)&size,		
                    (CPU_TS*		)0,
                    (OS_ERR*		)&err);
		switch(*key)
		{
			case WKUP_PRES:		//KEY_UP控制LED1
				LED1 = ~LED1;
				OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);
				break;
			case KEY0_PRES:		//KEY0刷新LCD背景
				num++;
				LCD_Fill(126,111,233,313,lcd_discolor[num%14]);
				break;
			case KEY1_PRES:		//KEY1控制定时器1
				tmr1sta = !tmr1sta;
				if(tmr1sta) 
				{
					OSTmrStart(&tmr1,&err);
					LCD_ShowString(10,150,100,16,16,"TMR1 START!");
				}
				else
				{
					OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err); //停止定时器1
					LCD_ShowString(10,150,100,16,16,"TMR1 STOP! ");
				}
				break;
		}
	}
}

接着是显示消息队列中的消息的函数

//显示消息队列中的消息
void msgdis_task(void *p_arg)
{
	u8 *p;
	OS_MSG_SIZE size;
	OS_ERR err; 
	while(1)
	{
		//请求消息
		p=OSQPend((OS_Q*		)&DATA_Msg,   
				  (OS_TICK		)0,
                  (OS_OPT		)OS_OPT_PEND_BLOCKING,
                  (OS_MSG_SIZE*	)&size,	
                  (CPU_TS*		)0,
                  (OS_ERR*		)&err);
		LCD_ShowString(5,270,100,16,16,p);
		free(p);	//释放内存
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时1s
	}
}

最后是定时器的回调函数

//定时器1的回调函数
void tmr1_callback(void *p_tmr,void *p_arg)
{
	u8 *pbuf;
	static u8 msg_num;
	OS_ERR err;
	pbuf = malloc(10);	//申请10个字节
	if(pbuf)	//申请内存成功
	{
		msg_num++;
		sprintf((char*)pbuf,"ALIENTEK %d",msg_num);
		//发送消息
		OSQPost((OS_Q*		)&DATA_Msg,		
				(void*		)pbuf,
				(OS_MSG_SIZE)10,
				(OS_OPT		)OS_OPT_POST_FIFO,
				(OS_ERR*	)&err);
		if(err != OS_ERR_NONE)
		{
			free(pbuf);	//释放内存
			OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err); //停止定时器1
			tmr1sta = !tmr1sta;
			LCD_ShowString(10,150,100,16,16,"TMR1 STOP! ");
		}
	}	
}

总结

重点在于理解消息队列这种任务间传递消息的思想,同时要注意比较不同任务间通信的方式(如信号量,管道,套接字,共享内存等)。当思想理解之后,API的使用是一件十分简单的事情。记住,一定要自己把代码写一遍

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UCOSIII中的消息传递 的相关文章

  • openwrt配置wifi桥接上级AP,再作为ap路由(可实现ip透传,例如远距离图像传输)

    第一步 上级ap配置为 接入点AP xff08 WDS xff09 xff0c 例如无人机的飞机端作为wds ap a xff0c 无线概况里点击修改 b xff0c ESSID改为你想要的名字 xff0c 要选择固定信道 xff08 非常
  • 菜鸟学Linux命令:ssh命令

    转载自品略图书馆 http www pinlue com article 2020 04 1003 1210139769049 html 1 查看SSH客户端版本 有的时候需要确认一下SSH客户端及其相应的版本号 使用ssh V命令可以得到
  • STM32串口发送数据

    串口通信经常作为开发调试的工具 xff0c 所以先介绍下串口通信 串口通讯 Serial Communication 是一种设备间非常常用的串行通讯方式 xff0c 因为它简单便捷 xff0c 大部分电子设备都支持该通讯方式 xff0c 电
  • npm ERR! code EINTEGRITY 解决方案

    报错信息 xff1a Error sha1 HsihLT8VutOkAReGpzpIZJY2twQ 61 integrity checksum failed when using sha1 wanted sha1 HsihLT8VutOkA
  • VScode搭建C/C++开发环境

    目录 1 VScode是什么 xff1f 2 VScode的下载和安装 2 1下载和安装 下载 xff1a 安装 xff1a 2 2环境的介绍 环境介绍 xff1a 安装中文版插件 xff1a 3 VScode配置C C 43 43 开发环
  • 从0开始跑通VINS FUSION(KITTI数据集)

    背景 xff1a VINS FUSION是香港科技大学在VINS MONO后做的推出的多功能版 xff0c 有双目的数据 xff0c 还有和GPS的融合 作为一个SLAM小白 xff0c 记录一下整个的跑通过程 代码连接 xff1a htt
  • ubuntu关于aptitude和apt-get

    起初GNU Linux系统中只有 tar gz 用户 必须自己编译他们想使用的每一个程序 在Debian出现之後 xff0c 人们认为有必要在系统 中添加一种机 制用来管理 安装在计算机上的软件包 人们将这套系统称为dpkg 至此着名的 p
  • C语言链表的简单编写

    代码分为3个部分 xff0c test c head h list c list c封装的函数 include 34 head h 34 创建一个空链表 Linklist list creat 申请一断空间 Linklist L L 61
  • java中a=a++;a=a+1;a+=1执行过程分析

    本文章内容前提是a数据类型为int 当a数据类型为int时 xff0c 执行a 61 a 43 43 后 xff0c a的数值不会变 xff1b 执行a 61 a 43 1后 xff0c 数值加1 xff1b 执行a 43 61 1后 xf
  • 【VPN(虚拟专用网)攻略大全】

    在 VPN 出现之前 xff0c 企业分支之间的数据传输只能依靠现有物理网络 xff08 例如 Internet xff09 由于 Internet 中存在多种不安全因素 xff0c 报文容易被网络中的黑客窃取或篡改 xff0c 最终造成数
  • Linux 如何检测硬盘坏道?

    在 Mac 和 Windows 下检测硬盘坏道有专门的工具 xff0c 或自带 或三方的都挺好用 xff0c 但是如何在 Linux 下检测硬盘坏道呢 xff1f 首先 xff0c 用 lsblk 命令查看下待检测硬盘的名字 xff1a 然
  • 图论-路径优化算法总结

    知乎主页 https www zhihu com people shuang shou cha dai 53 目录 1 xff1a Dijkstra算法 1 1 xff1a 算法思想 1 2 xff1a 算法步骤 1 3 xff1a 代码演
  • uORB发布订阅实例

    PX4SITL仿真 uORB实例 飞控串口读取外部传感器数据 xff1a 飞控开启一个进程读取外部传感器数据 xff0c 发布一个uORB主题 xff1b 另一个进程订阅前一个进程发布的主题 xff0c 订阅到的主题通过mavlink消息发
  • PX4仿真环境搭建

    PX4 SITL Simulation 前提准备 xff1a Ubuntu16 04 LTS 安装ROS kinetic 题外话 xff1a 如果连的是有IPV6的校园网 xff0c 在update时可能会访问IPV6地址出错 xff0c
  • PX4-Gazebo仿真学习笔记

    PX4 Gazebo仿真 xff1a http bbs amovauto com forum php mod 61 viewthread amp tid 61 486 amp extra 61 page 3D1 Simulator仿真器 x
  • C语言strtok函数

    1 strtok 语法 include lt string h gt char strtok char str const char delimiters 参数 xff1a str xff0c 待分割的字符串 xff08 c string
  • 终于把大数据类产品全流程解释清楚了

    你点开这文章 xff0c 说明你清晰知道了数据才是一切的基础 人工智能 机器学习 大数据等应用的基础都是基于这样的一个流程 xff0c 只是说运用领域不同 xff0c 那么偏重点不同 本文从数据采集到数据报告 xff0c 详细说明了大数据运
  • 关于slam

    什么是SLAM 机器人在未知环境中 xff0c 要实现智能化需要完成三个任务 xff0c 第一个是定位 Localization xff0c 第二个是建图 Mapping xff0c 第三个则是随后的路径规划 Navigation 之前地平
  • Linux(Ubuntu系统)同网段SSH连接不上,网络能ping通

    问题描述 测试以下命令同样连接不上 span class token function ssh span localhost 问题原因 Ubuntu系统自带 openssh client xff0c 但是没有自带 openssh serve
  • 本地进程间通信(二)--套接字socket

    目录 一 什么是Socket xff1f 二 socket通信流程 Server端 一 创建socket 二 命名socket 三 绑定 四 监听 五 关闭 Client端 一 创建socket 二 connect 三 发送数据 四 关闭s

随机推荐

  • debain服务器搭建之虚拟机安装(一)

    debain服务器虚拟机搭建系列 xff08 一 xff09 xff08 一 xff09 下载debain系统 xff08 二 xff09 搜索下载安装 vmware xff08 三 xff09 开始安装debain系统 xff08 四 x
  • 企业私有云技术设计方案

    1 概述 1 1 文档内容 本文档为某企业私有云技术路线设计文档 1 2 背景描述 1 2 1 某企业私有云业务线规划 近些年由于国内IDC市场发展迅速 xff0c 某企业从战略层面考虑 xff0c 建造了自己的高等级数据中心 xff0c
  • Qt的主窗口背景设置

    主界面设置背景一般有设置背景图片和背景颜色的需求 xff0c 其实二者之间设置方法类似 目录 主界面设置背景一般有设置背景图片和背景颜色的需求 xff0c 其实二者之间设置方法类似 方法一 xff1a 最简单的方式是通过ui界面来设置 xf
  • 7.使用码云

    使用GitHub时 xff0c 国内的用户经常遇到的问题是访问速度太慢 xff0c 有时候还会出现无法连接的情况 xff08 原因你懂的 xff09 如果我们希望体验Git飞一般的速度 xff0c 可以使用国内的Git托管服务 码云 xff
  • git diff命令之后,如何退出

    git diff命令是对比两次文件修改了什么 但如何退出呢 xff1f 输入q 按enter键盘
  • Float类型出现舍入误差的原因

    首先是float累加产生误差的原因 xff0c 该部分转自 xff1a http blog csdn net zhrh0096 article details 38589067 1 浮点数IEEE 754表示方法 要搞清楚float累加为什
  • React之antd Form回显数据

    转自 xff1a https blog csdn net welkin qing article details 110004969 文章目录 一 antd4如何回显数据 1 定义变量2 保存接口数据到form变量中3 form显示数据4
  • equals()方法和hashCode()方法

    1 hashCode 简介 该方法主要是利用一定的规则生成对象的哈希码 xff0c 也称散列码 它是是由对象导出的一个整数值 xff0c 是没有规律的 关于hashCode 使用的哈希算法 xff0c 越糟糕的哈希算法越容易产生哈希碰撞 产
  • 手把手教你基于STM32的BootLoader的OTA远程升级

    本文系21ic论坛蓝V作者小叶三千原创撰写 上次发过SD卡的Bootloader离线升级后 xff0c 应大家的要求 xff0c 这次就讲一下STM32的OTA远程升级 OTA又叫空中下载技术 xff0c 是通过移动通信的空中接口实现对移动
  • 转知乎,感觉非常棒,适合普通人c++学习路线图

    作者 xff1a 刘凯新 链接 xff1a https www zhihu com question 23447320 answer 39322848 来源 xff1a 知乎 著作权归作者所有 商业转载请联系作者获得授权 xff0c 非商业
  • InfluxDB和IotDB介绍与性能对比

    InfluxDB简介 InfluxDB 是用Go语言编写的一个开源分布式时序 事件和指标数据库 xff0c 无需外部依赖 用于存储和分析时间序列数据的开源数据库 适合存储设备性能 日志 物联网传感器等带时间戳的数据 其设计目标是实现分布式和
  • CVSNT SERVER Configuration

    title CVSNT SERVER Configuration 64 author H819 64 version 0 5 Copyright free reference note author name and the article
  • JavaScript高级使用(对象、BOM、封装)

    JavaScript高级 今天给大家介绍以下几个内容 xff1a JavaScript 面向对象JavaScript 内置对象JavaScript BOMJavaScript 封装 JavaScript面向对象 其实JavaScript中的
  • Python函数(函数定义、函数调用)用法详解

    Python 中 xff0c 函数的应用非常广泛 xff0c 前面章节中我们已经接触过多个函数 xff0c 比如 input print range len 函数等等 xff0c 这些都是 Python 的内置函数 xff0c 可以直接使用
  • zsh配置

    原本用WindTerm 43 bash xff0c WindTerm提供了高亮 自动记忆补全等功能 xff0c 基本上也够用 WindTerm还是比较早期阶段 xff0c 功能细节上还有待完善 xff0c 稳定性方面也有些小问题 比如用vi
  • Android CHRE (Context Hub Runtime Environment)简介

    当前的ARM处理的多个核心 xff0c Android系统运行在速度最快的大核上 xff0c 通常叫AP xff08 Application Processor xff09 AP主要为性能 体验优化 xff0c 相对来说能耗高 处理器中还有
  • Docker pull 命令

    Docker pull 命令 Docker 命令大全 docker pull 从镜像仓库中拉取或者更新指定镜像 语法 docker pull OPTIONS NAME TAG 64 DIGEST OPTIONS说明 xff1a a 拉取所有
  • strrchr函数的实现

    lt span style 61 34 font size 18px 34 gt include lt stdio h gt include lt string h gt char strrchr char const s1 int ch
  • UCOSIII概述

    又给自己挖了一个小坑 xff0c 今天开始学习UCOS xff0c 本篇文章只是作为学习笔记 xff0c 并不是什么教程 文章目录 序言源码概览配置文件UCOSIII与移植相关代码文件UCOS与CPU相关代码文件UCOSIII库文件UCOS
  • UCOSIII中的消息传递

    文章目录 序言什么是消息队列消息队列相关函数OSQCreate OSQPend OSQPost 消息队列实验总结 序言 前面我们介绍了信号量 xff0c 通过信号量我们能够解决优先级反转 xff0c 资源共享冲突等问题 xff0c 但是我们