转载自:https://mp.weixin.qq.com/s/9wleh0fUjXd5jLh1b81ncQ
1 张图看 3 种不同的 posix 线程
原创 honeysugar MultiMCU EDU 今天
本文基于《Azure RTOS ThreadX 的 Posix 接口》,从“线程栈”在哪里的角度说一说 3 种不同的 pthread 建立方法。
1 张图看 3 种不同的 posix 线程
图1. posix 线程
1、图中蓝色字标示的线程,线程栈在 posix 内存池里;
2、图中绿色字标示的线程,线程栈是一个全局数组(编译时确定好地址);
3、图中紫色字标示的线程,线程栈是动态分配的且不在 posix 内存池里。
这 3 种线程栈的位置是怎么回事?下面给出代码。
Posix 接口建立线程代码
对 ThreadX 上使用 posix 接口还不熟悉的请看《Azure RTOS ThreadX 的 Posix 接口》。
#define PTHREAD0_STACK_SIZE 4096
#define PTHREAD1_STACK_SIZE 8912
uint64_t pthread_1_stack[PTHREAD1_STACK_SIZE/8];
#define PTHREAD2_STACK_SIZE 2048
pthread_t pthread_0;
pthread_attr_t ptattr0;
VOID *pthread_0_entry(VOID *);
pthread_t pthread_1;
pthread_attr_t ptattr1;
VOID *pthread_1_entry(VOID *);
pthread_t pthread_2;
pthread_attr_t ptattr2;
VOID *pthread_2_entry(VOID *);
VOID *pthread_0_entry(VOID *pthread0_input)
{
struct timespec thread_0_sleep_time={0,0};
while(1)
{
thread_0_sleep_time.tv_nsec = 500*1e6;
thread_0_sleep_time.tv_sec = 0;
nanosleep(&thread_0_sleep_time,0);
}
}
VOID *pthread_1_entry(VOID *pthread1_input)
{
struct timespec thread_1_sleep_time={0,0};
while(1)
{
thread_1_sleep_time.tv_nsec = 500*1e6;
thread_1_sleep_time.tv_sec = 0;
nanosleep(&thread_1_sleep_time,0);
}
}
VOID *pthread_2_entry(VOID *pthread1_input)
{
struct timespec thread_2_sleep_time={0,0};
while(1)
{
thread_2_sleep_time.tv_nsec = 500*1e6;
thread_2_sleep_time.tv_sec = 0;
nanosleep(&thread_2_sleep_time,0);
}
}
ULONG free_memory[128*1024 / sizeof(ULONG)];
void application_task_create (void)
{
VOID* storage_ptr;
struct sched_param param;
/* Init POSIX Wrapper
* 经过这一步 storage_ptr 指针指向的地址
* 已经被推到 posix 内存池之外了
*/
UINT my_old_priority = THREADX_TASK_START_PRIORITY;
tx_thread_preemption_change(&threadx_task_start, 0, &my_old_priority);
storage_ptr = (VOID*) posix_initialize(free_memory);
tx_thread_preemption_change(&threadx_task_start, my_old_priority, &my_old_priority);
/* Create pthread attributes for pthread 0 to pthread 2 */
pthread_attr_init(&ptattr0);
pthread_attr_init(&ptattr1);
pthread_attr_init(&ptattr2);
TX_INTERRUPT_SAVE_AREA
ptattr0.stack_size = PTHREAD0_STACK_SIZE;
ptattr1.stack_size = PTHREAD1_STACK_SIZE;
ptattr2.stack_size = PTHREAD2_STACK_SIZE;
TX_DISABLE
TX_RESTORE
/* Create a sched_param structure */
memset(¶m, 0, sizeof(param));
/* Create pthread 0.
* 这个线程栈在 posix 内存池里
*/
param.sched_priority = 6;
pthread_attr_setschedparam(&ptattr0, ¶m);
//pthread_attr_setstackaddr(&ptattr0, storage_ptr );
//storage_ptr = (int *) storage_ptr + PTHREAD0_STACK_SIZE;
pthread_create (&pthread_0, &ptattr0,pthread_0_entry,NULL);
/* Create pthread 1.
* 这个线程栈在编译时确定好的数组里
*/
param.sched_priority = 7;
pthread_attr_setschedparam(&ptattr1, ¶m);
pthread_attr_setstackaddr(&ptattr1, &pthread_1_stack[0] );
//storage_ptr = (int *) storage_ptr + PTHREAD1_STACK_SIZE;
pthread_create (&pthread_1, &ptattr1,pthread_1_entry,NULL);
/* Create pthread 2.
* 这个线程栈不在内存池里,
* 而是在内存池之外的,并且是动态分配的栈空间
* 也就是说这样做无法在编译时确定栈地址在哪
*/
param.sched_priority = 8;
pthread_attr_setschedparam(&ptattr2, ¶m);
pthread_attr_setstackaddr(&ptattr2, storage_ptr );
storage_ptr = (int *) storage_ptr + PTHREAD2_STACK_SIZE;
pthread_create (&pthread_2, &ptattr2,pthread_2_entry,NULL);
}
3 种写法该怎么选
首先,要清楚一个非常重要的基本观点:安全关键的项目基本是不考虑动态内存分配的。
最优写法
Sugar 觉得不论项目是否是“安全关键”的,最好是按“安全关键”的习惯来写。从内存分配的角度来讲,如果要实现安全关键,最好是程序员知道线程栈的地址。所以 Sugar 认为最优写法是把线程栈放到代码里写的全局数组当中。
图2. 程序员知道的线程栈
次优写法
如果不想通过全局数组指定线程栈(比如程序员水平仍需提高)时,则可以把线程栈放到操作系统的内存池里,操作系统都会有自己的内存管理,由其像 ThreadX 这种过了最高等级安全认证的,其内存管理是能经得住考验的。
凑合用写法
如果程序员不知道线程栈在哪里,同时线程栈也不出自于操作系统的内存池(不在内存管理泛围),也就是说完全由动态分配来确定线程栈地址,万一程序需要 Debug 则会给开发者带来不必要的麻烦。