20145215《資訊安全系統設計基礎》第十四周學習總結
教材學習内容總結
- 虛拟存儲器的三個重要能力:
- 它将主存看成是一個存儲在磁盤上的位址空間的高速緩存,在主存中隻儲存活動區域,并根據需要在磁盤和主存之間來回傳送資料,通過這種方式,高效的使用了主存。
- 它為每個程序提供了一緻的位址空間,進而簡化了存儲器管理。
- 它保護了每個程序的位址空間不被其他程序破壞。
- 程式員需要了解虛拟存儲器的三個原因:
- 虛拟存儲器是中心的:它是硬體異常、硬體位址翻譯、主存、磁盤檔案和核心軟體的互動中心;
- 虛拟存儲器是強大的:它可以建立和銷毀存儲器片、可以映射存儲器片映射到磁盤某個部分等等;
- 虛拟存儲器若操作不當則十分危險。
實體和虛拟尋址
- 計算機系統的主存被組織成一個由M個連續的位元組大小的單元組成的數組,每位元組都有一個唯一的實體位址(PA)。CPU根據實體位址通路存儲器的方式是實體尋址。
20145215《資訊安全系統設計基礎》第十四周學習總結 - 使用虛拟尋址時,CPU通過生成一個虛拟位址VA來通路主存,這個虛拟位址在被送到存儲器之前先轉換成适當的實體位址,位址翻譯通過CPU晶片上的存儲器管理單元完成。
20145215《資訊安全系統設計基礎》第十四周學習總結
位址空間
- 位址空間是一個非負整數位址的有序集合:{0,1,2,……}
- 線性位址空間:位址空間中的整數是連續的。
- 虛拟位址空間:CPU從一個有 N=2^n 個位址的位址空間中生成虛拟位址,這個位址空間成為稱為虛拟位址空間。
- 位址空間的大小:由表示最大位址所需要的位數來描述。
- 實體位址空間:與系統中的實體存儲器的M個位元組相對應。
- 虛拟存儲器的基本思想:主存中的每個位元組都有一個選自虛拟位址空間的虛拟位址和一個選自實體位址空間的實體位址。
虛拟存儲器作為緩存的工具
- 虛拟存儲器——虛拟頁(VP),每個虛拟頁大小為P=2^p位元組。
- 實體存儲器——實體頁(PP),也叫頁幀,大小也為P位元組。
- 任意時刻,虛拟頁面的集合都被分為三個不相交的子集:
- 未配置設定的:VM系統還沒配置設定(建立)的頁,不占用任何磁盤空間。
- 緩存的:目前緩存在實體存儲器中的已配置設定頁。
- 未緩存的:沒有緩存在實體存儲器中的已配置設定頁。
DRAM緩存的組織結構
- 不命中處罰很大
- 是全相聯的——任何虛拟頁都可以放在任何的實體頁中
- 替換算法精密
- 總是使用寫回而不是直寫
頁表
- 頁表:是一個資料結構,存放在實體存儲器中,将虛拟頁映射到實體頁,就是一個頁表條目的數組。
- 頁表就是一個頁表條目PTE的數組。
- PTE:由一個有效位和一個n位位址字段組成的,表明了該虛拟頁是否被緩存在DRAM中。
- 頁表的組成:有效位+n位位址字段
- 如果設定了有效位:位址字段表示DRAM中相應的實體頁的起始位置,這個實體頁中緩存了該虛拟頁。
- 如果沒有設定有效位:
- 空位址:表示該虛拟頁未被配置設定
- 不是空位址:這個位址指向該虛拟頁在磁盤上的起始位置。
頁命中
- 當CPU讀取一個字的時候,位址翻譯硬體将虛拟位址作為一個索引來定位PTE,并從存儲器中讀取它。
缺頁
- 缺頁:就是指DRAM緩存不命中。
- 缺頁異常:會調用核心中的缺頁異常處理程式,選擇一個犧牲頁。
- 頁:虛拟存儲器的習慣說法,就是塊
- 交換=頁面排程:磁盤和存儲器之間傳送頁的活動
- 按需頁面排程:直到發生不命中時才換入頁面的政策,所有現代系統都使用這個。
又是局部性救了我們
- 局部性原則保證了在任意時刻,程式将往往在一個較小的活動頁面集合上工作,這個集合叫做工作集/常駐集。
- 是以隻要程式有良好的時間局部性,虛拟存儲器系統就能工作的相當好。
- 颠簸:工作集大小超出了實體存儲器的大小。
虛拟存儲器作為存儲器管理的工具
- 作業系統為每個程序提供了一個獨立的頁表,也就是一個獨立的虛拟位址空間。
- 多個虛拟頁面可以映射到同一個共享實體頁面上。
- 存儲器映射:将一組連續的虛拟頁映射到任意一個檔案中的任意位置的表示法。
- 按需頁面排程和獨立的虛拟位址空間的結合簡化了連結和加載、代碼和資料共享,以及應用程式的存儲器配置設定。
- 簡化連結:獨立的位址空間允許每個程序的存儲器映像使用相同的基本格式,而不管代碼和資料實際存放在實體存儲器的何處。
- 簡化加載:虛拟存儲器使得容易想存儲器中加載可執行檔案和共享檔案對象。
- 簡化共享:獨立位址空間為作業系統提供了一個管理使用者程序和作業系統自身之間共享的一緻機制。
- 簡化存儲器配置設定:虛拟存儲器為向使用者程序提供一個簡單的配置設定額外存儲器的機制。
虛拟存儲器作為存儲器保護的工具
- 通過在PTE上添加一些額外的許可來控制對一個虛拟頁面的内容通路。
- PTE的三個許可位:
- SUP:表示程序是否必須運作在核心模式下才能通路該頁
- READ:讀權限
- WRITE:寫權限
位址翻譯
- 位址翻譯:一個N元素的虛拟位址空間(VAS)中的元素和一個M元素的實體位址空間(PAS)之間的映射。
- MAP: VAS → PAS ∪ ∅
- MAP = A' ,如果虛拟位址A處的資料在PAS的實體位址A'處
- MAP = ∅ ,如果虛拟位址A處的資料不在實體存儲器中
- CPU中的一個控制寄存器頁表基址寄存器指向目前頁表,n位的虛拟位址包含兩個部分:一個p位的虛拟頁面偏移(VPO) 和一個(n-p)位的虛拟頁号,頁表條目中的實體頁頁号和虛拟位址中的VPO串聯起來,就得到了相應的實體位址。
20145215《資訊安全系統設計基礎》第十四周學習總結 - 當頁面命中時,CPU硬體執行步驟
- 處理器生成虛拟位址,傳給MMU
- MMU生成PTE位址,并從高速緩存/主存請求得到他
- 高速緩存/主存向MMU傳回PTE
- MMU構造實體位址,并把它傳給高速緩存/主存
- 高速緩存/主存傳回所請求的資料給處理器。
- 處理缺頁時,CPU硬體執行步驟
- PTE中有效位為0,觸發缺頁異常
- 确定犧牲頁
- 調入新頁面,更新PTE
- 傳回原來的程序,再次執行導緻缺頁的指令,會命中
結合高速緩存和虛拟存儲器
- 在既使用SRAM高速緩存又使用虛拟存儲器的系統中,大多數系統選擇實體尋址。
- 兩者結合的主要思路是位址翻譯發生在高速緩存之前。
- 頁表目錄可以緩存,就像其他的資料字一樣。
利用TLB加速位址翻譯
- TLB:翻譯後備緩沖器,是一個小的、虛拟存儲的緩存,其中每一行都儲存着一個由單個PTE組成的塊
- 步驟:
- CPU産生一個虛拟位址
- MMU從TLB中取出相應的PTE
- MMU将這個虛拟位址翻譯成一個實體位址,并且将它發送到高速緩存/主存
- 高速緩存/主存将所請求的資料字傳回給CPU
多級頁表
- 多級頁表——采用層次結構,用來壓縮頁表。
- 以兩層頁表層次結構為例,好處是:
- 如果一級頁表中的一個PTE是空的,那麼相應的二級頁表就根本不會存在;
- 隻有一級頁表才需要總是在主存中,虛拟存儲器系統可以在需要時建立、頁面調入或調出二級頁表,隻有最經常使用的二級頁表才緩存在主存中。
案例研究:Intel Core i7/Linux存儲器系統
core i7位址翻譯
- PTE的三個權限位:
- R/W位:确定内容是讀寫還是隻讀
- U/S位:确定是否能在使用者模式通路該頁
- XD位:禁止執行位,64位系統中引入,可以用來禁止從某些存儲器頁取指令
- 缺頁處理程式涉及到的位:
- A位:引用位,實作頁替換算法
- D位:髒位,告訴是否必須寫回犧牲頁
Linux虛拟存儲器系統
- linux為每個程序維持了一個單獨的虛拟位址空間,其中,核心虛拟存儲器位于使用者棧之上;
- 核心虛拟存儲器包含核心中的代碼和資料結構,還有一些被映射到一組連續的實體頁面(主要是便捷地通路特定位置,比如執行I/O操作的時候需要的位置)
- linux将虛拟存儲器組織成一些區域(也叫做段)的集合。一個區域就是已經存在的(已配置設定的)虛拟存儲器的連續片;
- 允許虛拟位址空間有間隙;核心不用記錄那些不存在的頁,這樣的頁也不用占用存儲器;
- 一個具體區域的區域結構:
- vm _start:指向這個區域的起始處;
- vm _end:指向這個區域的結束處;
- vm _prot:描述這個區域内所包含的所有頁的讀寫許可權限;
- vm _fags:描述這個區域内的頁面是與其他程序共享的,還是這個程序私有的,等等;
- vm _next:指向連結清單的下一個結構。
- Linux缺頁異常處理:
20145215《資訊安全系統設計基礎》第十四周學習總結
存儲器映射
- 存儲器映射:Linux通過将一個虛拟存儲器區域與一個磁盤上的對象關聯起來,以初始化這個虛拟存儲器區域的内容的過程。
- 映射對象:
- Unix檔案系統中的普通檔案
- 匿名檔案(全都是二進制0)
- 一旦一個虛拟頁面被初始化了,它就在一個由核心維護的專門的交換檔案之間換來換去。交換檔案也叫交換空間,或交換區域。
再看共享對象
- 共享對象
- 共享對象對于所有把它映射到自己的虛拟存儲器程序來說都是可見的。
- 即使映射到多個共享區域,實體存儲器中也隻需要存放共享對象的一個拷貝。
- 私有對象
- 私有對象運用的技術:寫時拷貝
- 在實體存儲器中隻儲存有私有對象的一份拷貝
再看fork函數
- 當fork函數被目前程序調用時,核心為新程序建立各種資料結構,并配置設定給它一個唯一的PID。為了給這個新程序建立虛拟存儲器,它建立了目前程序的mm_struct、區域結構和頁表的原樣拷貝。它将兩個程序中的每個頁面都為标記隻讀,并将兩個程序中的每個區域結構都标記為私有的寫時拷貝。
- 當fork在新程序中傳回時,新程序現在的虛拟存儲器剛好和調用fork時存在的虛拟存儲器相同。當這兩個程序中的任一個後來進行寫操作時,寫時拷貝機制就會建立新頁面,是以,也就為每個程序保持了私有位址空間的抽象概念。
再看execve函數
- 使用execve函數将a.out程式加載到存儲器的過程
Execve("a.out",NULL,NULL);
- 加載并運作所需要的步驟如下:
- 删除已存在的使用者區域。
- 映射私有區域。
- 映射共享區域。
- 設定程式計數器。
使用mmap函數的使用者級存儲器映射
- Unix程序可以使用mmap函數來建立新的虛拟存儲器區域,并将對象映射到這些區域當中
void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offest);
若成功則為指向映射區域的指針,若出錯則為MAP_FAILDE(-1)
- munmap函數删除虛拟存儲器的區域。
int munmap(void *start,size_t length);
若成功則傳回0,若失敗則傳回-1.
動态存儲器配置設定
- 當運作時需要額外虛拟存儲器時,使用動态存儲器配置設定器維護一個程序的虛拟存儲器區域。
- 配置設定器有兩種風格:
- 顯示配置設定器:要求應用顯式地釋放任何已經配置設定的塊。
- 隐式配置設定器:要求配置設定器檢測一個已配置設定塊何時不再被程式所使用,就釋放這個塊。也叫做垃圾收集器。
malloc和free函數
- 系統調用malloc函數,從堆中配置設定塊:
#include <stdlib.h>
void *malloc(size_t size);
成功傳回指針,指向大小至少為size位元組的存儲器塊,失敗傳回NULL
- 系統調用free函數來釋放已配置設定的堆塊:
#include <stdlib.h>
void free(void *ptr);
無傳回值
ptr參數必須指向一個從malloc、calloc或者reallov獲得的已配置設定塊的起始位置。
- 使用動态存儲器配置設定原因:經常直到程式實際運作時,才知道某些資料結構的大小。
配置設定器的要求和目标
- 顯示配置設定器的要求:
- 處理任意請求序列
- 立即響應請求
- 隻使用堆
- 對齊塊
- 不修改已配置設定的塊
- 目标:
- 最大化吞吐率:最大化存儲器使用率——峰值使用率最大化
- 吞吐率:每個機關時間裡完成的請求數
碎片
- 碎片:雖然有未使用的存儲器,但是不能用來滿足配置設定請求。
- 内部碎片:發生在一個已配置設定塊比有效載荷大的時候,易于量化。
- 外部碎片:發生在當空閑存儲器合計起來足夠滿足一個配置設定請求,但是沒有一個單獨的空間塊足以處理這個請求時發生。難以量化,不可預測。
隐式空閑連結清單
- 堆塊的格式:由一個字的頭部,有效荷載,和可能的額外填充組成。
- 将堆組織成一個連續的已配置設定塊和空閑塊的序列:
- 空閑塊通過頭部中的大小字段隐含地連接配接着,配置設定器可以通過周遊堆中所有的塊,進而間接地周遊整個空閑塊的集合。
- 需要:特殊标記的結束塊。
- 系統對齊要求和配置設定器對塊格式的選擇會對配置設定器上的最小塊大小有強制的要求。
放置已配置設定的塊
- 配置設定方式有:
- 首次适配:從頭開始搜尋空閑連結清單,選擇第一個合适的空閑塊
- 下一次适配:從上一次搜尋的結束位置開始搜尋
- 最佳适配:檢索每個空閑塊,選擇适合所需請求大小的最小空閑塊
申請額外的堆存儲器
- sbrk函數
#include <unistd.h>
vid *sbrk(intptr_t incr);
成功則傳回舊的brk指針,出錯為-1
- 通過将核心的brk指針增加incr來擴充和收縮堆。
合并空閑塊
- 合并是針對于假碎片問題的,任何實際的配置設定器都必須合并相鄰的空閑塊。
- 兩種政策:
- 立即合并
- 推遲合并
垃圾收集
- 垃圾收集器是一種動态存儲配置設定器。,自動釋放程式已經不再需要的已配置設定塊(垃圾)。
基本知識
- 垃圾收集器将存儲器視為一張有向可達圖,圖的節點被配置設定為一組根節點和一組堆節點。當存在一條從任意根節點出發到并到達P的有向路徑時,就稱節點P是可達的。
Mark&Sweep垃圾收集器
- Mark&Sweep垃圾收集器由标記階段和清除階段組成,标記階段标記出根節點所有可達的和已配置設定的後繼,清除階段釋放每個未被标記的已配置設定塊。
- 在對Mark&Sweep的描述中使用下列函數:
- ptr isPtr(ptr p):如果p指向一個已配置設定塊中的某個字,那麼就傳回一個指向這個塊起始位置的指針b,否則傳回NULL。
- int blockMarked(ptr b):如果已經标記了塊b,就傳回true。
- int blockAllocated(ptr b):如果塊b是已配置設定的,就傳回true。
- void markBlock(ptr b):标記塊b。
- int length(ptr b):傳回塊b的以字為機關的長度(不包括頭部)。
- void unmarkBlock(ptr b):将塊b的狀态由已标記的改為未标記的。
- ptr nextBlock(ptr b):傳回堆中塊b的後繼。
C程式中常見的與存儲器有關的錯誤
間接引用壞指針
- 在程序的虛拟位址空間中有較大的洞,沒有映射到任何有意義的資料,如果試圖引用一個指向這些洞的指針,作業系統就會以段異常來終止程式。
- 典型的錯誤是:
,沒有加&符号scanf("%d",val);
讀未初始化的存儲器
- 雖然bass存儲器位置總是被加載器初始化為0,但對于堆存儲器卻并不是這樣的。
- 常見的錯誤就是假設堆存儲器被初始化為0.
允許棧緩沖區溢出
- 如果一個程式不檢查輸入串的大小就寫入棧中的目标緩沖區,程式就會出現緩沖區溢出錯誤。
假設指針和指向他們的對象大小是相同的。
- 一種常見的錯誤是假設指向對象的指針和他們所指向的對象是大小相同的。
造成錯位錯誤。
- 一種很常見的覆寫錯誤來源
引用指針,而不是他所指向的對象。
- 注意C的優先級和結合性
誤解指針運算
- 忘記了指針的算術操作是以它們指向的對象的大小為機關來進行,而這種大小機關不一定是位元組。
引用不存在的變量
- 了解棧的規則,有時會引用不再合法的本地變量。
引用空閑堆塊中的資料
- 一個相似的錯誤是引用已經被釋放了的堆塊中的資料。
引起存儲器洩露
- 當不小心忘記釋放已配置設定塊,而在堆裡建立了垃圾時,就會引起存儲器洩露。
實踐
實作一個簡單的配置設定器
- 源代碼連結
主要代碼
- mm_init函數:
int mm_init(void) //初始化,成功傳回0,失敗傳回
{
mem_init();
if ( (heap_listp = mem_sbrk(4 * WSIZE)) == (void *)-1)
return -1;
PUT(heap_listp, 0);
PUT(heap_listp + WSIZE, PACK(8, 1)); //序言塊頭部
PUT(heap_listp + 2*WSIZE, PACK(8, 1)); //序言塊尾部
PUT(heap_listp + 3*WSIZE, PACK(0, 1)); //結尾塊
heap_listp += 2*WSIZE;
if (extend_heap(CHUNKSIZE/WSIZE) == NULL)
return -1;
return 0;
}
- mm_free函數:
void mm_free(void *bp) //釋放bp指向塊的記憶體
{
size_t size = GET_SIZE(HDRP(bp));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
coalesce(bp); //并合前後塊
}
- mm_malloc函數:
void *mm_malloc(size_t size) //配置設定size位元組大小的塊,傳回指向塊的指針
{
size_t asize; //調整過的size
size_t extendsize;
void *bp;
if (size == 0)
return NULL;
if (size < DSIZE)
asize = 2 * DSIZE;
else
asize = DSIZE * ((size + (DSIZE) + (DSIZE - 1)) / DSIZE);
if ( (bp = find_fit(asize)) != NULL)
{
place(bp, asize);
return bp;
}
extendsize = MAX(asize, CHUNKSIZE);
if ( (bp = extend_heap(extendsize/WSIZE)) == NULL )
return NULL;
place(bp, asize);
return bp;
}
- extend_heap函數:
//工具函數定義
static void *extend_heap(size_t words) //拓展堆的可用空間,傳回原堆頂位址(mem_brk),失敗傳回NULL
{
char *bp;
size_t size;
size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE; //保持雙字對齊
if ((long)(bp = mem_sbrk(size)) == -1)
return NULL;
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1));
return (void *)bp;
}
- coalesce函數:
static void *coalesce(void *bp) //并合bp指向塊的前後塊,傳回并合後的塊指針
{
size_t prev_alloc = GET_ALLOC(HDRP(PREV_BLKP(bp))); //上一塊是否配置設定
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); //下一塊是否配置設定
size_t size;
if (prev_alloc && next_alloc)
{
return bp;
}
else if (prev_alloc && !next_alloc)
{
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size,0));
}
else if (!prev_alloc && next_alloc)
{
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
else
{
size += GET_SIZE(HDRP(PREV_BLKP(bp))) +
GET_SIZE(FTRP(NEXT_BLKP(bp)));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
return bp;
}
- find_fit函數:
static void *find_fit(size_t size) //尋找第一個空間大于size的空閑塊,傳回其位址,未找到時,傳回NULL
{
void *bp;
for (bp = heap_listp; GET_SIZE(bp) > 0; bp = NEXT_BLKP(bp))
{
if (GET_SIZE(HDRP(bp)) >= size && GET_ALLOC(HDRP(bp)) != 1) //傳回第一塊未配置設定且空間大于size的空閑塊
return bp;
}
return NULL;
}
- place函數:
static void place(void *bp, size_t asize) //分割find_fit傳回的塊,建立塊結構
{
size_t bsize = GET_SIZE(HDRP(bp));
if ( (bsize - asize) > 2*DSIZE ) //最小塊為16位元組,分割塊
{
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1));
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(bsize - asize, 0));
PUT(FTRP(bp), PACK(bsize - asize, 0));
}
else //不用分割
{
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1));
}
}
- 主函數:
int main()
{
mem_init(); //初始化模型
mm_init(); //初始化配置設定器
int *a = mm_malloc(sizeof(int)); //測試int
*a = 1;
char *b = mm_malloc(sizeof(char)); //測試char
*b = 'z';
double *c = mm_malloc(sizeof(double)); //測試double
*c = 2.0;
printf("a = %d\nb = %c\nc = %f\n", *a, *b, *c);
}
- 運作結果:
20145215《資訊安全系統設計基礎》第十四周學習總結
本周代碼托管截圖
- 代碼托管連結:click here
- 代碼行數統計:
20145215《資訊安全系統設計基礎》第十四周學習總結
心得體會
- 本章關于配置設定器部分的了解并不是很透徹,隻是大緻的将書上的代碼敲上去,然後編寫了一個main函數進行了測試,關于其中的很多細節還有待進一步的揣摩。
- 關于最後一節C程式中常見的與存儲器有關的錯誤,個人看完之後感覺受益匪淺。之前程式設計過程中就遇到很多類似的問題,每次解決的過程一般都是把錯誤放到百度裡搜,然後根據百度裡的解決過程一個一個試,解決了就解決了,沒解決便無從下手,錯誤也不知道具體是由于什麼原因而導緻的,為什麼會産生這樣的錯誤。看完這一節之後,有一種豁然開朗的感覺,下次編代碼遇到類似的錯誤時就能夠更好的解決這些問題。
學習進度條
代碼行數(新增/累積) | 部落格量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小時 | |
第一周 | 0/0 | 1/2 | 25/45 | 學習了幾個Linux核心指令 |
第二周 | 55/55 | 2/4 | 27/72 | 學會了vim,gcc以及gdb的基本操作 |
第三周 | 148/203 | 1/5 | 23/95 | 對資訊的表示和處理有更深入的了解 |
第五周 | 72/275 | 1/6 | 25/120 | 對彙編語言有了更深的了解 |
第六周 | 56/331 | 2/8 | 30/150 | 安裝了Y86模拟器 |
第七周 | 61/392 | 1/9 | 22/172 | 了解了局部性原理和緩存思想在存儲層次結構中的應用 |
第八周 | 0/392 | 1/10 | 20/192 | 複習前幾章内容 |
第九周 | 132/524 | 2/12 | 24/216 | 了解了Linux作業系統提供的基本I/O服務 |
第十周 | 420/524 | 2/14 | 20/236 | 對常用指令的代碼進行了分析調試,加深了了解 |
第十一周 | 1017/1541 | 2/16 | 26/262 | 對系統調用有了更深的認識 |
第十二周 | 0/1541 | 1/17 | 18/280 | 複習前幾章的知識及代碼 |
第十三周 | 1001/2542 | 1/18 | 23/303 | 加深了對多線程的了解 |
第十四周 | 224/2766 | 1/19 | 22/325 | 了解了虛拟存儲器的概念 |
參考資料
- 《深入了解計算機系統V2》學習指導
- 2016-2017-1 《資訊安全系統設計基礎》教學程序
- 教材導讀與每周考試重點
- 代碼驅動的程式設計學習