中級篇 01 從執行緒的建立到就緒態

2021-08-28 23:19:34 字數 4332 閱讀 6661

多執行緒的表象就是cpu來回在多個執行緒中切換,造成這幾個執行緒同時都在響應的感覺。所以多執行緒的核心是排程,而使排程有意義就需要告訴系統,當前可以排程那些執行緒。

在rt-thread中,執行緒建立——>加入就緒態——>執行緒執行。

就緒態的實現,是因為引入了乙個雙向鍊錶,將執行緒插入鍊錶則表示就緒態,刪除則表示脫離就緒態。

棧,乙個神奇的地方。在我之前轉的乙個文章裡已經對這個有了一定程式的敘述。

棧區(stack)— 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。

align(rt_align_size)

static rt_uint8_t threadx_statck[512];

static struct rt_thread threadx;

執行緒結構體中包含很多資訊,而恰巧先前建立的threadx_statck[512]的位址和大小就會被記錄其中。當然這個是完全版的,包含很多資訊。不過還好注釋很翔實,這對於往後的學習非常有幫助。

不過,還是有一些疑惑。靜態建立的有threadx_statck一段陣列作為棧空間,然而又定義了乙個rt_thread結構體來從而記錄tcb資料塊的資料。雖然後續也有對threadx_stack的操作,但是執行緒執行的資料到底是存放在結構體那個位置,還是放在陣列這個棧空間位置了?是怎麼實現的?ps:在rt_hw_statck_init()函式裡,相當明顯得顯示了threadx_statck裡的資料是arm暫存器的資料,和tcb資料塊裡的資料並不是同一類資料。具體的內容下一節在中斷儲存和恢復中分析。

執行緒的動態建立方式摒棄了建立threadx_stack陣列,那麼他的資料是不是完全存放在堆中而非棧中?ps:剛才翻看了creat函式,的確是儲存在堆空間裡的。

/**

* thread structure

*/struct rt_thread

;typedef struct rt_thread *rt_thread_t;

這裡還有_rt_thread_init函式,不是rt_thread_init函式啊,看清

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)

在另外的帖子裡,有乙個關於為什麼rtos系統總會讓乙個執行緒用乙個執行緒棧而不共用同乙個執行緒棧的意義。《傳送門》在rtos中,棧大小就有了很大的意義,尤其是對於靜態建立而言。通過查閱.map檔案是理論上是可以分析棧空間大小是多少。不過,仍然是有疑問的,就是啟動檔案中可以檢視到statck_size,以及heap_size的大小定義,可是在程式設計使用時候,不太明白這個大小對於程式的意義,或者說感覺空間太小了不夠用但是程式貌似還是正常的。在日後的學習中,如果搞明白了,我再寫一篇說明。

如果你現在回頭看,我在第乙個**塊的位置,也裝模作樣的寫了乙個align(rt_align_size),其實講道理,我只是聽說這樣可以增加讀取的速度和節約存放的空間。講道理還是對編譯過程不熟悉,不過,我現在的水平也就是這樣,揠苗助長確實不妥,還是一步步來吧,先記著。

在rtos中,執行緒的排程基礎:首先得有乙個執行緒。當然這是一句廢話,那不是廢話的部分就是,執行緒的建立的實現。

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)

這裡仍然是_rt_thread_init函式,去掉一些暫時還看不懂的內容,是不是輕快多了,至少我感覺是這樣的。輸入的引數,大部分被struct rt_thread *thread給儲存到tcb控制塊中,然後,初始化乙個雙向鍊錶rt_list_init();這個函式的定義在rtservice.h函式中,嗯嗯~~~這種在.h寫函式貌似就沒有報錯,編譯出來沒有錯誤,也不清楚為什麼不寫到.c檔案檔案中。疑惑?

雙向鍊錶和單向鍊錶的定義

/**

* double list structure

*/struct rt_list_node

;typedef struct rt_list_node rt_list_t; /**< type for lists. */

/** * single list structure

*/struct rt_slist_node

;typedef struct rt_slist_node rt_slist_t; /**< type for single list. */

/**

* @brief initialize a list

* * @param l list to be initialized

*/rt_inline void rt_list_init(rt_list_t *l)

/**

* @brief insert a node after a list

* * @param l list to insert it

* @param n new node to be inserted

*///選定的節點後新增

rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)

/** * @brief insert a node before a list

* * @param n new node to be inserted

* @param l list to insert it

*///選定的節點前新增

rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)

/** * @brief remove node from list.

* @param n the node to remove from the list.

*///刪除乙個節點

鍊錶已經有功能了,初始化,增加,刪除。功能可以說比較完備了,鍊錶的作用在這裡是有準備就緒列表的作用。

就緒列表的樣子並不是我一開始理解得乙個雙向鍊錶,而是乙個優先順序一組就緒列表。也很好理解,因為放在乙個就緒列表裡,的確分不清優先順序,放在不同的就緒列表裡就不會出現這種情況了。

這就是從執行緒建立到插入就緒鍊錶的全部過程,嗯,插入就緒列表有乙個函式

/*

*在rtconfig.h

*/#define rt_thread_priority_max 32

/**在scheduler.c

*/rt_list_t rt_thread_priority_table[rt_thread_priority_max];

/**將執行緒插入就緒列表中

*/rt_list_insert_before(&(rt_thread_priority_table[0],&(thread1.tlist));

嗯,不要以為rt_thread_priority_table[0]的*next是rt_thread_priority_table[1]就行。

C 修煉篇 01 從C到C 的公升級

引言 本文意在完成一場從c到c 世界的平滑過渡,因此要求讀者應具備一定的c語言基礎功底。文章嚮導 一 c與c 的關係c與c 的關係並不能簡單地概括為僅多了兩個 號而已,兩者實際上既有區別又有聯絡,且並無所謂的何者好壞之說。簡單說來,c 其實是一種更好的c語言,只不過兩者適用場景並不相同。由上圖可知,...

程序從建立到結束的狀態

乙個程序的誕生,是從其父程序呼叫fork 開始的。程序從剛開始被建立出來,是處於task running 就緒態 程序被放在等待佇列中排隊等待系統排程。linux核心中的函式sched 稱為排程器,它會根據各種引數來選擇乙個等待的程序去占用cpu,當程序占用了cpu後,就從就緒態轉變成了執行態。此時...

Vue專案從建立到發布的流程

建立乙個新專案,個人webpack建立 建立完安裝npm包 npm i or yarn add 啟動專案 npm run dev or yarn run dev 頁面結構布局 安裝自己需要的依賴 個人一些基礎的 axios axios的封裝 element 移動端一畫素邊框問題和初始化reset 1...