本帖最后由 永不停息 于 2018-12-25 09:34 编辑 % f3 }" S: v8 z& p }1 ~7 k1 U6 B/ I
7 G$ O* }- b1 Y$ ^) j. _3 L: U
【RT-Thread内核实现与应用开发实战指南】本书总共分为两个部分,第一部分主要剖析 RT-Thread 内核的实现机理,通过代码从操作系统最基本的线程创建和线程切换开始,一步一步增加代码功能(线程阻塞、时间片、优先级、临界区等)
# ?! M1 @+ F/ D ?4 n; }由浅入深从开发者的角度教读者怎么从零实现基本的操作系统内核。虽然内容不是很细致,但是能够帮助读者抓住重点,
* |) N8 E% D0 T, S( Q# W让读者能够最快速的了解操作系统的核心,读完这部分然后再去阅读源码会比直接阅读源码事半功倍。第二部分内容着重' S9 O2 X+ D8 \3 @1 i/ P
RT-Thread 的应用,分别介绍操作系统各种机制(信号量、互斥量、时间、邮箱等)的工作机理以及相关的接口函数,读者% \4 }! d# B, T* i
可以通过这部分去熟悉 RT-Thread 的应用。
- q. ?. Y8 w' r+ S# E
* F( f- I+ f# U4 n% R/ Y, @由于本人之前对一些操作系统(FreeRTOS)也有所学习和应用,但是没有深入去学习源码,对于操作系统内核的实现机制4 n, h; }+ K8 ] c3 |! E- C5 U
并不是很了解,因此这次也将学习重点放在第一部分,下面针对第一部分的线程创建和切换这部分进行总结。
( @- d+ J, j5 l% Z$ ~, G3 ?' k) n. |% i2 l1 e/ Z
对于操作系统而言,线程是最基本的概念,什么是线程呢?线程是程序执行的最小单元,每个线程都拥有独立的栈空间
# n9 d5 K# G. e,是系统调度的基本单元。操作系统最核心的地方也在于线程的调度,系统为了方便线程调度,为每个线程都额外定义了" T0 I" \, L6 y/ w% o0 ]
线程控制块,线程控制块的定义如下:( Q1 Q* a) C0 J7 v
- /**3 U% ?: F$ k5 C: i- r
- * Thread structure
. T# |- B% w( Y0 J$ y1 V: _, n - */& P: ~5 g! A" e; Z; E. f1 v
- struct rt_thread. N; X q: p( K s
- {
' b' {$ c+ h1 | - /* rt object */- w5 ~1 x" w% [* c% B5 a- E) s' E
- char name[RT_NAME_MAX]; /**< the name of thread */
0 n+ _& `, `: H1 d - rt_uint8_t type; /**< type of object */! K7 \# b/ b' L2 ?- c/ {
- rt_uint8_t flags; /**< thread's flags */8 F0 r8 K6 g x1 D1 D0 L: T5 x
. r8 ~; n3 U1 V8 t0 l3 \- rt_list_t list; /**< the object list */2 o7 o; S F5 J% i7 |
- rt_list_t tlist; /**< the thread list */8 \2 ^7 f, ]1 Q3 g
/ O! [$ b/ w3 C0 @) D& _# @$ v& b- /* stack point and entry */
# l, m$ }7 P( s: f' D+ g8 X: L9 F - void *sp; /**< stack point */
9 n* m* b0 U4 q* e4 i - void *entry; /**< entry */) W3 X, O* x4 X- b8 J' U3 \
- void *parameter; /**< parameter */9 v1 L% f% ~2 l6 ^; D, e
- void *stack_addr; /**< stack address */
) {4 k" T+ J' r; B - rt_uint32_t stack_size; /**< stack size */6 T, b4 {; A' ?- `3 D; S
- & \1 S$ {* o# k- T" U( b/ L
- /* error code */
+ S6 ~1 `' y. ~5 E - rt_err_t error; /**< error code */; A( i t' ~7 g" ~
- * R0 ~' G0 C# v6 I' } D
- rt_uint8_t stat; /**< thread status */( M. I5 p0 s { J6 B# s
- 5 Y( H4 P% ^! e8 `
- /* priority */
6 e M) n! z0 ~$ }+ ]3 W - rt_uint8_t current_priority; /**< current priority */5 h1 U: q. J A4 o& v2 ]$ D; s B" B3 ]
- rt_uint8_t init_priority; /**< initialized priority */
复制代码 其中 tlist 成员是线程的链表节点,后面要把线程插入到各种双向链表中便是通过该链表节点实现,该链表节点定义如下:
2 n: A, E; H% q& ~* |9 n; _2 o- j! c- struct rt_list_node
3 a8 s) V7 x3 k; }. t - {
% y7 Z; c1 @9 X6 c w - struct rt_list_node *next; /**< point to next node. */
4 z3 F+ J& N- \2 r - struct rt_list_node *prev; /**< point to prev node. */
! v" a1 ^; R* q; D0 j - };9 G& |0 m9 o" D4 @$ j
- typedef struct rt_list_node rt_list_t; /**< Type for lists. */
复制代码 线程初始化通过函数 rt_thread_init(struct rt_thread *thread) 实现,主要进行线程控制块相关成员的初始化以及初始化线程栈
" |# G' c! F& L) U; L3 B具体实现如下所示:
% ]3 |6 g! C7 Y9 I- static rt_err_t _rt_thread_init(struct rt_thread *thread,' y% \; u3 u6 r! x
- const char *name,; j0 V3 S6 N) V. l
- void (*entry)(void *parameter),
( A! q" Y8 c1 P2 w! ?; X4 { - void *parameter,
# z% Q4 P9 Q, `2 O% ^. ^ - void *stack_start,
% c9 p: H7 e6 R; @ - rt_uint32_t stack_size,% t3 Y3 B2 W- @1 A: ?0 c& Q1 v* G
- rt_uint8_t priority,
/ G4 i+ H' h$ _- a - rt_uint32_t tick)9 t7 ~ j0 X0 n: X5 n) I) Z, {" E
- {
1 |0 |2 B7 c7 ]/ k7 }5 m2 F - /* init thread list */
. P) ^% b7 [" G# @, ? - rt_list_init(&(thread->tlist));+ `+ H! {% S+ T9 ]7 i
. h$ X. }. Y; L9 D! [% C9 }/ R- thread->entry = (void *)entry;* F1 C4 ~& D! z; A+ e
- thread->parameter = parameter;; l2 m# s& x3 Q
- . n. S* g3 `- k# V$ q4 U+ o, R$ [
- /* stack init */8 L: W }' v- J& C+ P* D
- thread->stack_addr = stack_start;4 v' [7 I. J# l& }; ~( @5 I0 |
- thread->stack_size = stack_size;: X6 B, p% V2 S8 p
$ B6 l6 A& L1 a a+ `, E- /* init thread stack */
: R# \. q- L. O7 k - rt_memset(thread->stack_addr, '#', thread->stack_size);
; b# A Q* P) M - thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
1 y+ g4 d4 s- j# f% _ - (void *)((char *)thread->stack_addr + thread->stack_size - 4),
; a' `2 ] D& e& s9 h9 D6 C - (void *)rt_thread_exit);0 X, }: l* A. o
0 J5 m. P# y) h! Y) @- /* priority init */
8 Z% {6 ~$ m) s0 d( B& X - RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);/ F2 o$ m" c* b$ ?5 u& l0 V. o* `
- thread->init_priority = priority;5 c: ^# _- \$ y; A1 E% e. ~/ k
- thread->current_priority = priority;
& W8 k, u' P6 D# a8 X - / S c1 E+ f' Y" B( P7 h
- thread->number_mask = 0;# h" w# O$ K; Y( w
- ; f4 N8 K( i4 Z: [
- /* tick init */$ L7 e# [" |4 O" m# c+ @2 ?( t& F
- thread->init_tick = tick;
$ h7 K: F0 S: V - thread->remaining_tick = tick;
$ s1 K" q: [# J
3 W6 g) n* T/ I) f* `& ]" O- /* error and flags */. N& G- {1 s' Z8 n1 T
- thread->error = RT_EOK;
/ I; W$ z' I( f - thread->stat = RT_THREAD_INIT;6 u% g6 o4 W1 Z+ k. d) `
) L# s* s0 G( J% z- /* initialize cleanup function and user data */
9 Z" E- q9 {' v' V# `( Y1 N0 B - thread->cleanup = 0;
( [* S; C6 u1 h/ J. y - thread->user_data = 0;# u# ^# s1 |; }; C3 l4 J6 g
8 j' q- H( c$ o8 @7 `- /* init thread timer */4 L5 E( t( d, M i
- rt_timer_init(&(thread->thread_timer),- c! o! `! A. O: R N
- thread->name,7 M) `4 X- r8 U* F# L% }
- rt_thread_timeout,6 U* n& r/ R+ S% t7 b" ~+ B% N, a
- thread,
( c7 F% G3 j& A/ m - 0,
5 P, s, |, O* b1 ?' w( ~$ q - RT_TIMER_FLAG_ONE_SHOT);0 W7 J5 ^! G+ n2 h* U
( D' @' C: |7 Z( u) e' P5 D- RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));4 R/ e) H! r+ r6 g4 g( W
$ c( ~+ b4 S$ e- v A* R& a3 I- return RT_EOK;
2 P6 _ B' y) v0 X/ M( h+ [) S - }
复制代码 从上面的函数中可以看出线程栈的初始化由函数 rt_hw_stack_init() 来完成,每个线程的栈由三部分组成:自动加载到 CPU 寄存器的部分、手动加载到 CPU 寄存器的部分以及空闲栈。如下图所示:
( R& p9 t8 i0 j/ B3 v4 j$ E7 S
' l- b4 }0 i: R& \! e. Z* [6 _
初始化后除了空闲栈部分其他值都变成 0xdeadbeef,线程第一次运行时,自动加载到 CPU 部分的内容要预先设置好,
( k- f$ ~8 j' P4 ^+ l4 V) @包括 PC 指针、R0任务形参,其它设置为 0。
" c4 p% V7 C# y& D+ G4 s线程初始化后要插入到就绪列表中,通过函数 rt_list_insert_before() 实现,最后要运行线程则需要启动调度器,$ ]" r; e0 ?" \5 q @
启用调度器通过函数 rt_system_scheduler_start() 实现,具体函数实现如下:
4 c# C5 v- V& e% W+ \- <blockquote>void rt_system_scheduler_start(void)
复制代码 函数中 rt_hw_context_switch_to() 用于启动第一次线程切换,该函数通过触发 PendSV 中断来实现上下文切换,后面的4 s2 ^5 r5 a/ A% y0 y) ~. F
上下文切换则通过函数 rt_hw_context_switch() 实现。最后系统调度通过 rt_scheduer() 函数实现,其核心内容也是通过5 x1 K+ }6 Q$ ^" D1 L7 Y
获取下一个要执行的线程然后产生上下文切换,在此不详细说明。 L! f# \7 q& m
- Z8 ?& W/ V! N) }
7 I: B% y5 i4 X |
第一次分享帖子,写得不好多多见谅。帖子内容有限,只是说明一下我个人对于其中的一点理解,各位有兴趣的还是得亲
! O1 ~7 W" t0 R* ~) u# H9 m7 B自去看书,个人觉得书是挺好的,由浅入深,循序渐进,但是看完书后最重要的还是得多实践,要想深入内核原理的还得. ^8 b/ r* C. D: t& A
慢慢啃源码。9 @4 Y% p: P" S, B/ H+ w
; T( l7 l- |, w$ y' G8 N, w |