高級操做系統——進程管理

1、進程描述符算法

進程控制塊PCB:是OS控制進程運行用的數據結構,是一個task_struct結構體。數據結構

PCB包括:進程標識信息(進程標識符PID等)、執行現場信息(CPU現場,進程切換時須要保存現場信息)、進程映像信息(進程地址空間,即進程在運行時代碼、數據、棧放在什麼位置,方便OS對地址空間進行管理)(現場與地址空間比較重要)、進程資源信息、信號信息。函數

對PCB,說其中幾個重要的字段:oop

mm_struct:有一個成員mm,標明瞭進程的地址空間;spa

thread:記錄了進程的現場,最後一個字段;線程

thread_info在4.4.6版本中改爲了stack,包括內核棧(即進程進入內核工做時須要的棧和用戶棧是分開的)和一些須要快速訪問的數據。3d

 

 

在4.4.6版本中,stack佔用了兩個頁面,即8k,大部分是放內核棧的,低端約10k存放快速訪問的信息。CPU若想訪問當前進程的快速訪問數據的話,只須要拿到當前的棧指針,即ESP寄存器的值,能夠推算出數據所在的位置來,所以在查找他的地址的時候,訪問速度能夠很快。這部分數據能夠看做是進程描述符的一部分,在空間上不是連續的,但相互之間有指針,能夠相互找獲得。指針

 

 

進程狀態轉換圖,可自行搜索。code

在4.4.6中,增長了被跟蹤和僵死撤銷狀態。blog

進程描述符是管理進程的重要數據結構,故他的組織方式很是重要。0號進程的描述符是由init_task這個變量所存儲的。從他出發,全部進程描述符構成了雙向鏈表。task_struct中包含一個成員,叫tasts,tasks類型是list_head類型,tasts自己是嵌入在進程描述符裏面的,知道tasks的地址,只要送減去620就能獲得進程描述符的首地址。在Linux中有不少這樣的技巧,即經過嵌入的地址,反推結構體的地址,進而找到結構體的其餘成員。

 

 

進程與線程關係

多個線程構成線程組,共享內存,不共享棧。

一個會話對應一個終端,在終端中敲一個命令至關於建立了一個進程組來執行。

下面進行演示,創建一個文件命名爲0.gdb,文件內容以下,直接運行

1 target remote localhost:1234
2 dir ~/aos/lab/busybox
3 add-symbol-file ~/aos/lab/busybox/busybox_unstripped 0x8048400
4 display $lx_current().pid
5 display $lx_current().comm
6 b start_kernel
7 b ls_main
8 c

 

 

執行含有下列代碼的文件

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <pthread.h>
 4 
 5 void loop(){
 6   while(1);
 7 }
 8 
 9 void *p1(){
10   printf("thread-1 starting\n");
11   loop();
12 }
13 
14 void *p2(){
15   printf("thread-2 starting\n");
16   loop();
17 }
18 
19 void main(){
20   int pid1, pid2;
21   pthread_t t1,t2;
22   void *thread_result;
23 
24   printf("main starting\n");
25 
26   if (!(pid1 = fork())){
27     printf("child-1 starting\n");
28     loop();
29     exit(0);
30   }
31 
32   if (!(pid2 = fork())){
33     printf("child-2 starting\n");
34     loop();
35     exit(0);
36   }
37 
38   pthread_create(&t1, NULL, p1, NULL);
39   pthread_create(&t2, NULL, p2, NULL);
40   
41   pthread_join(t1, &thread_result);
42   pthread_join(t2, &thread_result);
43 
44   int status;
45   waitpid(pid1, &status, 0);
46   waitpid(pid2, &status, 0);
47   printf("main exiting\n");
48   exit(0);
49 }

 能夠看到do-fork可執行文件建立了三個進程,97六、97七、978

 

 

 

97九、980是新建立的兩個線程

 

 再執行一次,能夠看到後臺運行了兩個

 

 新建立的三個進程是剛建立的

 

 fg %+序號將指定的進程放到前臺,ctrl+z放到後臺

 

 

用如下三條命令依次查看線程組、進程組和會話的leader

命令的意思是根據進程的描述符,找到線程組leader的描述符,裏面對應的字段就是要顯示的ID

p $lx_task_by_pid(977).group_leader->pids[0].pid->numbers.nr
p $lx_task_by_pid(977).group_leader->pids[1].pid->numbers.nr
p $lx_task_by_pid(977).group_leader->pids[2].pid->numbers.nr

987和988是984建立的,他們處於同一個線程組,leader是976

 

2、進程調度算法

每一個進程屬於某一個調度器類,每一個調度器類都有一個進程隊列,不一樣的隊列有不一樣的調度算法。

先調度硬實時的,軟實時次之,普通進程最後。

普通進程使用CFS(徹底公平)調度算法:

虛擬時鐘,調度器老是選時鐘最小的那個進程來執行。

優先級高的進程時鐘增加得慢。

全部可運行的進程被放在一個紅黑樹中。

 

下面進行演示:

再次運行0.gdb,在終端輸入ls,使其被捕獲

 

 

 

創建文件demo-2-2.gdb,內容以下

 1 break __schedule//進程調度的時候執行這個函數
 2 
 3 break __switch_to//調度時若是切換進程就會調用這個函數
 4   commands
 5     printf "next_p->pid: %d\n", next_p->pid
 6     printf "next_p->se.vruntime: "
 7     print  next_p->se.vruntime
 8   end
 9 
10 break enqueue_task_fair//若是有新進程要進入到CFS隊列時,執行這個函數
11   commands
12     printf "p->pid: %d\n", p->pid
13     printf "p->se.vruntime: "
14     print  p->se.vruntime
15   end
16 
17 display   $lx_current().state//顯示當前進程的狀態
18 display   $lx_current().se.vruntime
19 display   $lx_per_cpu("runqueues").nr_running//CPU裏面有多少個進程在運行
20 
21 display ((struct sched_entity *)((void *)$lx_per_cpu("runqueues").cfs.rb_leftmost - 0x8))->vruntime//CFS隊列裏最左邊的節點,即虛擬時鐘最小的信息
22 display ((struct task_struct *)((void *)$lx_per_cpu("runqueues").cfs.rb_leftmost - 0x4c))->pid

 

 

  由上圖能夠看到,當前正在運行的是975號進程,當前的虛擬時鐘能夠從runtime那裏看到,state=0表示其當前的狀態是就緒的或正在運行,樹最左邊目前尚未進程。因爲enqueue_task_fair函數的做用是往進程隊列裏面加入新進程,如今已經有一個,能夠看到,要加的是7號進程,下面一行的虛擬時鐘是個負值,如今還暫時看不到,繼續執行。

 

 能夠看到7號進程的虛擬時鐘小於975號的,下次若是要調度,應該選7號。即將建立的是3號進程。

 

 由上圖,975號進程的虛擬時鐘增長了,在這兩個斷點時間,存在中斷,這才致使了時鐘的增長。運行有必定的隨機性,虛擬機在虛擬的時候有必定的隨機性。繼續

 

 運行了切換函數,下一步要切換7號進程。繼續

 

 7號時鐘的進程的時鐘比以前也增長了,須要注意當前正在運行的進程不放在樹裏面,但放在了隊列裏面,隊列裏面進程就是樹裏面的進程加當前進程。繼續若干次

 

 下一步要建立的是4號進程。

 

除了普通進程有隊列以外,其餘的硬實時和軟實時都有各自的隊列。

紅黑樹的某個節點能夠是另一個樹,共用一個時鐘。

 

3、進程調度的時機

內核程序的入口,系統調用總控函數,異常處理函數,中斷處理函數、內核線程主函數,用bt查看棧頂層,根據函數的種類來肯定是哪一種內核調用。

相關文章
相關標籤/搜索