本帖最后由 永不停息 于 2018-12-25 09:34 编辑
' s. K7 \9 \1 \/ m. b0 {) v1 N+ ]1 o0 p
【RT-Thread内核实现与应用开发实战指南】本书总共分为两个部分,第一部分主要剖析 RT-Thread 内核的实现机理,通过代码从操作系统最基本的线程创建和线程切换开始,一步一步增加代码功能(线程阻塞、时间片、优先级、临界区等)/ Q! N1 c4 e; y, U- d% T
由浅入深从开发者的角度教读者怎么从零实现基本的操作系统内核。虽然内容不是很细致,但是能够帮助读者抓住重点,
4 u" T+ [# X- I让读者能够最快速的了解操作系统的核心,读完这部分然后再去阅读源码会比直接阅读源码事半功倍。第二部分内容着重( h8 n+ A. D# t; P( I& s1 s
RT-Thread 的应用,分别介绍操作系统各种机制(信号量、互斥量、时间、邮箱等)的工作机理以及相关的接口函数,读者# \$ Z1 H8 L* ?# v$ O( i: _
可以通过这部分去熟悉 RT-Thread 的应用。
4 Z! N- y: I1 o* m& k4 i6 r, K/ E2 J( T' F( k
由于本人之前对一些操作系统(FreeRTOS)也有所学习和应用,但是没有深入去学习源码,对于操作系统内核的实现机制
; o0 ~9 ~# |9 B a并不是很了解,因此这次也将学习重点放在第一部分,下面针对第一部分的线程创建和切换这部分进行总结。& X+ U4 q; i( p; D- i& l
/ A8 F" @: x9 v6 j. b! _% w6 H6 _对于操作系统而言,线程是最基本的概念,什么是线程呢?线程是程序执行的最小单元,每个线程都拥有独立的栈空间
6 f8 j2 D6 s+ W* M! \1 N+ w,是系统调度的基本单元。操作系统最核心的地方也在于线程的调度,系统为了方便线程调度,为每个线程都额外定义了3 {: s$ B% P5 I! s" M# ]" w
线程控制块,线程控制块的定义如下:
. d9 U/ e( ^! L- /**
" X8 @5 ]) E O, U; N - * Thread structure! T. r1 h, L3 [* A5 T: s
- */; x0 b- a* G+ b- ~
- struct rt_thread1 e) O* X6 Q/ V) X: l
- {9 K* W; K/ K# g4 }+ H
- /* rt object */5 N1 B" S9 Y- I1 S$ |5 T% s
- char name[RT_NAME_MAX]; /**< the name of thread */& B9 |+ a# M' v, q. @
- rt_uint8_t type; /**< type of object */) l4 D, ^6 {% r
- rt_uint8_t flags; /**< thread's flags */
+ e( U, F- t4 a: Q$ m
" g" v/ z8 m3 a- rt_list_t list; /**< the object list */' i6 O9 s+ D( D7 R0 u: G7 S
- rt_list_t tlist; /**< the thread list */% A' t/ M1 i: _* x9 P2 f" j4 N
- 0 `0 D4 Y0 K6 ~) F: M' z, ]8 ^
- /* stack point and entry */
. m9 _, k" j% s1 h0 b' H - void *sp; /**< stack point */$ G& p: B" U( K* Q$ Y9 U; a
- void *entry; /**< entry */
' Y- J* K+ s$ Z2 M; o0 R - void *parameter; /**< parameter */
9 s8 O: [4 a4 c* k# T2 | - void *stack_addr; /**< stack address */6 O9 a; E `1 k' Y
- rt_uint32_t stack_size; /**< stack size */: e: t# O8 a4 G6 ^6 K
$ ~5 U* Y! C `; x5 i7 V7 @9 o- /* error code */
! o0 n- o4 k: n( }5 g3 ~ - rt_err_t error; /**< error code */) B1 c& C. J$ }, t, ~& i4 x
- 3 X$ A! {, s7 T# O! J# @1 o
- rt_uint8_t stat; /**< thread status */
8 V$ }! `+ X" X2 v4 U - ! n# @( ^5 A# Q3 @1 T/ b
- /* priority */
5 ?& m* p( s6 \- g7 H' @3 t - rt_uint8_t current_priority; /**< current priority */
. n! d8 r4 g; D/ \9 X - rt_uint8_t init_priority; /**< initialized priority */
复制代码 其中 tlist 成员是线程的链表节点,后面要把线程插入到各种双向链表中便是通过该链表节点实现,该链表节点定义如下:1 ~( E) O/ o7 A9 p |
- struct rt_list_node
, g5 U# m+ X, i$ |& y- D& e- h0 d- |- u - {. ~7 s% T" L1 ]$ P2 ]
- struct rt_list_node *next; /**< point to next node. */% f! e% v4 ]+ N! W0 Z
- struct rt_list_node *prev; /**< point to prev node. */
5 i8 b& U1 V! T, F0 u - };
! p5 \. }/ b2 F" d* l5 ]% \ - typedef struct rt_list_node rt_list_t; /**< Type for lists. */
复制代码 线程初始化通过函数 rt_thread_init(struct rt_thread *thread) 实现,主要进行线程控制块相关成员的初始化以及初始化线程栈
& \2 m3 D/ g6 a4 T具体实现如下所示:
5 N Y- T( ?5 ~- static rt_err_t _rt_thread_init(struct rt_thread *thread,& ^" x. ~8 `7 a; `
- const char *name,% n1 q" D* F+ M; g$ P* q6 C( M
- void (*entry)(void *parameter),3 L ]! k7 K4 R- s
- void *parameter,
h# L: P3 x1 L8 i4 l - void *stack_start,8 r6 m9 Y% \! r" P
- rt_uint32_t stack_size,
, ~5 l6 f* G0 S% Q9 t - rt_uint8_t priority,6 w4 ?% W3 F* A
- rt_uint32_t tick)$ A# U: ]( G6 d4 f1 ~6 M* I9 C2 v
- {
2 Z6 o2 a) `" J5 V4 @1 F% ~5 w - /* init thread list */
& I* ~; T P3 t8 x- K2 b - rt_list_init(&(thread->tlist));
7 G5 g/ @7 e0 r/ y - 0 A/ [. m/ N& l! Y$ x
- thread->entry = (void *)entry;% k0 Z S1 g' z+ V- y& W4 _
- thread->parameter = parameter;' E4 e' a X) C# [% |
8 I. P. n* Q; U, H- /* stack init */
$ T2 v" U7 ?5 l4 [( T- p - thread->stack_addr = stack_start;
4 H9 ]& X/ r% g0 j+ @ X! u1 i - thread->stack_size = stack_size;6 _/ l/ `+ W" S
- 1 b/ a0 X& g% H4 X1 b
- /* init thread stack */
- J2 D# T( y y. Y$ S+ N6 l5 @ P( Y - rt_memset(thread->stack_addr, '#', thread->stack_size);
5 @# h2 K! _" L9 G9 ` - thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,- Q( {$ c' J" J0 ^! L T
- (void *)((char *)thread->stack_addr + thread->stack_size - 4),
. O; V$ j' R# L5 o5 Y0 j& j - (void *)rt_thread_exit);% F* y* p4 t, ^+ s9 N0 J8 l6 h+ r0 \
! d& b( J* N' |. d) ^. o- /* priority init */" G& {1 R7 A. E. e: ]
- RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
7 G: z2 F% y# @" L7 S; C" l. n - thread->init_priority = priority;4 ]2 q s' {3 G, x6 Y# m, l
- thread->current_priority = priority;
+ ` _0 n' [3 u% z- x
+ F; ?; R4 `) A- thread->number_mask = 0;
9 X( w3 N$ {5 d! A1 z
3 ~: l4 e0 }% G6 N' m6 H- /* tick init */$ X" |" w8 Q& N O( J
- thread->init_tick = tick;
5 O+ m7 x/ T/ G: m, G0 \7 d - thread->remaining_tick = tick;+ F! H/ f( f' d9 D
- - @# ]5 o6 }4 ~* l1 A4 m
- /* error and flags */
B) I" x5 w# y2 z" ` - thread->error = RT_EOK;6 C! d5 F8 b+ n2 r8 o3 n
- thread->stat = RT_THREAD_INIT;) U# K2 f3 _4 h7 z# ]
- . E; o/ g2 h8 }0 Q8 b) a; Q! b
- /* initialize cleanup function and user data */
{& Z$ z0 G" D8 s - thread->cleanup = 0;8 c- x4 q5 E6 A [6 m) e8 ^
- thread->user_data = 0;
( E& A( l: \ L, @/ R
$ i' d! P; a2 v) n8 X& ^1 {# \: M g7 w! i- /* init thread timer */
. S. ?1 C0 D" L9 M7 I9 `3 E( @9 [* z - rt_timer_init(&(thread->thread_timer),
( E5 [7 K2 B1 d9 e - thread->name," W6 A8 @; ]5 e# ^" I1 C* G
- rt_thread_timeout,/ Z4 e+ i; T) b
- thread,7 u3 A/ L5 t; ~6 q" ^% }1 r
- 0,
^# X* N9 r6 Z$ u# e - RT_TIMER_FLAG_ONE_SHOT);1 D$ n" ]& y! @ X5 Z& h2 g
7 b# M5 M4 A9 {- RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));2 s) z( b' l, T2 b$ D
! u( M' }: R w9 |% }- K- return RT_EOK;- ?2 w; r* M {- x5 N
- }
复制代码 从上面的函数中可以看出线程栈的初始化由函数 rt_hw_stack_init() 来完成,每个线程的栈由三部分组成:自动加载到 CPU 寄存器的部分、手动加载到 CPU 寄存器的部分以及空闲栈。如下图所示:7 c+ F5 G- L2 V1 W8 g; J3 r+ x
3 k! X3 ~0 N1 e% [
初始化后除了空闲栈部分其他值都变成 0xdeadbeef,线程第一次运行时,自动加载到 CPU 部分的内容要预先设置好,
( H# O3 ~7 Z# N4 l# G' E$ ^包括 PC 指针、R0任务形参,其它设置为 0。
: a$ y3 j) P0 k1 t9 U1 H线程初始化后要插入到就绪列表中,通过函数 rt_list_insert_before() 实现,最后要运行线程则需要启动调度器,
- X3 ?1 ?; T' d o6 `启用调度器通过函数 rt_system_scheduler_start() 实现,具体函数实现如下:4 a0 D1 F) R& @$ b* h& |
- <blockquote>void rt_system_scheduler_start(void)
复制代码 函数中 rt_hw_context_switch_to() 用于启动第一次线程切换,该函数通过触发 PendSV 中断来实现上下文切换,后面的
$ N4 L0 t. O+ _1 h上下文切换则通过函数 rt_hw_context_switch() 实现。最后系统调度通过 rt_scheduer() 函数实现,其核心内容也是通过
. J- h2 H* {& D. @% F' ^获取下一个要执行的线程然后产生上下文切换,在此不详细说明。
7 v1 ?- H v" k/ F: B* ?, j6 s( x0 ]6 Y' X0 \& |: ] W
' P+ ?+ I% q* T8 t4 ]3 @7 D# h
第一次分享帖子,写得不好多多见谅。帖子内容有限,只是说明一下我个人对于其中的一点理解,各位有兴趣的还是得亲
1 l8 n8 m2 K7 j' Q% i; }自去看书,个人觉得书是挺好的,由浅入深,循序渐进,但是看完书后最重要的还是得多实践,要想深入内核原理的还得
3 d' t6 I% s. Y- I+ @2 \慢慢啃源码。
, a. W$ L, X" i- E0 i9 X4 Y- M
# w; d, n* r: m) q0 F, K/ [ |