Xen 程式碼分析分析(3.一次排程的處理)
Xen排程入口函式位於檔案”./xen/common/schedule.c”中的static voidschedule(void)。排程的觸發方式會有多種,在Xen中確定已知的有定時觸發、中斷返回觸發、任務阻塞觸發(分析能觸發排程的時機是非常重要的,但由於能力所限,短時間內不能列舉,此處僅列舉分析過程中確定可以觸發排程的時機。)。所有觸發的排程最終都會傳導到schedule。
tasklet_work(tasklet_work=&this_cpu(tasklet_work_to_do)是當前pcpu變數,由DECLARE_PER_CPU定義。)是當前pcputasklet work的狀態標誌,可以為TASKLET_enqueued、TASKLET_scheduled以及TASKLET_enqueued|TASKLET_scheduled,如果僅有TASKLET_scheduled置位,表示不需要處理,否則排程器會執行idle_vcpu,idle_vcpu會執行do_tasklet(),並調整標誌位。do_tasklet()會呼叫do_tasklet_work(),在do_tasklet_work()中,tasklet的func會被執行;只有在tasklet連結串列中不再有元素時,do_tasklet()會清除TASKLET_enqueued標誌位。在TASKLET_enqueued標誌位被清除時schedule()會清除TASKLET_scheduled。
sd是當前pcpu的schedule_data,其中包含鎖、struct vcpu *curr(當前正在執行的任務vcpu資料結構指標。)、void *sched_priv(這是排程器的私有資料結構入口。)、struct timer s_timer(排程定時器,軟中斷將會處理其內function函式。)、urgent的vcpu計數。各pcpu的struct timers資料(struct timers也是per-cpu變數,依舊是繼承自Linux的各pcpu變數定義,struct timers並未被DECLARE_PER_CPU引用宣告,由巨集DEFINE_PER_CPU定義,DEFINE_PER_CPU是對per-cpu變數的真實定義,而DECLARE_PER_CPU是對per-cpu變數的引用宣告。&this_cpu(a)訪問本pcpu的per-cpu變數&per_cpu(a,cpu0)訪問cpu0核的per-cpu變數。)
呼叫當前pcpu的排程器計算下一個要投入執行的任務,其中next_slice包含3個元素:structvcpu *task( vcpu是Xen排程的基本單位。)、s_time_t time(指示當前task_slice將會執行多久,主要來自排程器自定資料結構中xx_vcpu->cur_budget。)、bool_t migrated(指示這個任務是否是從其他pcpu遷移到這裡的。);sched為排程器結構體,指向當前pcpu排程器指標;do_schedule()為排程器排程計算函式入口[49];now=NOW()是CPU時間;tasklet_work_scheduled是tasklet需要排程投入執行的標誌(tasklet_work_scheduled置位會使排程器在選擇任務使選中idle_vcpu,idle_vcpu內有tasklet處理函式入口do_tasklet()。)。
4. next =next_slice.task;
next是將會被投入執行的任務。
5. sd->curr= next;
至此,schedule函式選好了投入執行的任務,記錄到sd的curr(可執行狀態RUNSTATErunnable遇到tasklet佔用、高優先順序的vcpu等是會被排程出去的。)。
if (next_slice.time >= 0 ) set_timer(&sd->s_timer, now +next_slice.time);
next_slice.time只有在下一個任務使idle_vcpu的時候才會小於0;set_timer()將會給當前pcpu的排程定時器續時,時長決定於next_slice.time。
7. 省略
TRACE_3D()、TRACE_4D()會記錄排程的切換,不予分析;當計算完發現即將投入執行的任務還是之前的任務,則會直接投入執行。
8. vcpu_runstate_change();
修改被排程出局任務的runstate,runstate記錄有vcpu在各狀態停留時間,根據被排程出局的原因:阻塞、離線、可執行[52],是一個struct vcpu_runstate_info資料結構,包含int state(state元素記錄vcpu當前所處狀態:RUNSTATE_running、RUNSTATE_runnable、RUNSTATE_blocked、RUNSTATE_offline)、uint64_t state_entry_time(vcpu進入當前狀態的時間。)、uint64_t time[4](state元素的4個狀態,分別對應陣列中的四個元素,記錄有當前vcpu在四個狀態的累計時間。)。
9. prev->last_run_time= now;
記錄被排程出局的vcpu的出局時間。
10. vcpu_runstate_change(next,RUNSTATE_running, now);
記錄即將投運任務的runstate。
11. next->is_running= 1;
標誌著vcpu正在執行中。
12. stop_timer(&prev->periodic_timer);
關閉排程出局的vcpu的periodic_timer,其處理函式為vcpu_periodic_timer_fn()( init_timer(&v->periodic_timer,vcpu_periodic_timer_fn, v, v->processor);),periodic_timer的作用是向vcpu定時發出虛擬中斷訊號(通過evtchn_port_set_pending()向vcpu傳送了一箇中斷訊號,);此處將之關閉即不再發出此虛擬中斷。
對於即將投入執行的vcpu,如果是從別的cpu遷移過來的,則需要調整他的IRQ到當前的cpu上(這又是一個大活兒啊,另外還涉及到Xen對中斷機制的操作,)。
14. vcpu_periodic_timer_work(next);
即將投入執行的vcpu的periodc_timer的啟動:首先檢測當前是否到vcpu的週期,決定是否發出virq;然後檢查並遷移periodic_timer到在當前pcpu名下(migrate_timer()完成);最後設定vcpu下一個virq發生點。
15. context_switch(prev,next);
進行了任務切換( context_switch()需要做很多很多事,不過功能卻只有一個,任務切換,於是先不分析細節。)。
structvcpu 分析
vcpu是Xen的基本排程單位,其資料結構structvcpu複雜(還好我已經搞明白了不少。),下面將分析其關鍵元素。
int |
vcpu_id; |
vcpu識別號 |
int |
processor; |
vcpu執行的pcpu號 |
vcpu_info_t |
*vcpu_info; |
NC |
struct domain |
*domain; |
指向vcpu所在的域 (即描述虛擬機器的主資料結構) |
s_time_t |
periodic_period; |
時間計數,週期時長 |
s_time_t |
periodic_last_event; |
時間計數,上次週期開始時間 |
struct timer |
periodic_timer; |
週期定時器,給vcpu提供虛擬中斷的 |
struct timer |
poll_timer; |
/* timeout for SCHEDOP_poll */ |
void |
*sched_priv; |
指向vcpu所處排程器的私有資料結構,與排程演算法密切相關,由演算法實現者定義,對於RT排程,此指標指向struc rt_vcpu |
struct vcpu_runstate_info |
runstate; |
記錄vcpu執行狀態、進入此執行狀態的時間,在各個執行狀態累積時間。 |
uint64_t |
last_run_time; |
記錄排程出去的時間 |
bool |
is_initialised; |
/* Initialization completed for this VCPU? |
bool |
is_running; |
正在pcpu上執行的標誌 |
unsigned long |
pause_flags; |
vcpu被暫停標誌 |
atomic_t |
pause_count; |
被暫停計數 |
cpumask_var_t |
cpu_hard_affinity; |
允許vcpu執行的pcpu點陣圖 |
cpumask_var_t |
cpu_hard_affinity_tmp; |
/* Used to change affinity temporarily. */ |
cpumask_var_t |
cpu_hard_affinity_saved; |
/* Used to restore affinity across S3. */ |
cpumask_var_t |
cpu_soft_affinity; |
/* Bitmask of CPUs on which this VCPU prefers to run. */ |
struct arch_vcpu |
arch; |
記錄有ARM的各個暫存器值(含pc、sp)以及其他不認識的,在任務切換時大顯身手 |
cpumask_var_t |
vcpu_dirty_cpumask; |
/* Bitmask of CPUs which are holding onto this VCPU's state. */ |
struct vcpu還有很多很多元素,以上元素佔比<50%,其他元素與排程關係不大,省略。 |
structrt_vcpu分析
struct rt_vcpu是RT排程專有資料結構,如下為全部元素:
struct list_head |
q_elem |
作為一個連結串列元素,可能被放在RunQ連結串列、DeplQ連結串列或為空。 |
struct list_head |
replq_elem |
作為連結串列元素可能被放在ReplQ或為空 |
s_time_t |
period |
設定引數,vcpu的虛擬中斷觸發週期; 預設週期RTDS_DEFAULT_PERIOD為10毫秒 |
s_time_t |
budget |
設定引數,vcpu作為Xen中的任務,被排程時要設定的預算值,即允許持續執行的時間; 預設預算RTDS_DEFAULT_BUDGET為4毫秒 |
s_time_t |
cur_budget |
執行時引數,vcpu剩餘預算值 |
s_time_t |
last_start |
執行時引數,vcpu開始執行時間 |
s_time_t |
cur_deadline |
執行時引數,vcpu的deadline |
struct rt_dom |
*rt_dom |
NC |
struct vcpu |
*vcpu |
指向所描述vcpu主體 |
unsigned |
priority_level |
vcpu的排程優先順序 |
unsigned |
flag |
標誌位 RTDS_scheduled表示此vcpu是否正在pcpu上執行; RTDS_delayed_runq_add表示此vcpu被排程暫停執行時,將會被加入Runq還是DeplQ; RTDS_depleted表示此vcpu是否還有預算; RTDS_extratime置位時,預算耗盡將會自動補充 |