iOS底層系統:Mach排程原理之排程原語
E-moss,程式設計師,愛好閱讀和擼狗,主要從事iOS開發工作,公眾號:知本集。 主要分享和編寫技術方面文章,不定期分享讀書筆記,亦可訪問“知本集”Git地址:https://github.com/knowtheroot/KnowTheRoot_iOS,歡迎提出問題和討論。 複製程式碼
Git地址:github.com/knowtheroot…
- 執行緒 :和所有的現代作業系統一樣,Mach核心排程的物件是執行緒。
- 任務 :Mach中使用一個比程序更輕量級的概念:任務(task)。
最基本的單位是執行緒,一個任務包含一個或多個執行緒。
一、執行緒(thread)
1.什麼是執行緒
執行緒的定義
執行緒(thread)定義了match中最小的執行單元。
執行緒的含義
執行緒表示的是底層的機器暫存器狀態以及各種排程統計資料 。
執行緒的設計
執行緒從設計上提供了排程所需要的大量資訊,同時儘可能地維持最小開銷。
2.執行緒的實現
這裡以簡化的程式碼為例子:
struct thread { /* 頻繁讀,但很少修改的欄位 */ queue_chain_t links;/* 執行佇列/等待佇列的連結串列連線 */ wait_queue_t wait_queue;/* 所在的等待佇列 */ ... ... /* thread_invoke中更新/使用的資料 */ vm_offset_t kernel_stack;/* 當前的核心棧 */ vm_offset_t reserved_stack;/* 預留的核心棧 */ ... ... /* * 執行緒狀態位 */ #define TH_WAIT0x01/* 在佇列中等待 */ #define TH_SUSP0x02/* 停止,或請求停止 */ #define TH_RUN0x04/* 正在執行或在執行佇列中 */ #define TH_UNINT0x08/* 在不可中斷的等待 */ #define TH_TERMINATE0x10/* 終止時同時執行 */ #define TH_TERMINATE20x20/* 新增到終止佇列 */ #define TH_IDLE0x80/* 空閒執行緒 */ ... ... /* 排程資訊 */ sched_mode_t sched_mode;/* 排程模式 */ sched_mode_t saved_mode;/* 在被迫模式降級時儲存的模式 */ ... ... /* 排程相關的狀態位 */ integer_t sched_pri;/* 當前排程的優先順序 */ integer_t priority;/* 基礎優先順序 */ integer_t importance ;/* 任務相關的重要性 */ ... ... /* 定時相關的資料結構 */ timer_data_t user_timer;/* 使用者定時器 */ ... ... } 複製程式碼
執行緒模板thread_template
可以發現,thread的資料結構是非常巨大的,因此大部分建立執行緒的時候都是從一個通用的模板複製而來 ,這個模板使用預設值填充這個資料結構。這個模板名為thread_template。
核心引導的過程中,呼叫thread_bootstrap()方法負責填充這個模板。
thread_create_internal()函式會分配新的執行緒資料結構。
Mach API的thread_create() 就是通過thread_create_internal()實現的 。
執行緒的容器
Mach將任務定義為執行緒的容器。
資源是在任務這個層次處理的,執行緒只能通過埠訪問包含這個執行緒的任務中分配的資源和記憶體 。
二、任務(task)
1.任務的定義
任務(task)是一種容器物件。虛擬記憶體空間和其他資源都是通過這個容器物件管理的。
資源被進一步抽象為埠。
因此資源的共享實際上相當於允許對對應埠進行訪問。
每一個BSD程序(也就是OS X程序)都在底層關聯了一個Mach任務物件。
相對於執行緒,任務是一個比較輕量級的資料結構:
struct task { /* 同步相關的資訊 */ unit_32_t ref_count;/* 引用計數 */ boolean_t active;/* 任務還沒有終止 */ boolean_t halting;/* 任務被停止 */ ... ... /* 雜項 */ vm_map_t map;/* 地址空間對映 */ ... ... /* 任務中的執行緒 */ queue_head_t threads;/* 用FIFO佇列儲存執行緒 */ int thread_count;/* 執行緒佇列中的執行緒數 */ unit32_t active_thread_count;/* 活動的執行緒數 */ integer_t priority;/* 執行緒基礎優先順序 */ integer_t max_priority;/* 執行緒的最高優先順序 */ ... ... //每個任務都有自己私有的埠名稱空間 struck ipc_space *itk_space; ... ... #ifdef MACH_BSD void *bsd_info;//指向BSD程序物件 #endif } 複製程式碼
2.任務存在的目的
就本身而言,任務是沒有生命的。任務存在的目的就是要成為一個或多個執行緒的容器 。
任務中的執行緒都在threads成員中維護,這是一個包含thread_count個執行緒的佇列。
3.任務的操作
大部分針對任務的操作實際上就是遍歷給定任務中的所有執行緒,並對這些執行緒進行對應的執行緒操作。
4.賬本
定義
賬本(ledger)是Mach任務的配額記賬和設定限制所需要的機制。
資源(一般指CPU資源和記憶體資源)可以在賬本間轉移。
三、任務和執行緒相關的API
1.獲取當前的任務和執行緒
在任何時刻,核心都必須能夠獲得當前任務和當前執行緒的控制代碼。
核心分別通過:
- current_task()
- current_thread()
兩個函式來完成。
2.函式內部實現
以上兩個函式都是對“fast”版本的函式呼叫的巨集。既:
- current_task()呼叫current_task_fast()
- current_thread()呼叫current_thread_fast()
四、任務相關的API
Mach提供了完整的一套用於操作任務的API。
這裡列舉幾個介面:
Mach任務相關API | 用途 |
---|---|
mach_task_self() | 獲取任務埠,帶有傳送許可權的名稱 |
task_create(task_t target_task, ledger_array_t ledgers, mach_msg_type_number_t boolean_t task_t *child_task) | 以target_task為父任務建立一個child_task。 |
五、執行緒相關的API
類似任務相關的API,Mach還提供了豐富的執行緒管理API。這些API大部分都和任務API的功能類似。
實際上,任務API通常的實現方法就是遍歷任務中的執行緒列表,然後對每個執行緒執行對應的操作 。
這些呼叫大部分都是通過Mach訊息實現的。
注意:phtread中的指POSLX的系統呼叫。
1.核心私有的執行緒API
Mach核心提供了一組執行緒控制的函式,這些函式只能核心態中呼叫,這裡暫時不做展開。
2.執行緒建立
我們特別關注一下執行緒建立的API。
這部分介面定義在<mach/ARCH/task.h>中:
Mach執行緒API | 用途 |
---|---|
thread_create (task_t parent, thread_act_t *child_act) | 在parent任務中建立一個執行緒,將結果返回在child_act中 |
thread_create_running (task_t parent, thread_state_flavor_t flavor, thread_state_t new_state, mach_msg_type_number_t nsCnt, thread_act_t *child_act) | 在parent任務中建立一個執行緒,其初始狀態為new_state,thread_state_t與機器架構有關 |
注意第一個引數是task_t,這個引數表示建立執行緒的任務。
從Mach的角度看,執行緒可以建立在任何任務中,只要使用者具有任務對應的埠即可。
當呼叫pthread_create()的時候,底層轉而會呼叫Mach的APi呼叫thread_create()方法。
注意:
建立一個執行緒並不困難,但是要讓執行緒做有意義的事情則不是那麼容易了。這一內容將在後面討論。