1. 程式人生 > >Xen 程式碼分析分析(3.一次排程的處理)

Xen 程式碼分析分析(3.一次排程的處理)

Xen排程入口函式位於檔案”./xen/common/schedule.c”中的static voidschedule(void)。排程的觸發方式會有多種,在Xen中確定已知的有定時觸發、中斷返回觸發、任務阻塞觸發(分析能觸發排程的時機是非常重要的,但由於能力所限,短時間內不能列舉,此處僅列舉分析過程中確定可以觸發排程的時機。)。所有觸發的排程最終都會傳導到schedule。

tasklet_worktasklet_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變數。

中有struct timer,分為struct timer **heap根據add_to_heap()和remove_from_heap()的分析,struct timer **heap是一個timer堆,大概按照超時順序排列。如果發現所插入timer為堆內首個timer,則會軟體產生一個TIMER_SOFTIRQ,堆滿才用連結串列。,struct *liststruct timers中的struct timer list是一個timer連結串列,按照超時時間大小排列,在struct timer ** heap空間不夠時,才會用連結串列。發現所插入timer是連結串列第一個元素時會軟體產生TIMER_SOFTIRQ。
,struct timer *runningstruct timers中的struct timer running在timer的function被執行時(見20.中timer_softirq_action()),會從timer堆或timer連結串列中移除,然後實際執行時(execute_timer())被加入到running連結串列。,struct list_head inactivestruct timers中的struct timer inactive是timer被deactivate之後的存放之處。,此處將struct timer->status設定為TIMER_STATUS_inactive,並加入timer所屬pcpu下struct timers資料的inactive連結串列。timers和timer有著完整的機制描述,可在軟定時器機制struct timers和struct timer檢視。

呼叫當前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 statestate元素記錄vcpu當前所處狀態:RUNSTATE_running、RUNSTATE_runnable、RUNSTATE_blocked、RUNSTATE_offline、uint64_t state_entry_timevcpu進入當前狀態的時間。、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置位時,預算耗盡將會自動補充