🌹1.Linux线程篇
🌹1.1Linux简介
Linux 内核最初只是由芬兰人林纳斯·托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的。
Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 UNIX 的多用户、多任务、支持多线程和多 CPU 的操作系统。
Linux 能运行主要的 UNIX 工具软件、应用程序和网络协议。它支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。
🌹1.2线程和进程的比较
- 线程是CPU调度和分配的基本单位,进程是系统执行和资源分配的基本单位
- 线程一般是访问所属进程的资源,如进程代码块,数据段系统资源等等
- 同一个进程内的线程共享一个地址空间,意思是全局变量的值都共享
- 在Linux下使用线程编译时要加上-lpthread
🌹1.3线程工作机制
🌹1.3.1创建线程
#include <pthread.h>
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,
void *(*start_routine)(void *),void *arg);
功能:创建一个线程。
参数:
thread: 线程标识符地址。
attr: 线程属性结构体地址。
start_routine: 线程函数的入口地址。
arg: 传给线程函数的参数。
返回值:
成功: 返回 0
失败: 返回非 0
🌹1.3.2线程等待
在Linux线程中,所有子线程结束后父线程才能结束,所以需要线程的等待,当子进程结束时,那么线程等待函数将会回收资源
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
功能:
等待子线程结束, 并回收子线程资源。
参数:
thread: 被等待的线程号。
retval: 用来存储线程退出状态的指针的地址。
返回值:
成功返回 0, 失败返回非 0。
🌹1.3.3线程的分离
通常来说线程创建后需要回收资源,这个函数可以让线程进行分离,自己做自己的事情不受影响,除非所属的父进程结束。
#include <pthread.h>
int pthread_detach(pthread_t thread);
功能:
使调用线程与当前进程分离, 使其成为一个独立的线程, 该线程终止时, 系统将自动回收它的资源。
参数:
thread: 线程号
返回值:
成功: 返回 0, 失败返回非 0。
🌹1.3.4线程的退出
相当于结束掉改线程
#include <pthread.h>
void pthread_exit(void *retval);
功能:
退出调用线程。
参数:
retval: 存储线程退出状态的指针。
注:
一个进程中的多个线程是共享该进程的数据段, 因此, 通常线程退出后所占用的资源并不会释放。
🌹1.3.5线程的取消
pthread_cancel函数原型如下:
线程被动退出,其他线程使用该函数让另一个线程退出
#include <pthread.h>
int pthread_cancel(pthread_t thread);
成功:返回0
该函数传入一个tid号,会强制退出该tid所指向的线程,若成功执行会返回0。
其实这个函数的原理时发送终止信号给某个线程,不过线程会有取消点和取消状态和取消的类型,我们大部分人平常都不会用到,所以不用浪费篇幅来阐述
🌹1.4线程池的概念
应用程序可以有多个线程,这些线程在休眠状态中需要耗费大量时间来等待事件发生。其他线程可能进入睡眠状态,并且仅定期被唤醒以轮循更改或更新状态信息,然后再次进入休眠状态。为了简化对这些线程的管理,.NET框架为每个进程提供了一个线程池,一个线程池有若干个等待操作状态,当一个等待操作完成时,线程池中的辅助线程会执行回调函数。线程池中的线程由系统管理,程序员不需要费力于线程管理,可以集中精力处理应用程序任务。
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中.如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙.如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值.超过最大值的线程可以排队,但他们要等到其他线程完成后才启动
🌹1.5实战篇:Linux线程代码演示
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <pthread.h>
void* callback(void * arg)
{
int i=(int)arg;
if(i==0)
{
int j=0;
for(j=0;j<3;j++){
printf("1111111111111\n");
sleep(1);
}
pthread_exit((void *)1);
}
else if(i==1)
{
while(1){
printf("222222222222222\n");
sleep(1);
}
}
else if(i==2)
{
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
while(1){
sleep(1);
printf("33333333333333333\n");
}
}
else if(i==3)
{
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
while(1){
printf("4444444444444444\n");
sleep(1);
}
}
else;
}
int main(int argc,char *argv[]){
//多线程的创建
int i=0;
pthread_t pth[4];
for(i=0;i<4;i++){
pthread_create(&pth[i],NULL,callback,(void *)i);
}
int num=0;
for(i=0;i<4;i++){
pthread_join(pth[i],(void **)&num);
printf("%d 线程结束 %d\n",i+1,num);
sleep(5);
pthread_cancel(pth[i+1]);
}
return 0;
}
🌹2.RTOS线程篇
🌹2.1RT-Thread简介
RT-Thread 是一个集实时操作系统(RTOS)内核、中间件组件和开发者社区于一体的技术平台,由熊谱翔先生带领并集合开源社区力量开发而成,RT-Thread 也是一个组件完整丰富、高度可伸缩、简易开发、超低功耗、高安全性的物联网操作系统。RT-Thread 具备一个 IoT OS 平台所需的所有关键组件,例如GUI、网络协议栈、安全传输、低功耗组件等等。经过11年的累积发展,RT-Thread 已经拥有一个国内最大的嵌入式开源社区,同时被广泛应用于能源、车载、医疗、消费电子等多个行业,累积装机量超过 8亿 台,成为国人自主开发、国内最成熟稳定和装机量最大的开源 RTOS。
🌹2.2线程管理的特点
RT-Thread 线程管理的主要功能是对线程进行管理和调度,系统中总共存在两类线程,分别是系统线程和用户线程。RT-Thread 的线程调度器是抢占式的,主要的工作就是从就绪线程列表中查找最高优先级线程,保证最高优先级的线程能够被运行,最高优先级的任务一旦就绪,总能得到 CPU 的使用权。调度器调度线程切换会把线程上下文保存进栈里面,当这个线程被调用时,再将它恢复
🌹2.3线程工作机制
🌹2.3.1线程控制块 struct rt_thread
和Linux的线程相似,线程也有一个结构体来存放线程的一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构,线程等待事件集合等。
struct rt_thread
{
/* rt object */
char name[RT_NAME_MAX]; /**< the name of thread */
rt_uint8_t type; /**< type of object */
rt_uint8_t flags; /**< thread's flags */
rt_list_t list; /**< the object list */
rt_list_t tlist; /**< the thread list */
/* stack point and entry */
void *sp; /**< stack point 栈指针*/
void *entry; /**< entry */
void *parameter; /**< parameter */
void *stack_addr; /**< stack address point 栈地址指针*/
rt_uint32_t stack_size; /**< stack size */
/* error code */
rt_err_t error; /**< error code */
rt_uint8_t stat; /**< thread status 线程状态*/
/* priority */
rt_uint8_t current_priority; /**< current priority */
rt_uint8_t init_priority; /**< initialized priority */
rt_uint32_t number_mask;
...
rt_ubase_t init_tick; /**< thread's initialized tick */
rt_ubase_t remaining_tick; /**< remaining tick */
struct rt_timer thread_timer; /**< built-in thread timer */
void (*cleanup)(struct rt_thread *tid); /**< cleanup function when thread exit */
rt_uint32_t user_data; /**< private user data beyond this thread */
};
🌹2.3.2线程运行状态
- 初始态:线程刚创建还没开始运行的时候,此时线程不参与调度
- 就绪态:此时按照线程的优先级进行排队。等待被执行,相当于差个CPU
- 运行态:线程运行中
- 挂起态:一直在阻塞,等待被其他期间唤醒,此时不参与线程调度,只有运行的时候才参与线程调度
- 关闭态:线程结束的时候会关处于关闭状态
🌹2.3.3线程优先级
RT-Thread 最大支持 256 个线程优先级 (0~255),数值越小的优先级越高,0 为最高优先级。在一些资源比较紧张的系统中,可以根据实际情况选择只支持 8 个或 32 个优先级的系统配置;对于 ARM Cortex-M系列,普遍采用 32 个优先级。最低优先级默认分配给空闲线程使用,用户一般不使用。在系统中,当有比当前线程优先级更高的线程就绪时,当前线程将立刻被换出,高优先级线程抢占处理器运行,其实就是STM32的NVIC机制
🌹 2.3.4时间片
程序在执行过程中,首先会进入主线程(main),运行main方法,当时间到达时间片的时间后,cup会退出主线程,然后随机选择一个线程(主线程、线程t或是线程myThread),进入到cup中执行线程,cup中执行过1ms(一个时间片)后,推出cup当前线程,重复之前的动作,直到线程执行结束。这个进入cup的时间段就叫做时间片。
🌹2.3.5注意事项
实时系统不像Linux系统,如果一个线程进入死循环,那么比它优先级低的线程都不能够执行,所以不用的时候要马上挂起或者调用延时函数或者结束掉线程
🌹2.4相关线程操作
一个线程的一生可以理解为:创建->启动->运行->删除/脱离
在RT-Thread’中创建线程可以分为动态线程和静态线程:动态线程就是从堆分配空间,静态线程是从栈上分配空间,因为实时操作系统的内存很紧缺,所以我们要按需分配.
🌹2.4.1创建线程
🌹 2.4.1.1动态线程创建
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
参数:分别对应名字–回调函数—传入回调函数的参数-----栈大小----线程优先级—节拍数
🌹2.4.1.2静态线程创建
/**
* This function will initialize a thread, normally it's used to initialize a
* static thread object.
*
* @param thread the static thread object
* @param name the name of thread, which shall be unique
* @param entry the entry function of thread
* @param parameter the parameter of thread enter function
* @param stack_start the start address of thread stack
* @param stack_size the size of thread stack
* @param priority the priority of thread
* @param tick the time slice if there are same priority thread
*
* @return the operation status, RT_EOK on OK, RT_ERROR on error**/
rt_err_t rt_thread_init(struct rt_thread *thread,
const char *name,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
🌹2.4.2线程销毁
/**
* This function will delete a thread. The thread object will be removed from
* thread queue and deleted from system object management in the idle thread.
*
* @param thread the thread to be deleted
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_delete(rt_thread_t thread)
🌹2.4.3线程分离
/**
* This function will detach a thread. The thread object will be removed from
* thread queue and detached/deleted from system object management.
*
* @param thread the thread to be deleted
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_detach(rt_thread_t thread)
🌹2.4.4线程启动
**
* This function will start a thread and put it to system ready queue
*
* @param thread the thread to be started
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_startup(rt_thread_t thread)
这个线程会把线程状态改为就绪态,放入线程队列中等待cpu的运行。
🌹2.4.5资源让出
当我们需要某个线程占用CPU的资源让出时,使用该函数,意思时进入了就绪态
/**
* This function will let current thread yield processor, and scheduler will
* choose a highest(բ/ָĊߓŏȼ) thread to run. After yield processor, the current thread
* is still in READY state.
*
* @return RT_EOK
*/
rt_err_t rt_thread_yield(void)
🌹2.4.6线程休眠
/**
* This function will let current thread sleep for some ticks.
*
* @param tick the sleep ticks
*
* @return RT_EOK
*/
rt_err_t rt_thread_sleep(rt_tick_t tick)
rt_err_t rt_thread_delay(rt_tick_t tick)
/**
* This function will let current thread delay for some milliseconds.
*
* @param tick the delay time
*
* @return RT_EOK
*/
rt_err_t rt_thread_mdelay(rt_int32_t ms)
🌹2.4.7线程控制函数
这个函数类似与Linux下的fcntl,改变线程的状态,比如说可以调用这个函数进行线程开始,结束等待
/**
* This function will control thread behaviors according to control command.
*
* @param thread the specified thread to be controlled
* @param cmd the control command, which includes
* RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread;
* RT_THREAD_CTRL_STARTUP for starting a thread; == rt_thread_startup()
* RT_THREAD_CTRL_CLOSE for delete a thread; == rt_thread_delete()
* RT_THREAD_CTRL_BIND_CPU for bind the thread to a CPU.
* @param arg the argument of control command
*
* @return RT_EOK
*/
rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg)
🌹 2.4.8创建和销毁hook
我们知道,在线程执行过程中,线程之间会来回切换,有时候我们需要知道哪个线程切换到了哪个线程,这是我们可以通过一个调度器来进行追踪,通过这个然后这些信息都会记录到一个结构体上,可以人为的打印在串口上
/**
* @ingroup Hook
* This function sets a hook function to idle thread loop. When the system performs
* idle loop, this hook function should be invoked.
*
* @param hook the specified hook function
*
* @return RT_EOK: set OK
* -RT_EFULL: hook list is full
*
* @note the hook function must be simple and never be blocked or suspend.
*/
rt_err_t rt_thread_idle_sethook(void (*hook)(void))
/**
* delete the idle hook on hook list
*
* @param hook the specified hook function
*
* @return RT_EOK: delete OK
* -RT_ENOSYS: hook was not found
*/
rt_err_t rt_thread_idle_delhook(void (*hook)(void))
🌹2.4.9设置调度器hook
**
* This function will set a hook function, which will be invoked when thread
* switch happens.
*
* @param hook the hook function
*/
void rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to))
🌹2.5实战篇:RTOS线程代码演示
效果:两个线程循环打印消息
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-11-13 15118 the first version
*/
#include "thread.h"
rt_thread_t tid=NULL;
rt_uint8_t th2_stack[512] = {0};
struct rt_thread th2 ;
static void th1_entry(void *parameter)
{
int i = 0;
for(i = 0; i < 5 ; i++){
rt_kprintf("th1 running...\n");
rt_thread_mdelay(1000);
}
}
static void th2_entry(void *parameter)
{
int i = 0;
for(i = 0; i < 5 ; i++)
{
rt_kprintf("th2 running...\n");
rt_thread_mdelay(1000);
}
}
void scheduler_hook(struct rt_thread *from, struct rt_thread *to)
{
rt_kprintf("from:%s ---> to:%s\n",from->name,to->name);
}
int main()
{
int ret ;
//线程调度器钩子函数
rt_scheduler_sethook(scheduler_hook);
//动态创建
tid = rt_thread_create("main", th1_entry, RT_NULL,
RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
if(tid==RT_NULL)
{
LOG_E("error\n");
}
LOG_D("SUCCESSED\n");
rt_thread_startup(tid);//启动线程
// /**静态创建线程操作**/
ret = rt_thread_init(&th2, "th2_demo", th2_entry, NULL, th2_stack, \
sizeof(th2_stack), 19, 5);//静态创建
if(ret<0)
{
LOG_E("rt_init error\n");
return ret;
}
LOG_D("rt_thread_init successed ...\n");
rt_thread_startup(&th2);
return 1;
}
🌹3.总结(Linux和RTOS线程区别)
- RTOS线程自动回收,父进程不用进行等待
- Linux线程使用的方法更多
- RTOS的线程里不能有死循环,否则其他低优先级的线程不能运行
- RTOS线程具有优先级的概念