Exercise1 源代碼閱讀
1.基本頭文件:types.h param.h memlayout.h defs.h x86.h asm.h mmu.h elf.hhtml
- types.h:僅僅是定義uint, ushort, uchar pde_t別名;
typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned char uchar;
typedef uint pde_t;
- parame.h 利用宏定義了進程最大數量,進程的內核棧大小,CPU的最大數量,進程能打開的文件描述符數等;
#define NPROC 64 // maximum number of processes
#define KSTACKSIZE 4096 // size of per-process kernel stack
#define NCPU 8 // maximum number of CPUs
#define NOFILE 16 // open files per process
#define NFILE 100 // open files per system
#define NBUF 10 // size of disk block cache
#define NINODE 50 // maximum number of active i-nodes
#define NDEV 10 // maximum major device number
#define ROOTDEV 1 // device number of file system root disk
#define MAXARG 32 // max exec arguments
#define LOGSIZE 10 // max data sectors in on-disk log
- memlayout.h:利用宏定義內存空間範圍,地址和偏移的轉換等;
- defs.h:內部包含了許多前向聲明,同時聲明瞭許多全局函數,這些全局函數的實如今具體使用的模塊定義;
- x86.h:定義了許多C代碼與彙編指令交互的內斂函數,同時定義了從用戶態陷入內核態的數據結構struct trapframe;
- asm.h:彙編宏定義;
- mmu.h:內存管理單元,進程地址空間詳細數據結構;
- elf.h:聲明瞭elf格式的目標文件有關的數據結構。
2.進程線程部分:vm.c proc.h proc.c swtch.S kalloc.c以及相關其餘文件代碼node
- vm.c:內存管理接口,好比頁表申請,頁表釋放,頁目錄的轉換,cpu與進程的綁定等;
- proc.h:聲明瞭cpu、進程、進程上下文等數據結構;
swtch.S:內部是上下文切換的彙編指令,保存換出進程的現場,加載換入進程的現場;
- kalloc.c:物理內存的申請和釋放。主要接口:void kinit1(void * vstart, void * vend), kinit2(void * vstart, void * vend), char * kalloc(void), void kfree(char * v)。
Exercise2 帶着問題閱讀
1.什麼是進程?什麼是線程?操做系統的資源分配單位和調度單位分別是什麼?XV6 中的進程和線程分別是什麼,都實現了嗎?linux
- 進程是在多道程序系統出現之後,爲了描述系統內部各做業的活動規律而引進的概念。進程有3個基本狀態:運行狀態、就緒狀態和等待狀態;進程是具備獨立功能的程序關於某個數據集合上的一次運行活動;
- 線程是輕量級的進程,線程是進程內的一個相對獨立的可執行的單元,若把進程稱爲任務的話,那麼線程則是應用中的一個子任務的執行;
- 操做系統的資源分配的單位是進程,處理機調度的單位是線程;
- xv6操做系統實現了一個基於進程 (沒有實現線程) 的簡單進程管理機制。XV6中進程和CPU的數據結構見proc.h。
// proc.h
// 上下文切換現場
struct context {
uint edi;
uint esi;
uint ebx;
uint ebp;
uint eip;
};
// 枚舉進程狀態
enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
// Per-process state
struct proc {
uint sz; // Size of process memory (bytes)
pde_t* pgdir; // Page table
char *kstack; // Bottom of kernel stack for this process
enum procstate state; // Process state
volatile int pid; // Process ID
struct proc *parent; // Parent process
struct trapframe *tf; // Trap frame for current syscall
struct context *context; // swtch() here to run process
void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
};
2.進程管理的數據結構是什麼?在 Windows,Linux,XV6 中分別叫什麼名字?其中包含哪些內容?操做系統是如何進行管理進程管理數據結構的?它們是如何初始化的?算法
- 進程管理的數據結構是進程控制塊 (PCB );
- Linux下爲/include/linux/sched.h內部的struct task_struct,其中包括管理進程所需的各類信息。建立一個新進程時,系統在內存中申請一個空的task_struct ,並填入所需信息。同時將指向該結構的指針填入到task[]數組中。當前處於運行狀態進程的PCB用指針數組current_set[]來指出。這是由於 Linux 支持多處理機系統,系統內可能存在多個同時運行的進程,故 current_set定義成指針數組;
- Windows下用EPROCESS(執行體進程對象)表示 , PCB 也稱爲內核進程塊KPROCESS(KPROCESS即內核進程對象 ),EPOCESS和KPROCESS位於內核空間 ;
- XV6下在proc .h內聲明,包括進程 ID ,進程狀態 ,父進程,context,cpu記錄了內存地址和棧指針等。XV6中經過get_raw_proc()對進程進行控制管理,get_raw_proc()方法能夠建立一個新的進程並對進程進行初始化分配進程頁表和內核堆棧。
3.XV6中進程有哪些狀態? 請畫出XV6 的進程狀態轉化圖。在Linux中,進程的狀態分別包括哪些? 你認爲操做系統的設計者爲何會有這樣的設計思路?數組

- Linux 的進程狀態可分爲R (TASK_RUNNING),可執行狀態,S (TASK_INTERRUPTIBLE),可中斷的睡眠狀態,D(TASK_UNINTERRUPTIBLE)不可中斷的睡眠狀態,T(TASK_STOPPED or TASK_TRACED)暫停狀態或跟蹤狀態,Z(TASK_DEAD - EXIT_ZOMBIE)退出狀態但資源未回收,稱爲殭屍進程,X (TASK_DEAD - EXIT_DEAD)退出狀態,進程即將被銷燬。
- 設計這這樣設計的目的,是爲了cpu可以根據進程的狀態進行更好的調度,提升cpu的利用率,適應io密集型和cpu密集型的進程,避免cpu的浪費。
4.如何啓動多進程(建立子進程)? 如何調度多進程,調度算法有哪些? 操做系統爲什麼要限制一個CPU最大支持的進程數? XV6中的最大進程數是多少?如何執行進程的切換? 什麼是進程上下文? 多進程和多CPU有什麼關係?bash
- 父進程能夠利用fork()函數建立多個子進程。首先,爲每一個新建的子進程分配一個空閒的proc結構並賦予子進程惟一標識pid;其次,以一次一頁的方式複製父進程地址空間(採用cow寫時複製),得到子進程繼承的共享資源的指針;最後將子進程加入就緒隊列,對子進程返回標識符0,向父進程返回子進程pid;
- cpu使用規定的調度算法從就緒隊列選擇一個進程執行,經常使用調度算法:時間片輪轉調度、先來先服務、短做業優先調度策略、基於優先級的可搶佔調度策略、基於優先級的不可搶佔式調度策略、最短剩餘時間優先算法、高響應比優先算法、多級反饋隊列算法等;
- 一是內存空間有限,若是讀入的進程數量過多,勢必會吃掉大量的內存空間,而cpu在調度過程當中也會從棧或堆上申請空間,若是申請失敗則沒法繼續運行。二是增長了缺頁中斷的可能性,會致使cpu不斷的執行頁面換入換出,使得大部分時間浪費在無心義的事情上;
- XV6的最大進程數見param.h文件中的#define NPROC 64,最大64;
- 進程切換是一個進程讓出處理器,由另外一個進程佔用處理器的過程。進程的切換是由進程狀態的變化引發的,而進程狀態的變化又與出現的事件有關。當有事件(中斷或異常)發生時,當前運行進程暫停,保存當前進程的現場,而後根據調度算法從就緒隊列選一個進程換入CPU,同時加載換入進程的現場進行執行;
- 進程的上下文包括當前進程的程序計數 器PC和當前運行的CPU中各個寄存器的內容。當進程切換和發生中斷的時候這些信息要保存下來以便於下次運行時使用;
- 同一時刻每一個cpu上只能有一個進程被執行,且同一時刻一個進程只能被一個cpu調度,同一時刻多個cpu能夠同時調度不一樣的進程,同一時間段內每一個cpu能夠調度多個進程。
5.內核態進程是什麼? 用戶態進程是什麼? 它們有什麼區別?數據結構
- 多數系統將處理器工做狀態劃分爲內核態和用戶態。前者通常指操做系統管理程序運行的狀態,具備較高的特權級別,又稱爲特權態、系統態或管態;後者通常指用戶程序運行時的狀態;具備較低的特權級別,又稱爲普通態、目態。區分了用戶態和內核態就是限定用戶什麼操做能夠作,什麼操做不能讓用戶直接作。若是遇到不能讓用戶直接作的操做,用戶就必須請求操做系統作系統調用,這樣操做系統就會進入內核態進行系統操做。內核態的進程就是系統進入內核態以後進行系統操做所產生的進程;
- 用戶態進程是用戶經過請求操做而產生的進程;
- 區別: 運行在不一樣的系統狀態,用戶態進程執行在用戶態,內核態進程執行在內核態;進入方式不一樣,用戶態進程可直接進入,內核態必須經過運行系統調用命令;返回方式不一樣,用戶態進程直接返回,內核態進程有從新調度過程;內核態進程優先級要高於用戶態進程,而且內核態進程特權級別最高,它能夠執行系統級別的代碼。
6.進程在內存中是如何佈局的? 進程的堆和棧有什麼區別?併發
- 內存分爲內核空間和用戶空間,內核空間通常運行操做系統程序,而用戶空間通常運行用戶程序。主要目的是對系統程序進行包含。進程在內存中包含堆、棧、數據段、代碼段。代碼段 : 保存程序的執行碼,在進程併發時,代碼段是共享的且只讀的,在存儲器中只需有一個副本。數據段 : 此段又稱爲初始化數據段,它包含了程序中已初始化的全局變量、全局靜態變量、局部靜態變量。
- 棧 : 程序執行前靜態分配的內存空間,棧的大小可在編譯時指定,Linux環境下默認爲 8M。棧段是存放程序執行時局部變量、函數調用信息、中斷現場保留信息的空間。程序執行時,CPU堆棧段指針會在棧頂根據執行狀況進行上下移動。
- 堆 : 程序執行時, 按照程序須要動態分配的內存空間,使用malloc、 calloc、realloc函數分配的空間都在堆上分配。

參考文獻
[1] xv6進程與內存管理-CSDN
[2] linux進程地址空間-博客園
[3] xv6進程線程-百度文庫
[4] 操做系統-進程線程模型課件函數