1. 程式人生 > >2018-2019-1 20189229 《Linux核心原理與分析》第九周作業

2018-2019-1 20189229 《Linux核心原理與分析》第九周作業

教材內容總結

程序排程的時機

  • 中斷處理過程(包括時鐘中斷、I/O中斷、系統呼叫和異常)中,直接呼叫schedule(),或者返回使用者態時根據need_resched標記呼叫schedule();
  • 核心執行緒可以直接呼叫schedule()進行程序切換,也可以在中斷處理過程中進行排程,也就是說核心執行緒作為一類的特殊的程序可以主動排程,也可以被動排程;
  • 使用者態程序無法實現主動排程,僅能通過陷入核心態後的某個時機點進行排程,即在中斷處理過程中進行排程。

總結:

  • 使用者態程序只能被動排程
  • 核心執行緒是隻有核心態沒有使用者態的特殊程序
  • 核心執行緒既可以主動排程也可以被動排程

    程序排程策略與演算法

  • 排程策略:考慮到演算法的整體目標,是追求資源利用率最高,還是追求響應最及時,或其他特定目標。
  • 排程演算法:考慮如何實現排程策略並滿足設定的目標

    程序的分類

  • I/O消耗型程序:需要大量檔案讀寫操作,cpu負載不高,大量時間在等待讀寫資料。
  • 處理器消耗型程序:cpu佔用率100%,但沒有太多硬體進行讀寫操作。
  • 互動式程序:大量人機互動,對系統響應時間要求較高。
  • 批處理程序:在後臺執行,佔用大量系統資源。
  • 實時程序:對排程延遲要求最高。

    排程策略

  • SCHED_FIFO 先進先出的實時程序,如果沒有其它更高優先順序的可執行實時程序,就可以一直使用cpu執行。
  • SCHED_RR 時間片輪轉的實時程序。
  • SCHED_NORMAL 時間片輪轉的普通程序。

    程序的切換

  • 為了控制程序的執行,核心必須有能力掛起正在CPU上執行的程序,並恢復以前掛起的某個程序的執行,這叫做程序切換、任務切換、上下文切換;
  • 掛起正在CPU上執行的程序,與中斷時儲存現場是不同的,中斷前後是在同一個程序上下文中,只是由使用者態轉向核心態執行;
  • 程序上下文包含了程序執行需要的所有資訊
    • 使用者地址空間:包括程式程式碼,資料,使用者堆疊等
    • 控制資訊:程序描述符,核心堆疊等
    • 硬體上下文(注意中斷也要儲存硬體上下文只是儲存的方法不同)
  • schedule()函式選擇一個新的程序來執行,並呼叫context_switch進行上下文的切換,這個巨集呼叫switch_to來進行關鍵上下文切換
    next = pick_next_task(rq, prev);//程序排程演算法都封裝這個函式內部
    context_switch(rq, prev, next);//程序上下文切換
    switch_to利用了prev和next兩個引數:prev指向當前程序,next指向被排程的程序

linux系統架構與執行過程

系統架構

如圖:

ls執行過程

如圖:

實驗八 理解程序排程時機跟蹤分析程序排程與程序切換的過程

配置MenuOS系統

使用gdb跟蹤分析schedule()函式,在schedule、context_switch、switch_to、pick_next_task處設定斷點,由於switch_to為巨集定義,所以實際為在函式__switch_to處設定斷點,如下圖:

執行程式,程式分別停在schedule函式、pick_next_task函式斷點、context_switch處,檢視程式碼:

context_switch中單步執行,呼叫switch_to

switch_to函式如下所示:

31#define switch_to(prev, next, last)                    
32do {                                 
33  /*                              
34   * Context-switching clobbers all registers, so we clobber  
35   * them explicitly, via unused output variables.     
36   * (EAX and EBP is not listed because EBP is saved/restored  
37   * explicitly for wchan access and EAX is the return value of   
38   * __switch_to())                     
39   */                                
40  unsigned long ebx, ecx, edx, esi, edi;                
41                                  
42  asm volatile("pushfl\n\t"      /* 儲存當前程序flags */   
43           "pushl %%ebp\n\t"        /* 當前程序堆疊基址壓棧*/ 
44           "movl %%esp,%[prev_sp]\n\t"  /*儲存ESP,將當前堆疊棧頂儲存起來*/ 
45           "movl %[next_sp],%%esp\n\t"  /*更新ESP,將下一棧頂儲存到ESP中*/ 
        
            //完成核心堆疊的切換
46           "movl $1f,%[prev_ip]\n\t"    /*儲存當前程序EIP*/ 
47           "pushl %[next_ip]\n\t"   /*將next程序起點壓入堆疊,即next程序的棧頂為起點*/    
48         
            //完成EIP的切換
             __switch_canary    
            //next_ip一般是$1f,對於新建立的子程序時ret_from_fork               
49           "jmp __switch_to\n"  /*prev程序中,設定next程序堆疊*/ 
            //jmp不同於call是通過暫存器傳遞引數
50           "1:\t"                //next程序開始執行
51           "popl %%ebp\n\t"       
52           "popfl\n"         
53                                  
54           /*輸出變數定義*/                
55           : [prev_sp] "=m" (prev->thread.sp),     //[prev_sp]定義核心堆疊棧頂
56             [prev_ip] "=m" (prev->thread.ip),     //[prev_ip]當前程序EIP  
57             "=a" (last),                 
58                                  
59             /* 要破壞的暫存器: */     
60             "=b" (ebx), "=c" (ecx), "=d" (edx),      
61             "=S" (esi), "=D" (edi)             
62                                       
63             __switch_canary_oparam                
64                                  
65             /* 輸入變數: */                
66           : [next_sp]  "m" (next->thread.sp),     //[next_sp]下一個核心堆疊棧頂    
67             [next_ip]  "m" (next->thread.ip),     
68           //[next_ip]下一個程序執行起點,,一般是$1f,對於新建立的子程序是ret_from_fork 
   
69             /* regparm parameters for __switch_to(): */  
70             [prev]     "a" (prev),              
71             [next]     "d" (next)               
72                                  
73             __switch_canary_iparam                
74                                  
75           : /* 重新載入段暫存器 */           
76          "memory");                  
77} while (0)