线程是操作系统的一个重要概念,正是因为有了线程,操作系统才可以实现多任务。在了解线程之前,先来看一下在没有操作系统之前实现多任务是怎样的。
所谓的多任务就是让MCU能够在宏观上同时运行多个函数,在没有使用操作系统的时候,实现多任务通常是利用定时器判断标志位,然后在主函数中根据标志位来选择不同的函数执行。但是这种方法的实时性很差,并且不能自由地控制函数的执行,停止。
int flag = 0;
void main(void)
{
while(1)
{
switch(flag)
{
case 1:
task1();
break;
case 2:
task2();
break;
case 3:
task3();
break;
}
}
}
void timer_handler(void)
{
static int count = 0;
count++;
if(count <= 50)
{
flag = 1;
}
else if(count > 50 && count <= 100)
{
flag = 2;
}
else if(count > 100 && count <= 150)
{
flag = 3;
}
else
{
count = 0;
}
}
在rt-thread中利用线程来实现多任务功能,线程是rt-thread操作系统中的一个很重要的概念,在rt-thread中线程有有两种,分别是系统线程和用户线程,系统线程由rt-thread操作系统创建,主要用于管理内核的一些资源。而用户线程由用户自己创建。
struct rt_thread
{
/* rt object */
char name[RT_NAME_MAX]; //线程名字
rt_uint8_t type; //对象类型
rt_uint8_t flags; //标志位
#ifdef RT_USING_MODULE
void *module_id; /**< id of application module */
#endif
rt_list_t list; //对象链表
rt_list_t tlist; //线程链表
/* stack point and entry */
void *sp; //栈指针
void *entry; //入口函数
void *parameter; //线程参数
void *stack_addr; //栈起始地址
rt_uint32_t stack_size; //栈大小
/* error code */
rt_err_t error; //错误码
rt_uint8_t stat; //线程状态
#ifdef RT_USING_SMP
rt_uint8_t bind_cpu; /**< thread is bind to cpu */
rt_uint8_t oncpu; /**< process on cpu` */
rt_uint16_t scheduler_lock_nest; /**< scheduler lock count */
rt_uint16_t cpus_lock_nest; /**< cpus lock count */
rt_uint16_t critical_lock_nest; /**< critical lock count */
#endif /*RT_USING_SMP*/
/* priority */
rt_uint8_t current_priority; //线程优先级
rt_uint8_t init_priority; /**< initialized priority */
#if RT_THREAD_PRIORITY_MAX > 32
rt_uint8_t number;
rt_uint8_t high_mask;
#endif
rt_uint32_t number_mask;
#if defined(RT_USING_EVENT)
/* thread event */
rt_uint32_t event_set;
rt_uint8_t event_info;
#endif
#if defined(RT_USING_SIGNALS)
rt_sigset_t sig_pending; /**< the pending signals */
rt_sigset_t sig_mask; /**< the mask bits of signal */
#ifndef RT_USING_SMP
void *sig_ret; /**< the return stack pointer from signal */
#endif
rt_sighandler_t *sig_vectors; /**< vectors of signal handler */
void *si_list; /**< the signal infor list */
#endif
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 */
/* light weight process if present */
#ifdef RT_USING_LWP
void *lwp;
#endif
rt_uint32_t user_data; //用户私有数据
};
在实时操作系统中,每个线程都有自己的时间片,所谓时间片就是线程一次可以运行多长事件。时间片仅对优先级相同的线程有用,当线程优先级相同时,会采用时间片轮转调度的方式进行线程的调度。每个线程都安装规定的时间片运行,当时间片到的时候,线程就会让出CPU,给下一个进程运行,如此循环所有优先级相同的进程。
void thread_entry(void* paramenter)
{
while (1)
{
/* 等 待 事 件 的 发 生 */
/* 对 事 件 进 行 服 务、 进 行 处 理 */
}
}
通过会在线程函数中循环做一些事情,但是这里需要注意一点是,不要让线程函数陷入死循环。在优先级相同的线程中,可以让线程陷入死循环,因为优先级相同会采用时间片调度的方法依次运行。但是当高优先级线程陷入死循环时,会导致低优先级的线程无法执行。所以在线程中要有让出CPU的行为。
#define RT_EOK 0 /**< There is no error */
#define RT_ERROR 1 /**< A generic error happens */
#define RT_ETIMEOUT 2 /**< Timed out */
#define RT_EFULL 3 /**< The resource is full */
#define RT_EEMPTY 4 /**< The resource is empty */
#define RT_ENOMEM 5 /**< No memory */
#define RT_ENOSYS 6 /**< No system */
#define RT_EBUSY 7 /**< Busy */
#define RT_EIO 8 /**< IO error */
#define RT_EINTR 9 /**< Interrupted system call */
#define RT_EINVAL 10 /**< Invalid argument */