堆和栈的区别之我见

一、预备知识—程序的内存分配
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器(Compiler)自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 — 常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区— 存放函数体的二进制代码。



二、例子程序
这是一个前辈写的,非常详细
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char
s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456/0在常量区,p3在栈上。

static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)
malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"; 123456/0放在常量区,
编译器可能会将它与p3所指向的"123456"优化成一个地方。
}


二、堆和栈的理论知识
2.1申请方式
stack:
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在栈中的。


2.2 申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

2.3申请大小的限制
栈:在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。


2.4申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。

2.5堆和栈中的存储内容
栈: 在函数调用时,(1) 第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,(2) 然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,(3) 然后是函数中的局部变量。 注意: 静态变量是不入栈的。
当本次函数调用结束后,(1) 局部变量先出栈,(2) 然后是参数,(3) 最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

2.6存取效率的比较
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-
14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov
byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。


2.7小结:
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、
付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

来源:http://www.21emb.com/Pages/WenZhangContent.asp?id=1 

<think>我们正在讨论FreeRTOS中的任务创建函数,特别是xTaskCreate,以及CMSIS-RTOS中的osThreadDef。用户想知道它们的用法及区别,并进行RTOS线程创建方法的比较。首先,回顾一下用户的问题:-osThreadDef用法-xTaskCreate用法-osThreadDef与xTaskCreate的区别-RTOS线程创建方法比较根据引用内容,我们已经有了关于xTaskCreate的详细信息。现在,我们需要补充osThreadDef的相关信息,并进行比较。###1.osThreadDef(CMSIS-RTOS)osThreadDef是CMSIS-RTOSAPI中用于定义线程的宏。CMSIS-RTOS是一个针对Cortex-M处理器的RTOS标准化接口,旨在提高不同RTOS之间的可移植性。**用法:**```cosThreadDef(name,function,priority,instances,stacksz);```-`name`:线程的名称(标识符)-`function`:线程函数(任务函数)-`priority`:线程的优先级(osPriority枚举值)-`instances`:允许创建的实例数(通常为1)-`stacksz`:大小(单位为字节)定义后,使用`osThreadCreate`来创建线程实例:```cosThreadIdosThreadCreate(constosThreadDef_t*thread_def,void*argument);```-`thread_def`:由osThreadDef定义的线程定义-`argument`:传递给线程函数的参数**示例:**```c#include"cmsis_os.h"voidthread_function(voidconst*arg){//线程逻辑while(1){osDelay(1000);}}//定义线程osThreadDef(thread1,thread_function,osPriorityNormal,0,128);//名称、函数、优先级、实例数、大小(字?注意单位)intmain(void){osKernelInitialize();//初始化内核osThreadIdtid=osThreadCreate(osThread(thread1),NULL);//创建线程osKernelStart();//启动内核while(1);}```注意:在CMSIS-RTOSv1中,大小的单位是字(word),而在CMSIS-RTOSv2中,单位是字节(byte)。具体取决于版本。###2.xTaskCreate(FreeRTOS)xTaskCreate是FreeRTOS中用于动态创建任务的函数。它会在FreeRTOS中动态分配任务任务控制块(TCB)。**函数原型:**```cBaseType_txTaskCreate(TaskFunction_tpvTaskCode,//任务函数constchar*constpcName,//任务名称(字符串)configSTACK_DEPTH_TYPEusStackDepth,//深度(以字为单位)void*constpvParameters,//传递给任务函数的参数UBaseType_tuxPriority,//任务优先级(数值,通常0为最低)TaskHandle_t*constpvCreatedTask//任务句柄指针,用于返回句柄);```**示例:**(见引用[2])###3.osThreadDef与xTaskCreate的区别|特性|osThreadDef(CMSIS-RTOS)|xTaskCreate(FreeRTOS)||------|--------------------------|------------------------||**所属标准**|CMSIS-RTOS标准API|FreeRTOS原生API||**功能**|定义线程属性,配合osThreadCreate使用|直接创建任务(动态分配)||**单位**|在v1中是字(word),v2中是字节(byte)|字(word)||**优先级表示**|使用标准枚举osPriority_t|使用数值(0最低,configMAX_PRIORITIES-1最高)||**内存分配**|由底层RTOS实现决定(可能是动态或静态)|动态分配(从FreeRTOS中分配TCB)||**静态创建支持**|有osThreadDefStatic(在CMSIS-RTOSv2中)|使用xTaskCreateStatic(静态分配)||**可移植性**|高(遵循CMSIS-RTOS标准,可更换底层RTOS)|低(仅适用于FreeRTOS)||**参数传递**|通过osThreadCreate的第二个参数传递|通过xTaskCreate的第四个参数传递||**任务句柄**|通过osThreadCreate返回osThreadId|通过最后一个参数返回TaskHandle_t|###4.RTOS线程创建方法比较除了上述两种方法,常见的还有静态创建方法(避免动态内存分配):-FreeRTOS:`xTaskCreateStatic`(见引用[3])-CMSIS-RTOSv2:`osThreadNew`(结合静态内存分配)**一般比较:**-**动态创建**:使用方便,但可能因内存碎片导致分配失败。适合资源充足或不确定任务数量的情况。-**静态创建**:需预先分配TCB内存,无动态分配开销,适合资源受限或确定性的系统。**跨RTOS比较:**-**CMSIS-RTOS**:提供统一接口,方便移植,但可能损失特定RTOS的优化特性。-**原生API**:如FreeRTOS的xTaskCreate,能充分利用FreeRTOS特性,但移植性差。###总结-`osThreadDef`是CMSIS-RTOS定义线程的宏,需要配合`osThreadCreate`使用,强调可移植性。-`xTaskCreate`是FreeRTOS的动态任务创建函数,直接创建任务,使用简单但特定于FreeRTOS。-选择依据:若注重可移植性,使用CMSIS-RTOS;若仅使用FreeRTOS且无需移植,直接使用xTaskCreate更直接。§§相关问题§§1.CMSIS-RTOSv1v2在线程创建方面有什么不同?2.在FreeRTOS中,如何实现静态内存分配的任务创建?3.任务优先级在不同RTOS中如何映射?4.使用CMSIS-RTOS接口有哪些优势劣势?</think>### osThreadDef 与 xTaskCreate 的用法及区别分析 #### 一、`xTaskCreate` 用法(FreeRTOS 原生 API) `xTaskCreate` 是 FreeRTOS 动态分配内存创建任务的函数: ```c BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 任务函数指针 const char * const pcName, // 任务名称(字符串) configSTACK_DEPTH_TYPE usStackDepth, // 大小(以字为单位) void * const pvParameters, // 任务参数指针 UBaseType_t uxPriority, // 优先级(0 ~ configMAX_PRIORITIES-1) TaskHandle_t * const pxCreatedTask // 返回任务句柄 ); ``` **示例**: ```c void vTask(void *pvParams) { while(1) { vTaskDelay(pdMS_TO_TICKS(1000)); } } TaskHandle_t xHandle; xTaskCreate(vTask, "MyTask", 256, NULL, 2, &xHandle); ``` **关键点**: 1. **动态内存分配**:从 FreeRTOS 中分配空间 TCB(任务控制块)[^1][^3] 2. **单位**:`usStackDepth` 以字(word)为单位(32位系统 1字=4字节) 3. **优先级**:数值越高优先级越高,需在 `configMAX_PRIORITIES` 范围内 4. **资源释放**:任务删除时会自动释放内存[^3] --- #### 二、`osThreadDef` 用法(CMSIS-RTOS 封装层) `osThreadDef` 是 CMSIS-RTOS 标准接口的线程定义宏,需配合 `osThreadCreate` 使用: ```c // 定义线程属性 osThreadDef(thread_name, thread_func, priority, instances, stack_size); // 创建线程 osThreadId osThreadCreate(const osThreadDef_t *thread_def, void *arg); ``` **示例**: ```c void thread_func(void const *arg) { while(1) { osDelay(1000); } } osThreadDef(worker, thread_func, osPriorityNormal, 0, 128); // 定义 osThreadId tid = osThreadCreate(osThread(worker), NULL); // 创建 ``` **关键点**: 1. **跨平台抽象层**:CMSIS-RTOS 是 ARM 的 RTOS 标准化接口[^2] 2. **静态定义**:`osThreadDef` 仅定义线程属性,不分配内存 3. **优先级枚举**:使用标准优先级如 `osPriorityNormal`(内部映射到底层 RTOS 优先级) 4. **单位**:`stack_size` 以字节为单位(CMSIS-RTOS v2)或字(v1) --- ### 三、核心区别对比 | **特性** | **xTaskCreate (FreeRTOS)** | **osThreadDef (CMSIS-RTOS)** | |------------------|-------------------------------------|------------------------------------| | **层级** | 原生 RTOS API | 标准化抽象层 | | **内存分配** | 动态分配 TCB | 依赖底层实现(可静态或动态) | | **优先级指定** | 数值(0~31) | 枚举值(osPriorityHigh 等) | | **单位** | 字(Word) | 字节(Byte) | | **跨平台性** | 仅限 FreeRTOS | 支持多种 RTOS(FreeRTOS/RTX 等) | | **资源管理** | 需手动删除任务 | 统一 osThreadTerminate 接口 | | **典型应用场景** | 直接操作 FreeRTOS | 需要 RTOS 移植性的项目 | --- ### 四、RTOS 线程创建方法比较 1. **动态创建**(如 `xTaskCreate`) - ✅ 优点:编码简单,无需预分配内存 - ❌ 缺点:可能内存碎片化,需确保空间充足 - 适用场景:任务数量动态变化的系统 2. **静态创建**(如 `xTaskCreateStatic`) ```c StackType_t xStack[1024]; // 静态空间 StaticTask_t xTCB; // 静态 TCB xTaskCreateStatic(..., xStack, &xTCB); ``` - ✅ 优点:无动态分配,确定性高 - ❌ 缺点:需预先分配内存资源[^3] - 适用场景:安全关键系统(如汽车电子) 3. **CMSIS-RTOS 封装** - ✅ 优点: - 统一接口简化跨平台移植 - 隐藏底层 RTOS 差异(如 FreeRTOS/RTX) - ❌ 缺点: - 轻微性能开销(多一层函数调用) - 无法使用特定 RTOS 的高级特性 --- ### 五、选择建议 - **直接控制需求** → 使用 `xTaskCreate/xTaskCreateStatic` - **跨平台移植需求** → 使用 `osThreadDef/osThreadCreate` - **资源受限系统** → 优先静态创建方法 - **快速原型开发** → CMSIS-RTOS 更高效 > 在实际工程中,FreeRTOS 原生 API 性能更高(节省约 5-10% 的 CPU 周期),但 CMSIS-RTOS 可减少移植成本[^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值