進程調度詳解算法

引言

原因

需要進程調度的理由很簡單,即充分利用計算機系統中的CPU資源,讓計算機系統能夠多快好省地完成我們讓它做的各種任務。爲此,可在內存中可存放數目遠大於計算機系統內CPU個數的進程,讓這些進程在操作系統的進程調度器的調度下,能夠讓進程高效(高的吞吐量—throughput)、及時(低延遲—latency)、公平(fairness)地使用CPU。爲此調度器可設計不同的調度算法來選擇進程,這體現了進程調度的策略,同時還需並進一步通過進程的上下文切換(context switch)來完成進程切換,這體現了進程調度的機制。總體上說,我們需要何時調度(調度的時機)、是否能夠在內核執行的任意位置進行調度(調度的方式)、如果完成進程切換(上下文切換)、如果選擇“合適”的進程執行(調度策略/調度算法)、如果評價選擇的合理性(進程調度的指標)。瞭解上述細節,也就可以說是瞭解了進程調度。

進程調度的指標

不同的進程調度算法具有不同的特徵,爲此需要建立衡量一個算法的基本指標。一般而言,衡量和比較各種進程調度算法性能的主要因素如下所示:

  • CPU利用率:CPU是計算機系統中的稀缺資源,所以應在有具體任務的情況下儘可能使CPU保持忙,從而使得CPU資源利用率最高。
  • 吞吐量:CPU運行時的工作量大小是以每單位時間所完成的進程數目來描述的,即稱爲吞吐量。
  • 週轉時間:指從進程創建到作進程結束所經過的時間,這期間包括了由於各種因素(比如等待I/O操作完成)導致的進程阻塞,處於就緒態並在就緒隊列中排隊,在處理機上運行所花時間的總和。
  • 等待時間:即進程在就緒隊列中等待所花的時間總和。因此衡量一個調度算法的簡單方法就是統計進程在就緒隊列上的等待時間。
  • 響應時間:指從事件(比如產生了一次時鐘中斷事件)產生到進程或系統作出響應所經過的時間。在交互式桌面計算機系統中,用戶希望響應時間越快越好,但這常常要以犧牲吞吐量爲代價

這些指標其實是相互有衝突的,響應時間短也就意味着在相關事件產生後,操作系統需要迅速進行進程切換,讓對應的進程儘快響應產生的事件,從而導致進程調度與切換的開銷增大,這會降低系統的吞吐量。

進程調度的時機

進程調度發生的時機(也稱爲調度點)與進程的狀態變化有直接的關係。回顧進程狀態變化圖,我們可以看到進程調度的時機直接與進程在運行態<—>退出態/就緒態/阻塞態的轉變時機相關。簡而言之,引起進程調度的時機可歸結爲以下幾類:

  • 正在執行的進程執行完畢,需要選擇新的就緒進程執行。
  • 正在執行的進程調用相關係統調用(包括與I/O操作,同步互斥操作等相關的系統調用)導致需等待某事件發生或等待資源可用,從而將白己阻塞起來進入阻塞狀態。
  • 正在執行的進程主動調用放棄CPU的系統調用,導致自己的狀態爲就緒態,且把自己重新放到就緒隊列中。
  • 等待事件發生或資源可用的進程等待隊列,從而導致進程從阻塞態回到就緒態,並可參與到調度中。
  • 正在執行的進程的時間片已經用完,致自己的狀態爲就緒態,且把自己重新放到就緒隊列中。
  • 在執行完系統調用後準備返回用戶進程前的時刻,可調度選擇一新用戶進程執行
  • 就緒隊列中某進程的優先級變得高於當前執行進程的優先級,從而也將引發進程調度。

進程調度的方式

這裏需要注意,存在兩種進程搶佔處理器的調度方式:

  • 可搶佔式(可剝奪式,preemptive):就緒隊列中一旦有某進程的優先級高於當前正在執行的進程的優先級時,操作系統便立即進行進程調度,完成進程切換。
  • 不可搶佔式(不可剝奪式non_preemptive):即使在就緒隊列存在有某進程優先級高於當前正在執行的進程的優先級時,當前進程仍將佔用處理機執行,直到該進程自己進入阻塞狀態,或時間片用完,或在執行完系統調用後準備返回用戶進程前的時刻,才重新發生調度讓出處理機。

顯然,可搶佔式調度可有效減少等待時間和響應時間,但會帶來較大的其他管理開銷,使得吞吐量等的性能指標比不可搶佔式調度要低。所以一般在桌面計算機中都支持可搶佔式調度,使得用戶可以得到更好的人機交互體驗,而在服務器領域不必非要可搶佔式調度,而通常會採用不可搶佔式調度,從而可提高系統的整體吞吐量。

進程調度的策略/算法

在早期操作系統的調度方式大多數是非剝奪的,這是由於早期的應用一般是科學計算或事務處理,不太把人機交互的響應時間指標放在首要位置。在這種情況下,正在運行的進程可一直佔用CPU直到進程阻塞或終止。這種方式的調度算法可以很簡單,且比較適用對於響應時間不關心或者關心甚少的批處理科學計算或事務處理應用。隨着計算機的應用領域進一步擴展,計算機更多地用在了多媒體等人機交互應用上,爲此採用可搶佔式的調度方式可在一個進程終止或阻塞之前就剝奪其執行權,把CPU儘快分配給另外的“更重要”進程,使得就緒隊列中的進程有機會響應它們用戶的IO事件。基於這兩種方式的調度算法如下:

  • 先來先服務(FCFS)調度算法:處於就緒態的進程按先後順序鏈入到就緒隊列中,而FCFS調度算法按就緒進程進入就緒隊列的先後次序選擇當前最先進入就緒隊列的進程來執行,直到此進程阻塞或結束,才進行下一次的進程選擇調度。FCFS調度算法採用的是不可搶佔的調度方式,一旦一個進程佔有處理機,就一直運行下去,直到該進程完成其工作,或因等待某一事件而不能繼續執行時,才釋放處理機。操作系統如果採用這種進程調度方式,則一個運行時間長且正在運行的進程會使很多晚到的且運行時間短的進程的等待時間過長。

  • 短作業優先(SJF)調度算法:其實目前作業的提法越來越少,我們姑且把“作業”用“進程”來替換,改稱爲短進程優先調度算法,此算法選擇就緒隊列中確切(或估計)運行時間最短的進程進入執行。它既可採用可搶佔調度方式,也可採用不可搶佔調度方式。可搶佔的短進程優先調度算法通常也叫做最短剩餘時間優先(Shortest Remaining Time First,SRTF)調度算法。短進程優先調度算法能有效地縮短進程的平均週轉時間,提高系統的吞吐量,但不利於長進程的運行。而且如果進程的運行時間是“估計”出來的話,會導致由於估計的運行時間不一定準確,而不能實際做到短作業優先。

  • 時間片輪轉(RR)調度算法:RR 調度算法與FCFS 調度算法在選擇進程上類似,但在調度的時機選擇上不同。RR調度算法定義了一個的時間單元,稱爲時間片(或時間量)。一個時間片通常在1~100 ms之間。當正在運行的進程用完了時間片後,即使此進程還要運行,操作系統也不讓它繼續運行,而是從就緒隊列依次選擇下一個處於就緒態的進程執行,而被剝奪CPU使用的進程返回到就緒隊列的末尾,等待再次被調度。時間片的大小可調整,如果時間片大到讓一個進程足以完成其全部工作,這種算法就退化爲FCFS調度算法;若時間片設置得很小,那麼處理機在進程之間的進程上下文切換工作過於頻繁,使得真正用於運行用戶程序的時間減少。時間片可以靜態設置好,也可根據系統當前負載狀況和運行情況動態調整,時間片大小的動態調整需要考慮就緒態進程個數、進程上下文切換開銷、系統吞吐量、系統響應時間等多方面因素。

  • 高響應比優先(Highest Response Ratio First,HRRF)調度算法:HRRF調度算法是介於先來先服務算法與最短進程優先算法之間的一種折中算法。先來先服務算法只考慮進程的等待時間而忽視了進程的執行時間,而最短進程優先調度算法只考慮用戶估計的進程的執行時間而忽視了就緒進程的等待時間。HRRF調度算法二者兼顧,既考慮進程等待時間,又考慮進程的執行時間,爲此定義了響應比(Rp)這個指標:

    Rp=(等待時間+預計執行時間)/執行時間=響應時間/執行時間
    上個表達式假設等待時間與預計執行時間之和等於響應時間。HRRF調度算法將選擇Rp最大值的進程執行,這樣既照顧了短進程又不使長進程的等待時間過長,改進了調度性能。但HRRF調度算法需要每次計算各各個進程的響應比Rp,這會帶來較大的時間開銷(特別是在就緒進程個數多的情況下)。

  • 多級反饋隊列(Multi-Level Feedback Queue)調度算法:在採用多級反饋隊列調度算法的執行邏輯流程如下:

  1. 設置多個就緒隊列,併爲各個隊列賦予不同的優先級。第一個隊列的優先級最高,第二隊次之,其餘隊列優先級依次降低。僅當第1~i-1個隊列均爲空時,操作系統調度器纔會調度第i個隊列中的進程運行。賦予各個隊列中進程執行時間片的大小也各不相同。在優先級越高的隊列中,每個進程的執行時間片就越小或越大(Linux-2.4內核就是採用這種方式)。
  2. 當一個就緒進程需要鏈入就緒隊列時,操作系統首先將它放入第一隊列的末尾,按FCFS的原則排隊等待調度。若輪到該進程執行且在一個時間片結束時尚未完成,則操作系統調度器便將該進程轉入第二隊列的末尾,再同樣按先來先服務原則等待調度執行。如此下去,當一個長進程從第一隊列降到最後一個隊列後,在最後一個隊列中,可使用FCFS或RR調度算法來運行處於此隊列中的進程。
  3. 如果處理機正在第i(i>1)隊列中爲某進程服務時,又有新進程進入第k(k<i)的隊列,則新進程將搶佔正在運行進程的處理機,即由調度程序把正在執行進程放回第i隊列末尾,重新將處理機分配給處於第k隊列的新進程。
    從MLFQ調度算法可以看出長進程無法長期佔用處理機,且系統的響應時間會縮短,吞吐量也不錯(前提是沒有頻繁的短進程)。所以MLFQ調度算法是一種合適不同類型應用特徵的綜合進程調度算法。
  • 最高優先級優先調度算法:進程的優先級用於表示進程的重要性及運行的優先性。一個進程的優先級可分爲兩種:靜態優先級和動態優先級。靜態優先級是在創建進程時確定的。一旦確定後,在整個進程運行期間不再改變。靜態優先級一般由用戶依據包括進程的類型、進程所使用的資源、進程的估計運行時間等因素來設置。一般而言,若進程需要的資源越多、估計運行的時間越長,則進程的優先級越低;反之,對於I/O bounded的進程可以把優先級設置得高。動態優先級是指在進程運行過程中,根據進程執行情況的變化來調整優先級。動態優先級一般根據進程佔有CPU時間的長短、進程等待CPU時間的長短等因素確定。佔有處理機的時間越長,則優先級越低,等待時間越長,優先級越高。那麼進程調度器將根據靜態優先級和動態優先級的總和現在優先級最高的就緒進程執行。

操作系統中爲了能夠讓每個進程都有機會運行,需要給每個進程分配一個時間片,當一個進程的時間片用完以後,操作系統的調度器就會讓當前進程放棄CPU,而選擇另外一個進程佔用CPU執行。爲了有效地支持進程調度所需的時間片,ucore設計並實現了一個timer(計時器)功能。這樣,通過timer就對基於時間事件的調度機制提供了基本支持。

算法詳解

先來先服務(FCFS)調度算法

先來先服務,是一種簡單的調度算法,它既適用於作業調度,也適用於進程調度。先來先服務算法是按照作業或進程到達的先後次序來進行調度。當作業調度中採用該算法時,每次調度都是從後備隊列中選擇一個最先進入該隊列中作業,將它調入內存,爲其創建進程、分配相應的資源,將該作業的進程放入就緒隊列。在進程調度中採用該算法時,每次調度是從就緒隊列中選擇一個最新進入該隊列的進程,並給他分配處理機。
先來先服務調度算法

進程(作業)名 到達時間(開始時間) 運行時間 結束時間 週轉時間
P1 0 9 9 20
P2 0.4 4 13 5
P3 1 1 14 1
P4 5.5 4 18 6
P5 7 2 20 2

先來先服務調度算法分析

時間/s 進程分析
0 P1到達,P1執行(期間執行9s)
0.4 P2到達,P2未執行,P1執行中(剩餘8.6s)
1 P3到達,P2未執行,P3未執行,P1(剩餘7.6s)
5.5 P4到達,P2未執行,P3未執行,P1(剩餘3.5s)
7 P5到達,P5未執行,P4未執行,P2未執行,P3未執行,P1(剩餘2s)
9 P1 結束 ,P2開始運行(期間執行4s)
13 P2 結束 ,P3開始運行(期間執行1s)
14 P3 結束 ,P4開始運行(期間執行4s)
18 P4 結束 ,P5開始運行(期間執行2s)
20 P4 結束

需要提出的一點,這個調度算法的調度過程是先找作業或者進程中最先到來的那一個,也就是說,這個是看到達時間的,到達時間越早,則最先進行調度,值得注意的是,此調度算法是服務完一個作業或進程後,再服務下一個作業或者進程。

優點:有利於長作業以及CPU繁忙的作業
缺點:不利於短作業以及I/O繁忙的作業

短作業優先(SJF)調度算法

短作業優先調度算法(Shortest Job First,SJF)或短進程調度算法(Shortest Process First,SPF)是指對短作業或短進程優先調度的算法 。這裏,作業或進程的長短是以作業或進程要求運行時間的長短來衡量的。

短作業(進程)優先調度算法

進程(作業)名 到達時間(開始時間) 運行時間 結束時間 週轉時間
P1 0 9 20 20
P2 0.4 4 5.4 5
P3 1 1 2 1
P4 5.5 4 11.5 6
P5 7 2 9 2

短作業(進程)優先調度算法分析

時間/s 進程分析
0 P1到達,P1執行
0.4 P2到達,P2執行,P1停(剩餘8.6s)
1 P3到達,P2停(剩餘3.4s),P3執行
2 P3 結束 ,P2執行
5.4 P2 結束 ,P1執行
5.5 P4到達,P1停(剩餘8.5s),P4運行
7 P5到達,P5運行,P4停(剩餘2.5s)
9 P5 結束 ,P4運行
11.5 P4 結束 ,P1運行
20 P1 結束

值得注意的時,短作業作業(進程)優先調度算法的確是按照運行時間的長短來衡量的。也就是誰的運行時間短,就先調度哪一個作業或進程。但是,這並不意味着是先把最先調度的那一個作業或進程運行完畢後調度其他的作業或進程,真正的調度過程是交叉進行的。總的順序還是按照到達的時間開始從最先到達的作業或進程開始進行調度,例如在本例中,P1到達後,立即執行,在P1調度完成之前,P2開始到達,立即執行(此時,P1停止),依次類推。上述表-短作業(進程)優先調度算法分析 相信說的已經足夠清楚。

優點:
   1.比FCFS改善平均週轉時間和平均帶權週轉時間,縮短作業的等待時間;
   2.提高系統的吞吐量;
缺點:
   1.對長作業非常不利,可能長時間得不到執行;
   2.未能依據作業的緊迫程度來劃分執行的優先級;
   3.難以準確估計作業(進程)的執行時間,從而影響調度性能。

時間片輪轉(RR)調度算法

時間片輪轉法(Round-Robin,RR算法)主要用於分時系統中的進程調度。
輪轉調度的實現原理爲系統把所有就緒進程按先入先出的原則排成一個隊列,新來的進程就緒隊列末尾,每當執行進程調度時,就緒隊列的隊首進程總是先被調度程序選中,在CPU上運行一個時間片後,系統的計時器發出時鐘中斷,調度程序進行調度,停止該進程的運行,並把它放入就緒隊列的末尾;隨後,進行進程切換,把CPU分給就緒隊列隊首進程,同樣讓它運行一個時間片,如此往復。輪轉法的原理圖如下。

以下表來說,其中時間片長度爲2ms。

進程(作業)名 到達時間 運行時間 開始時間 結束時間 週轉時間
A 0 10 0 30 30
B 0 6 2 22 20
C 0 2 4 6 2
D 0 4 6 16 10
E 0 8 8 28 20
   A   B     C   D    E    A     B   D    E    A     B   E    A     E   A
|————|————|————|————|————|————|————|————|————|————|————|————|————|————|————|————>
0    2    4    6    8   10    12   14   16   18   20   22   24   26   28   30

在這個算法中,可以把時間片輪轉法看作一個隊列,在隊列前面的先進行調度,但是和先來先服務算法不同是,各個進程是交叉進行的。而在先來先服務算法中則是 在調度完成一整個作業(進程) 後,再去調度其他的作業或者進程。從上面的時間軸可以看出,都在一個時間片大小的時間裏(這裏是2ms)進行調度,一直到一個進程調度完成,從上面的時間軸上還可以清楚的得出,每個進程的到達時間和開始時間也是不一樣的。這一點和前面的幾種算法也是稍微有區別。
在這裏插入圖片描述
當同時到達時
老進程先返回:


新進程先進入:
在這裏插入圖片描述
在這裏插入圖片描述

- 進 程 切 換 時 機 
      時 間 片 尚 未 用 完 , 進 程 己 完 成 
      時 間 片 己 經 用 完 , 進 程 未 完 成 
- 時 間 片 的 大 小 
         時 間 片 小 利 於 短 作 業 , 但 大 量 中 斷 和 切 換 會 增 
    加 系 統 開 銷 
         時 間 片 大 減 少 屮 斷 和 切 換 , 但 可 能 退 化 爲 FCFS , 
   無 法 滿 足 交 互 要 求 
         應 使 時 間 片 略 人 於 一 次 典 型 交 互 所 需 時 間 , 以 
   保 證 大 部 分 進 程 在 一 個 時 間 片 完 成 

高響應比優先(HRRF)調度算法

高響應比優先調度算法爲每一個作業引入一個動態優先級,即優先級是可以改變的,令他隨等待時間延長而增加,這將使長作業的優先級在等待期間不斷的增加,等到足夠的時間後,必然會有機會獲得處理機。該優先級變化規律爲:
    優先級 = (等待時間+要求服務時間)/要求服務時間
由於等待時間與服務時間之和就是系統對該作業的響應時間,故該優先級又相當於響應比Rp。優先級又可表示爲:

優先級 = (等待時間+要求服務時間)/要求服務時間 = 響應時間/要求服務時間
由上式可以看出

  • 如果作業的等待時間相同,則要求服務的時間愈短,其優先級愈高,因而類似於SJF算法,有利於短作業。
  • 當要求服務的的時間相同時,作業的優先級又取決於其等待時間,因而又類似於FCFS算法。
  • 對於長作業的優先級,可以隨等待時間的增加而增大,當其等待時間足夠長時,也可獲得處理機。
  • 在每次進行調度前,都需要進行響應比的計算,顯然會增加系統開銷。

案例: 某流水線負責生產汽車玩具的車輪,車蓋,車身和座位(兩人座),其中車身,車蓋,座位,車輪生產時間分別爲80s,50s,20s,10s。車輪,車身,座位生產原料相繼隔5 秒到達,車蓋原料在車輪生產後10s到達。只有一條流水線。爲了提高成品的生產效率,應該如何安排零件的生產順序?

解決步驟:

  1. 車輪最先到達,生產時間最短,所以先生產;車輪在流水線第一生產次序,5秒後到達的車身、座位原料等待5s
  2. 計算車身,座位優先權:

在這裏插入圖片描述
在這裏插入圖片描述

由於1.6025>1.25,車身生產次序排第二 此時車蓋原料等待80s,座位原料等待5+80=85s

  1. 計算車蓋、座位優先權:

高響應比優先調度算法5.png

高響應比優先調度算法6.png

由於5.25>2.6,座位生產次序排第三

  1. 車蓋在流水線第四生產次序

在這個解決方案中,生產車輪,車蓋,車身和座位就是四個任務,這四個任務到達先後順序不同,任務執行的時間也不相同。車輪因爲先到,就先被生產了,後面等待的車身和座位誰先執行,就由等待時間與生產時間兩個因素決定,原則上,等待時間相對生產時間越大的,優先被生產,這就體現在第2步中優先級的計算中,這種優先級的計算就體現了等待時間越長,優先級越高的思想。

多級反饋隊列(MFFP)調度算法

引 入 原 因
前 述 方 法 中 , OS 只 設 置 一 個 就 緒 隊 列 , 因 此 其 進 程 調 度
算 法 只 有 一 種 , 這 些 定 單 一 的 方 法 無 法 滿 足 多 川 屍 的
不 同 要 求
多級反饋隊列調度算法是目前操作系統調度算法中被公認的一種較好的調度算法。它可以滿足各種類型進程的需要,既能使高優先級的作業得到響應又能使短作業(進程)迅速完成。
解 決 辦 法
多 隊 列 調 度 算 法
就 緒 隊 列 不 止一個 , 同 類 或 同 性 質 的 進 程 在 同 一 隊 列 , 每
個 隊 列 都 可 以 單 獨 置 進 程 優 先 級 的 計 算 公 式
不 同 就 緒 隊 列 採 用 不 同 調 度 算 法 和 優 先 級
應 用 領 域
多 處 理 器 系 統 : 每 個 處 理 器 一 個 單 獨 的 就 緒 隊 列
多 用 戶 糸 統 : 根 不 同 用 戶 進 程 的 需 求 提 供 多 種 調 度 策略
在這裏插入圖片描述
· 新 進 程 進 入 內 存 後 , 按 照 FCFS 原 則 放 入 第 一 個 隊 列 末 尾 等 待 調 度 , 在 其 時 間 片 內 , 若 能 完 成 任 務 就 撤 離 系 統 , 若 不 能 完 成 就 將 其 優 先 權 降 低 一 個 檔 次 , 放 入 第 二 個 隊 列 的 隊 尾 等 待 調 度 , 如 此 循 環 , 直 到 降 入 第 n 個 ( 最 後 一 個 ) 隊 列 後 ,便 在 該 隊 列 中 以 時 間 片 輪 轉 方 式 運 行

  • 僅 當 第 一 隊 列 空 閒 時 , 調 度 程 序 才 會 調 度 第 二 隊 列 屮 的 進 程 運 行 , 僅 當 第 1 一 (i-l) 隊 列 均 空 閒時 , 才 調 度 第 i 隊 列 的 進 程 運 行
  • 在 採 用 搶 佔 式 調 度 算 法 的 模 型 中 , 若 第 i 隊 列 的 進 程 正 在 執 行 時 , 有 優 先 級 較 高 的 進 程 進 人 高 優 先 權 隊 列 , 則 新 進 程 搶 佔 當 前 進 程 的 CPU , 並 迫 使 其 回 到 第 i 隊 列 的 隊 尾 排 隊 等 待 下 次 調 度

在這裏插入圖片描述

最高優先級優先調度算法

分 類

  • 非 搶 佔 式 優 先 級 調 度 算 法
    當 前 最 高 優 先 級 進 程 一 直 運 行 至 結 東 或 主 動 放 棄
  • 搶 佔 式 優 先 級 調 度 算 法
    當 前 最 高 優 先 級 進 程 執 行 期 間 , 有 更 高 優 先 級 進
    程 到 達 就 要 讓 出 CPU
    優 先 級 調 度 算 法 的 關 鍵 在 於 使 用 靜 態 優 先 權
    還 是 動 態 優 先 權 以 及 如 何 確 定 進 程 的 優 先 權
    ( 優 先 數 )

優 先 權 類 型

  • 靜 態 優 先 權 : 進 程 創 建 時 確 定 且 在 進 程 生 命 期
    中 保 持 小 變 , 這 種 方 法 實 現 簡 單 、 開 銷 小 但 小 精
    確 , 可 能 會 產 生 低 優 先 權 進 程 飢 餓 , 用 於 低 要 求
    的 OS
  • 動 態 優 先 權 : 進 程 創 建 時 確 定 的 優 先 數 可 能 陌
    進 程 發 展 或 等 待 時 間 增 加 等 因 素 而 變 化 , 確 保 了
    更 優 良 的 調 度 性 能
    優先權
    確 定 優 先 權 的 依 據
  • 進 程 類 型 : 系 統 進 程 高 於 用 戶 進 程
  • 進 程 對 資 源 的 需 求 : 對 系 統 資 源 的 需 求 量 小 的
    進 程 可 以 優 先 運 行
  • 用 戶 要 求 : 根 據 用 戶 進 程 緊 迫 程 度 和 用 戶 所 付
    費 用 決 定 優 先 權
    在這裏插入圖片描述
    在這裏插入圖片描述

小結

在這裏插入圖片描述

w:花費等待的時間
e:到現在爲止執行所用的時間
s:進程所需要的總的服務時間

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章