繼續(xù)按照程序流程前進蚂维,前面提到的時鐘初始化癌刽,堆初始化都還未跳出板級初始化rt_hw_board_init函數(shù)
然后看看rt_hw_board_init函數(shù)中剩下的代碼
/* Pin driver initialization is open by default */
#ifdef RT_USING_PIN
rt_hw_pin_init();
#endif
/* USART driver initialization is open by default */
#ifdef RT_USING_SERIAL
rt_hw_usart_init();
#endif
/* Set the shell console output device */
#ifdef RT_USING_CONSOLE
rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
/* Board underlying hardware initialization */
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
也就是分別對引腳、串口步绸、shell進行了初始化掺逼,最后rt_components_board_init是對執(zhí)行添加進自啟動的函數(shù),自啟動相關(guān)前篇文章描述瓤介,這里也就不談了吕喘。
這樣又回到了componts.c的rtthread_startup函數(shù)中,板級初始化之后需要執(zhí)行三個函數(shù)
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
其中rt_show_version是打印一些系統(tǒng)信息刑桑,rt_system_timer_init則是基于系統(tǒng)時鐘的定時器初始化氯质。最后就是操作系統(tǒng)的重頭線程調(diào)度初始化了rt_system_scheduler_init,但這里先不談?wù){(diào)度器祠斧,而是看看本文主題--線程闻察,調(diào)度器相關(guān)下篇再分析。
先從線程結(jié)構(gòu)體入手琢锋,便能了解到每個線程所包含的信息辕漂,該定義在文件rtdef.h中
/**
* Thread structure
*/
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 */
#ifdef RT_USING_MODULE
void *module_id; /**< id of application module */
#endif
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 */
rt_uint32_t stack_size; /**< stack size */
/* error code */
rt_err_t error; /**< error code */
rt_uint8_t stat; /**< thread status */
#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 */
#endif /*RT_USING_SMP*/
/* priority */
rt_uint8_t current_priority; /**< current priority 當(dāng)前優(yōu)先級*/
rt_uint8_t init_priority; /**< initialized priority 初始化優(yōu)先級*/
#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能占用的tick */
rt_ubase_t remaining_tick; /**< remaining tick 剩余可運行的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; /**< private user data beyond this thread */
};
typedef struct rt_thread *rt_thread_t;
如果看了內(nèi)核對象管理那篇,前幾個成員看著肯定熟悉吧吴超,其實就和內(nèi)核對象結(jié)構(gòu)體struct rt_object成員是一樣钉嘹,最后多了一個線程鏈表rt_list_t tlist。里面其他成員單獨拿出來是沒啥說的鲸阻,在具體使用時就清楚了跋涣。
線程相關(guān)函數(shù)分析
靜態(tài)創(chuàng)建線程
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)
{
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(stack_start != RT_NULL);
/* init thread object */
rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name);//將新創(chuàng)建的線程加入到管理器中
//實際的初始化函數(shù)
return _rt_thread_init(thread,
name,
entry,
parameter,
stack_start,
stack_size,
priority,
tick);
}
RTM_EXPORT(rt_thread_init);
英文單詞能直接表達其意思就不打備注了缨睡,其中RTM_EXPORT宏定義是將rt_thread_init函數(shù)公開給所以內(nèi)核代碼∑统保可以參考博文
接下里再看看實際的線程初始化函數(shù)_rt_thread_init宏蛉,其實線程初始化就是在給線程結(jié)構(gòu)體參數(shù)賦值,配合線程結(jié)構(gòu)體的注釋看就好了
static 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)
{
/* init thread list */
rt_list_init(&(thread->tlist));//清空鏈表
thread->entry = (void *)entry;
thread->parameter = parameter;
/* stack init */
thread->stack_addr = stack_start;
thread->stack_size = stack_size;
/* init thread stack 將被分配的內(nèi)存全部用#符號填充*/
rt_memset(thread->stack_addr, '#', thread->stack_size);
//給堆棧指針賦值
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(void *)((char *)thread->stack_addr),
(void *)rt_thread_exit);
#else
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(void *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)),
(void *)rt_thread_exit);
#endif
/* priority init */
RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
thread->init_priority = priority;
thread->current_priority = priority;
thread->number_mask = 0;
#if RT_THREAD_PRIORITY_MAX > 32
thread->number = 0;
thread->high_mask = 0;
#endif
/* tick init */
thread->init_tick = tick;//用于保存可用時間片大小
thread->remaining_tick = tick;//用于保存還市灾茫可用時間片大小
/* error and flags */
thread->error = RT_EOK;
thread->stat = RT_THREAD_INIT;
#ifdef RT_USING_SMP//多核
/* not bind on any cpu */
thread->bind_cpu = RT_CPUS_NR;
thread->oncpu = RT_CPU_DETACHED;
/* lock init */
thread->scheduler_lock_nest = 0;
thread->cpus_lock_nest = 0;
#endif /*RT_USING_SMP*/
/* initialize cleanup function and user data */
thread->cleanup = 0;//回調(diào)函數(shù)
thread->user_data = 0;
/* init thread timer */
rt_timer_init(&(thread->thread_timer),
thread->name,
rt_thread_timeout,
thread,
0,
RT_TIMER_FLAG_ONE_SHOT);
/* initialize signal */
#ifdef RT_USING_SIGNALS
thread->sig_mask = 0x00;
thread->sig_pending = 0x00;
thread->sig_ret = RT_NULL;
thread->sig_vectors = RT_NULL;
thread->si_list = RT_NULL;
#endif
#ifdef RT_USING_LWP
thread->lwp = RT_NULL;
#endif
RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));
return RT_EOK;
}
其中每個線程都建立了一個定時器拾并,超時函數(shù)均指向了rt_thread_timeout,他實際上就是將對于的線程踢出掛起鏈表鹏浅,加入到就緒鏈表嗅义,并且手動執(zhí)行調(diào)度器切換,在看調(diào)度器部分的時候應(yīng)該會配合則這個函數(shù)說明隐砸,這里就不多言了之碗。
移除進程函數(shù)
該函數(shù)與靜態(tài)創(chuàng)建線程配對,其實靜態(tài)創(chuàng)建于動態(tài)創(chuàng)建區(qū)域就在于是在運行之前就分配好了內(nèi)存季希,還是在運行中使用malloc來分配褪那,其他部分都是一樣的,這里就不再分析動態(tài)創(chuàng)建刪除等相關(guān)的函數(shù)了式塌。
rt_err_t rt_thread_detach(rt_thread_t thread)
{
rt_base_t lock;
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread));
if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) //線程被創(chuàng)建但未啟動則處于INIT狀態(tài)
{
/* remove from schedule */
rt_schedule_remove_thread(thread);
}
/* release thread timer */
rt_timer_detach(&(thread->thread_timer)); //刪掉線程匹配的定時器
/* change stat */
thread->stat = RT_THREAD_CLOSE;
/* detach object */
rt_object_detach((rt_object_t)thread); //將線程從管理器中移除
if (thread->cleanup != RT_NULL) //如果有綁定清除回調(diào)
{
/* disable interrupt */
lock = rt_hw_interrupt_disable();
/* insert to defunct thread list */
rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));
/* enable interrupt */
rt_hw_interrupt_enable(lock);
}
return RT_EOK;
}
RTM_EXPORT(rt_thread_detach);
啟動線程
rt_thread_resume函數(shù)是將掛起線程切換成就緒狀態(tài)并且放入線程鏈表中去博敬,在線程啟動函數(shù)rt_thread_startup中被調(diào)用,所以先拿出來說說峰尝。
其實線程狀態(tài)實際就四種偏窝,初始、就緒武学、掛起祭往、關(guān)閉。初始就是線程剛剛被創(chuàng)建時候的狀態(tài)火窒,就緒表示該線程已經(jīng)準(zhǔn)備好被執(zhí)行硼补,就等調(diào)度器亂轉(zhuǎn)到它了,掛起表示該線程在獲得某種條件前不被執(zhí)行熏矿。關(guān)閉和初始對應(yīng)括勺,就是線程被刪除。
rt_err_t rt_thread_resume(rt_thread_t thread)
{
register rt_base_t temp;
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: %s\n", thread->name));
//當(dāng)線程狀態(tài)不是SUSPEND時曲掰,錯誤退出
if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_SUSPEND)
{
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: thread disorder, %d\n",
thread->stat));
return -RT_ERROR;
}
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* remove from suspend list */
//這里是將該線程鏈表指針清空
rt_list_remove(&(thread->tlist));
rt_timer_stop(&thread->thread_timer);
/* enable interrupt */
rt_hw_interrupt_enable(temp);
/* insert to schedule ready list */
//這里是將狀態(tài)切換成READY,并且插入到線程鏈表中去
rt_schedule_insert_thread(thread);
RT_OBJECT_HOOK_CALL(rt_thread_resume_hook, (thread));
return RT_EOK;
}
RTM_EXPORT(rt_thread_resume);
rt_err_t rt_thread_startup(rt_thread_t thread)
{
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
/* set current priority to init priority */
thread->current_priority = thread->init_priority;
/* calculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
thread->number = thread->current_priority >> 3; /* 5bit */
thread->number_mask = 1L << thread->number;
thread->high_mask = 1L << (thread->current_priority & 0x07); /* 3bit */
#else
thread->number_mask = 1L << thread->current_priority;
#endif
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d\n",
thread->name, thread->init_priority));
/* change thread stat */
thread->stat = RT_THREAD_SUSPEND; //狀態(tài)由INIT 變成了SUSPEND
/* then resume it */
rt_thread_resume(thread);//切換成就緒態(tài)奈辰,加入線程鏈表
if (rt_thread_self() != RT_NULL)
{
/* do a scheduling */
rt_schedule();//該函數(shù)是手動切換一次調(diào)度器輪轉(zhuǎn)栏妖,選擇一個最高優(yōu)先級的線程執(zhí)行
}
return RT_EOK;
}
RTM_EXPORT(rt_thread_startup);
線程掛起
與函數(shù)rt_thread_resume相對于的是rt_thread_suspend,前者是將掛起轉(zhuǎn)為就緒奖恰,后者是將就緒轉(zhuǎn)為掛起吊趾。
rt_err_t rt_thread_suspend(rt_thread_t thread)
{
register rt_base_t stat;
register rt_base_t temp;
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: %s\n", thread->name));
stat = thread->stat & RT_THREAD_STAT_MASK;
if ((stat != RT_THREAD_READY) && (stat != RT_THREAD_RUNNING))
{
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, 0x%2x\n",
thread->stat));
return -RT_ERROR;
}
/* disable interrupt */
temp = rt_hw_interrupt_disable();
if (stat == RT_THREAD_RUNNING)
{
/* not suspend running status thread on other core */
//當(dāng)執(zhí)行掛起正在運行的程序則會報錯宛裕,正在運行其實意思就是自己,也就是自己不能掛起自己
RT_ASSERT(thread == rt_thread_self());
}
/* change thread stat */
//踢出鏈表论泛,狀態(tài)切換為suspend
rt_schedule_remove_thread(thread);
thread->stat = RT_THREAD_SUSPEND | (thread->stat & ~RT_THREAD_STAT_MASK);
/* stop thread timer anyway */
rt_timer_stop(&(thread->thread_timer));
/* enable interrupt */
rt_hw_interrupt_enable(temp);
RT_OBJECT_HOOK_CALL(rt_thread_suspend_hook, (thread));
return RT_EOK;
}
RTM_EXPORT(rt_thread_suspend);
rt_thread_sleep函數(shù)實際上是基于rt_thread_suspend和定時器做了一個功能性封裝揩尸,先將對于線程掛起,然后開啟定時器屁奏,由于定時器超時函數(shù)被設(shè)定成了將對于線程加入就緒鏈表中去岩榆,就達到了類似讓線程掛起特定時間的目的。
rt_err_t rt_thread_sleep(rt_tick_t tick)
{
register rt_base_t temp;
struct rt_thread *thread;
/* set to current thread */
thread = rt_thread_self();
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* suspend thread */
rt_thread_suspend(thread);
/* reset the timeout of thread timer and start it */
//使用每個線程配備的定時器來設(shè)定掛起時間坟瓢,超時將被切換回就緒狀態(tài)
rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick);
rt_timer_start(&(thread->thread_timer));
/* enable interrupt */
rt_hw_interrupt_enable(temp);
rt_schedule();
/* clear error number of this thread to RT_EOK */
if (thread->error == -RT_ETIMEOUT)
thread->error = RT_EOK;
return RT_EOK;
}
其他函數(shù)便沒什么說的了勇边,接下來便是調(diào)度器部分。這里補充說明一下關(guān)于線程棧初始化函數(shù)rt_hw_stack_init折联,它實際上是根據(jù)不同內(nèi)核進行的棧信息填充粒褒,我們分配給線程的棧在這里被切成了兩部分,第一部分是CPU寄存器诚镰,主要是用來保存上下文奕坟,第二部分才是線程可使用的棧空間清笨。