2017-2018-1 20155303 《信息安全系統設計基礎》第十一週學習總結

2017-2018-1 20155303 《信息安全系統設計基礎》第十一週學習總結

————————CONTENTS————————


教材內容總結

本週學習目標:①理解虛擬存儲器的概念和做用;②理解地址翻譯的概念;③理解存儲器映射;④掌握動態存儲器分配的方法;⑤瞭解C語言中與存儲器有關的錯誤。html

『一 虛擬存儲器的概念和做用』linux

虛擬存儲器git

  • 虛擬存儲器的三個重要能力:
    • 它將主存當作是一個存儲在磁盤上的地址空間的高速緩存,在主存中只保存活動區域,並根據須要在磁盤和主存之間來回傳送數據,經過這種方式,高效的使用了主存。
    • 它爲每一個進程提供了一致的地址空間,從而簡化了存儲器管理。
    • 它保護了每一個進程的地址空間不被其餘進程破壞。
  • 程序員須要理解虛擬存儲器的三個緣由:
    • 虛擬存儲器是中心的:它是硬件異常、硬件地址翻譯、主存、磁盤文件和內核軟件的交互中心;
    • 虛擬存儲器是強大的:它能夠建立和銷燬存儲器片、能夠映射存儲器片映射到磁盤某個部分等等;
    • 虛擬存儲器若操做不當則十分危險。

地址空間程序員

  • 地址空間是一個非負整數地址的有序集合:{0,1,2,……}
    • 線性地址空間:地址空間中的整數是連續的。
    • 虛擬地址空間:CPU從一個有 N=2^n 個地址的地址空間中生成虛擬地址,這個地址空間成爲稱爲虛擬地址空間。
    • 地址空間的大小:由表示最大地址所須要的位數來描述。
    • 物理地址空間:與系統中的物理存儲器的M個字節相對應。
  • 虛擬存儲器的基本思想:主存中的每一個字節都有一個選自虛擬地址空間的虛擬地址和一個選自物理地址空間的物理地址。

虛擬存儲器做爲緩存的工具編程

  • 虛擬存儲器——虛擬頁(VP),每一個虛擬頁大小爲P=2^p字節。
  • 物理存儲器——物理頁(PP),也叫頁幀,大小也爲P字節。
  • 任意時刻,虛擬頁面的集合都被分爲三個不相交的子集:
    • 未分配的:VM系統還沒分配(建立)的頁,不佔用任何磁盤空間。
    • 緩存的:當前緩存在物理存儲器中的已分配頁。
    • 未緩存的:沒有緩存在物理存儲器中的已分配頁。

頁表數組

  • 頁表:是一個數據結構,存放在物理存儲器中,將虛擬頁映射到物理頁,就是一個頁表條目的數組。
  • 頁表就是一個頁表條目PTE的數組。
    • PTE:由一個有效位和一個n位地址字段組成的,代表了該虛擬頁是否被緩存在DRAM中。
  • 頁表的組成:有效位+n位地址字段
  • 若是設置了有效位:地址字段表示DRAM中相應的物理頁的起始位置,這個物理頁中緩存了該虛擬頁。
  • 若是沒有設置有效位:
    • 空地址:表示該虛擬頁未被分配
    • 不是空地址:這個地址指向該虛擬頁在磁盤上的起始位置。

『二 地址翻譯』緩存

  • 地址翻譯:一個N元素的虛擬地址空間(VAS)中的元素和一個M元素的物理地址空間(PAS)之間的映射。
  • MAP: VAS → PAS ∪ ∅
    • MAP = A' ,若是虛擬地址A處的數據在PAS的物理地址A'處
    • MAP = ∅ ,若是虛擬地址A處的數據不在物理存儲器中
  • CPU中的一個控制寄存器頁表基址寄存器指向當前頁表,n位的虛擬地址包含兩個部分:一個p位的虛擬頁面偏移(VPO) 和一個(n-p)位的虛擬頁號,頁表條目中的物理頁頁號和虛擬地址中的VPO串聯起來,就獲得了相應的物理地址。

『三 存儲器映射』安全

  • 存儲器映射:Linux經過將一個虛擬存儲器區域與一個磁盤上的對象關聯起來,以初始化這個虛擬存儲器區域的內容的過程。
  • 映射對象:
    • Unix文件系統中的普通文件
    • 匿名文件(全都是二進制0)
  • 一旦一個虛擬頁面被初始化了,它就在一個由內核維護的專門的交換文件之間換來換去。交換文件也叫交換空間,或交換區域。

『四 動態存儲器分配的方法』數據結構

  • 當運行時須要額外虛擬存儲器時,使用動態存儲器分配器維護一個進程的虛擬存儲器區域。
  • 分配器有兩種風格:
    • 顯示分配器:要求應用顯式地釋聽任何已經分配的塊。
    • 隱式分配器:要求分配器檢測一個已分配塊什麼時候再也不被程序所使用,就釋放這個塊。也叫作垃圾收集器。

『五 C語言中與存儲器有關的錯誤』多線程

  • 一、間接引用壞指針
    • 在進程的虛擬地址空間中有較大的洞,沒有映射到任何有意義的數據,若是試圖引用一個指向這些洞的指針,操做系統就會以段異常來終止程序。
    • 典型的錯誤是:scanf("%d",val);,沒有加&符號
  • 二、讀未初始化的內存
    • 雖然bass存儲器位置老是被加載器初始化爲0,但對於堆存儲器卻並非這樣的。
    • 常見的錯誤就是假設堆存儲器被初始化爲0.
  • 三、容許棧緩衝區溢出
    • 若是一個程序不檢查輸入串的大小就寫入棧中的目標緩衝區,程序就會出現緩衝區溢出錯誤。
  • 四、假設指針和指向他們的對象大小是相同的。
  • 五、形成錯位錯誤。
  • 六、引用指針,而不是他所指向的對象。
    • 注意C的優先級和結合性
  • 七、誤解指針運算
    • 忘記了指針的算術操做是以它們指向的對象的大小爲單位來進行,而這種大小單位不必定是字節。
  • 八、引用不存在的變量
    • 理解棧的規則,有時會引用再也不合法的本地變量。
  • 九、引用空閒堆塊中的數據
    • 一個類似的錯誤是引用已經被釋放了的堆塊中的數據。
  • 十、引發存儲器泄露
    • 當不當心忘記釋放已分配塊,而在堆裏建立了垃圾時,就會引發存儲器泄露。

返回目錄


學習過程當中遇到的問題及解決

『問題一』:教材P565提到,能夠利用Linux的getrusage函數監測缺頁的數量以及許多其餘的信息,具體如何使用這個函數呢?

『問題一解決』:

首先使用「man -k getrusage」獲取函數的初步信息:

得知getrusage在手冊的第二節,接下來使用「man 2 getrusage」查看函數基本格式和需包含的頭文件:

getrusage函數有兩個參數。第一個參數能夠設置爲RUSAGE_SELF或者RUSAGE_CHILDREN。若是設置成 RUSAGE_SELF,那麼將會以當前進程的相關信息來填充rusage(數據)結構。反之,若是設置成RUSAGE_CHILDREN,那麼 rusage結構中的數據都將是當前進程的子進程的信息。

咱們還注意到,裏面介紹了一個很是重要的結構體:struct rusage,其中包含的內容以下所示

各個字段的解釋以下:

  • ru_utime:返回進程在用戶模式下的執行時間,以timeval結構的形式返回(該結構體在bits/timeval中聲明)。
  • ru_stime:返回進程在內核模式下的執行時間,以timeval結構的形式返回(該結構體在bits/timeval中聲明)。
  • ru_maxrss(Linux 2.6.32起支持):返回最大駐留集的大小,單位爲kb。當who被指定爲RUSAGE_CHILDREN時,返回各子進程最大駐留集的大小中最大的一個,而不是進程樹中最大的最大駐留集。
  • ru_ixrss:目前不支持
  • ru_idrss:目前不支持
  • ru_isrss:目前不支持
  • ru_minflt:缺頁中斷的次數,且處理這些中斷不須要進行I/O;不須要進行I/O操做的緣由是系統使用「reclaiming」的方式在物理內存中獲得了以前被淘汰可是未被修改的頁框。(第一次訪問bss段時也會產生這種類型的缺頁中斷)
  • ru_majflt:缺頁中斷的次數,且處理這些中斷須要進行I/O。
  • ru_nswap:目前不支持
  • ru_inblock(Linux 2.6.22起支持):文件系統須要進行輸入操做的次數。
  • ru_oublock(Linux 2.6.22起支持):文件系統須要進行輸出操做的次數。
  • ru_msgsnd:目前不支持
  • ru_msgrcv:目前不支持
  • ru_nsignals:目前不支持
  • ru_nvcsw(Linux 2.6起支持):因進程自願放棄處理器時間片而致使的上下文切換的次數(一般是爲了等待請求的資源)。
  • ru_nivcsw(Linux 2.6起支持):因進程時間片使用完畢或被高優先級進程搶斷致使的上下文切換的次數。

執行成功返回0,發生錯誤返回-1,同時設置errno的值。

下面使用一個簡單的程序測試函數的功能:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
int main(int argc, char **argv)
{

    struct rusage buf;

    
    if(argc == 2) {
      system(argv[1]);
    }else {
      fprintf(stderr,"./getrusage  \"ls -l > /dev/null\"\n");
      exit(0);
    }
    int err = getrusage(RUSAGE_CHILDREN, &buf);
    //int err = getrusage(RUSAGE_SELF, &buf);
    printf("ERR=%d\n", err);
    printf("%20s:%ld/%ld\t%s\n", "ru_utime", 
       buf.ru_utime.tv_sec, buf.ru_utime.tv_usec,
       "user time used (secs/usecs)");
    printf("%20s:%ld/%ld\t%s\n", "ru_stime", 
       buf.ru_stime.tv_sec, 
       buf.ru_stime.tv_usec,
       "system time used (secs/usecs)");
    printf("%20s:%-10ld\t%s\n", 
       "ru_maxrss",
       buf.ru_maxrss,
       "maximum resident set size");
    printf("%20s:%-10ld\t%s\n",
       "ru_ixrss", 
       buf.ru_ixrss,
       "integral shared memory size");
    printf("%20s:%-10ld\t%s\n", 
       "ru_idrss",
       buf.ru_idrss,
       "integral unshared data size");
    printf("%20s:%-10ld\t%s\n", 
       "ru_isrss",
       buf.ru_isrss,
       "integral unshared data stack size");
    printf("%20s:%-10ld\t%s\n", 
       "ru_minflt",
       buf.ru_minflt,
       "page reclaims");
    printf("%20s:%-10ld\t%s\n",
       "ru_majflt",
       buf.ru_majflt,
       "page faults");
    printf("%20s:%-10ld\t%s\n",
       "ru_nswap",
       buf.ru_nswap,
       "swaps");
    printf("%20s:%-10ld\t%s\n",
       "ru_inblock",
       buf.ru_inblock,
       "block input operations");
    printf("%20s:%-10ld\t%s\n",
       "ru_oublock",
       buf.ru_oublock,
       "block output operations");
    printf("%20s:%-10ld\t%s\n",
       "ru_msgsnd",
       buf.ru_msgsnd,
       "messages sent");
    printf("%20s:%-10ld\t%s\n",
       "ru_msgrcv",
       buf.ru_msgrcv,
       "messages received");
    printf("%20s:%-10ld\t%s\n",
       "ru_nsignals",
       buf.ru_nsignals,
       "signals received");
    printf("%20s:%-10ld\t%s\n",
       "ru_nvcsw",
       buf.ru_nvcsw,
       "voluntary context switches");
    printf("%20s:%-10ld\t%s\n",
       "ru_nivcsw",
       buf.ru_nivcsw,
       "involuntary context switches");
  exit(0);
}

測試結果以下:

『問題二』:教材P567提到,每一個PTE中添加了三個許可位,其中SUP位表示進程是否必須運行在內核(超級用戶)模式下才能訪問該頁。什麼是內核模式呢?與之相對應的是什麼模式呢?

『問題二解決』:

查閱資料瞭解到,Linux操做系統使用了雙模式(內核模式和用戶模式),能夠有效地實現時間共享。

在Linux機器上,CPU要麼處於受信任的內核模式,要麼處於受限制的用戶模式。除了內核自己處於內核模式之外,全部的用戶進程都運行在用戶模式之中。

內核模式的代碼能夠無限制地訪問全部處理器指令集以及所有內存和I/O空間。若是用戶模式的進程要享有此特權,它必須經過系統調用向設備驅動程序或其餘內核模式的代碼發出請求。另外,用戶模式的代碼容許發生缺頁,而內核模式的代碼則不容許。

  • 內核空間和用戶空間:

Linux簡化了分段機制,使得虛擬地址與線性地址老是一致,所以,Linux的虛擬地址空間也爲0~ 4G。Linux內核將這4G字節的空間分爲兩部分。將最高的1G字節(從虛擬地址0xC0000000到0xFFFFFFFF),供內核使用,稱爲「內核空間」。而將較低的3G字節(從虛擬地址0x00000000到0xBFFFFFFF),供各個進程使用,稱爲「用戶空間「)。由於每一個進程能夠經過系統調用進入內核,所以,Linux內核由系統內的全部進程共享。因而,從具體進程的角度來看,每一個進程能夠擁有4G字節的虛擬空間。

  • 內核態和用戶態:

當一個任務(進程)執行系統調用而陷入內核代碼中執行時,咱們就稱進程處於內核運行態(或簡稱爲內核態)。此時處理器處於特權級最高的(0級)內核代碼中執行。當進程處於內核態時,執行的內核代碼會使用當前進程的內核棧。每一個進程都有本身的內核棧。

當進程在執行用戶本身的代碼時,則稱其處於用戶運行態(用戶態)。即此時處理器在特權級最低的(3級)用戶代碼中運行。當正在執行用戶程序而忽然被中斷程序中斷時,此時用戶程序也能夠象徵性地稱爲處於進程的內核態。由於中斷處理程序將使用當前進程的內核棧。這與處於內核態的進程的狀態有些相似。

  • 進程上下文和中斷上下文:

處理器總處於如下狀態中的一種:

一、內核態,運行於進程上下文,內核表明進程運行於內核空間;

二、內核態,運行於中斷上下文,內核表明硬件運行於內核空間;

三、用戶態,運行於用戶空間。

用戶空間的應用程序,經過系統調用,進入內核空間。這個時候用戶空間的進程要傳遞不少變量、參數的值給內核,內核態運行的時候也要保存用戶進程的一些寄存器值、變量等。所謂的「進程上下文」,能夠看做是用戶進程傳遞給內核的這些參數以及內核要保存的那一整套的變量和寄存器值和當時的環境等。

硬件經過觸發信號,致使內核調用中斷處理程序,進入內核空間。這個過程當中,硬件的一些變量和參數也要傳遞給內核,內核經過這些參數進行中斷處理。所謂的「中斷上下文」,其實也能夠看做就是硬件傳遞過來的這些參數和內核須要保存的一些其餘環境(主要是當前被打斷執行的進程環境)。

『問題三』:如何理解malloc/brk/mmap函數?

『問題三解決』:

結合以前學習過的fork相關知識,參考如下代碼:

#include<stdio.h>  
#include<unistd.h>  
#include<stdlib.h>  
#include<sys/wait.h>  
#include<signal.h>  
#include<sys/mman.h>  

int ga = 1;  
int main()  
{  
    int a = 10;  
    int *pa = malloc(sizeof(int));  
    *pa = 100;  
    int *ma = mmap(0,4,PROT_READ|PROT_WRITE,  
            MAP_ANONYMOUS|MAP_SHARED/*MAP_PRIVATE*/,0,0);  
    *ma = 1000; 
    int *spa = sbrk(4);  
    *spa = 10000;  
    if(fork())  
    {  
        printf("parent:ga = %d\n",ga);  
        printf("parent:a = %d\n",a);  
        printf("parent:*pa = %d\n",*pa);  
        printf("parent:*ma = %d\n",*ma);  
        printf("parent:*spa = %d\n",*spa);  
        a = 7;  
        ga = 77;  
        *pa = 777;  
        *ma = 7777;  
        *spa = 77777;  
    }  
    else  
    {  
        sleep(5);  
        printf("\nchild:ga = %d\n",ga);  
        printf("child:a = %d\n",a);  
        printf("child:*pa = %d\n",*pa);  
        printf("child:*ma = %d\n",*ma);  
        printf("child:*spa = %d\n",*spa);  
    }  
    return 0;  
}

運行結果以下:

咱們知道,經過fork建立的子進程克隆父進程的內存區域(全局區、棧區、堆區、代碼區),但內存區域經過映射以後指向不一樣的物理空間,因此,儘管子進程克隆了父進程的內存區域,但他們的實際內存是獨立. 不能相互訪問。能夠看出,malloc/brk/mmap對fork建立的子進程的操做並不徹底相同。

  • brk、mmap:系統調用

brk系統調用,可讓進程的堆指針增加必定的大小,邏輯上消耗掉一塊本進程的虛擬地址區間,malloc向OS獲取的內存大小比較小時,將直接經過brk調用獲取虛擬地址,結果是將本進程的brk指針推高。

mmap系統調用,可讓進程的虛擬地址區間裏切分出一塊指定大小的虛擬地址區間vma_struct,並返回給用戶態進程,被mmap映射返回的虛擬地址,邏輯上被消耗了,直到用戶進程調用munmap,纔回收回來。malloc向系統獲取比較大的內存時,會經過mmap直接映射一塊虛擬地址區間。mmap系統調用用處很是多,好比一個進程的全部動態庫文件.so的加載,都須要經過mmap系統調用映射指定大小的虛擬地址區間,而後將.so代碼動態映射到這些區域,以供進程其餘部分代碼訪問;另外,多進程通信,也可使用mmap,這塊另開文章詳解。

不管是brk仍是mmap返回的都是虛擬地址,在第一次訪問這塊地址的時候,會觸發缺頁異常,而後內核爲這塊虛擬地址申請並映射物理頁框,創建頁表映射關係,後續對該區間虛擬地址的訪問,經過頁表獲取物理地址,而後就能夠在物理內存上讀寫了。

  • malloc:libc庫函數

malloc是 libc實現的庫函數,主要實現了一套內存管理機制,當其管理的內存不夠時,經過brk/mmap等系統調用向內核申請進程的虛擬地址區間,若是其維護的內存能知足malloc調用,則直接返回,free時會將地址塊返回空閒鏈表。

malloc(size) 的時候,這個函數會多分配一塊空間,用於保存size變量,free的時候,直接經過指針前移必定大小,就能夠獲取malloc時保存的size變量,從而free只須要一個指針做爲參數就能夠了calloc 庫函數至關於 malloc + memset(0)

『問題四』:mmap() vs read()/write()?

『問題四解決』:

系統調用mmap()能夠將某文件映射至內存(進程空間),如此能夠把對文件的操做轉爲對內存的操做,以此避免更多的lseek()與read()、write()操做,這點對於大文件或者頻繁訪問的文件而言尤爲受益。但有一點必須清楚:mmap的addr與offset必須對齊一個內存頁面大小的邊界,即內存映射每每是頁面大小的整數倍,不然maaped_file_size%page_size內存空間將被閒置浪費。

如下代碼分別使用mmap與read/write兩種方法,將test.txt文件中的字符轉換成大寫:

  • mmap:
/*
* @file: t_mmap.c
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/mman.h> /*mmap munmap*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
    int fd;
    char *buf;
    off_t len;
    struct stat sb;
    char *fname = "test.txt";
 
    fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd == -1)
    {
        perror("open");
        return 1;
    }
    if (fstat(fd, &sb) == -1)
    {
        perror("fstat");
        return 1;
    }
 
    buf = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if (buf == MAP_FAILED)
    {
        perror("mmap");
        return 1;
    }
 
    if (close(fd) == -1)
    {
        perror("close");
        return 1;
    }
 
    for (len = 0; len < sb.st_size; ++len)
    {
        buf[len] = toupper(buf[len]);
        /*putchar(buf[len]);*/
    }
 
    if (munmap(buf, sb.st_size) == -1)
    {
        perror("munmap");
        return 1;
    }
    return 0;
}

使用strace運行程序:

  • read/write:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
    int fd, len;
    char *buf;
    char *fname = "/tmp/file_mmap";
    ssize_t ret;
    struct stat sb;
 
    fd = open(fname, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
    if (fd == -1)
    {
        perror("open");
        return 1;
    }
    if (fstat(fd, &sb) == -1)
    {
        perror("stat");
        return 1;
    }
 
    buf = malloc(sb.st_size);
    if (buf == NULL)
    {
        perror("malloc");
        return 1;
    }
    ret = read(fd, buf, sb.st_size);
    for (len = 0; len < sb.st_size; ++len)
    {
        buf[len] = toupper(buf[len]);
        /*putchar(buf[len]);*/
    }
 
    lseek(fd, 0, SEEK_SET);
    ret = write(fd, buf, sb.st_size);
    if (ret == -1)
    {
        perror("error");
        return 1;
    }
 
    if (close(fd) == -1)
    {
        perror("close");
        return 1;
    }
    free(buf);
    return 0;
}

使用strace運行程序:

能夠看出:read()/write()在頻繁訪問大文件時,須要調用多個lseek()來肯定位置。每次編輯read()/write(),在物理內存中的雙份數據。而mmap內存映射文件以後,操做內存便是操做文件,能夠省去很多系統內核調用(lseek, read, write)。

『問題五』:fork/寫時複製是如何使用內存空間的?

『問題五解決』:

咱們知道,一個進程在地址空間上的表現形式主要是:正文段,數據段,堆,棧。內核爲其分配相應的數據結構來表示它們,其看作是進程在地址空間的實體,也能夠想象爲靈魂。隨後內核會爲這四部分分配相應的載體,即真正的物理存儲,那麼這些物理存儲就是進程的真正實體。就像靈魂要附之於身體同樣。

有一個父進程P1,這是一個主體(有靈魂也身體)。如今在其虛擬地址空間(有相應的數據結構表示)上有:正文段,數據段,堆,棧這四個部分。相應的,內核要爲這四個部分分配各自的物理塊,即:正文段塊,數據段塊,堆塊,棧塊。

  • 如今P1用fork()函數爲進程建立一個子進程P2,內核:(1)複製P1的正文段,數據段,堆,棧這四個部分,注意是其內容相同。(2)爲這四個部分分配物理塊,P2的:正文段->PI的正文段的物理塊,其實就是不爲P2分配正文段塊,讓P2的正文段指向P1的正文段塊,數據段->P2本身的數據段塊(爲其分配對應的塊),堆->P2本身的堆塊,棧->P2本身的棧塊。以下圖所示:同左到右大的方向箭頭表示複製內容。

  • 寫時複製技術:內核只爲新生成的子進程建立虛擬空間結構,它們來複制於父進程的虛擬究竟結構,可是不爲這些段分配物理內存,它們共享父進程的物理空間,當父子進程中有更改相應段的行爲發生時,再爲子進程相應的段分配物理空間。

返回目錄


代碼託管

碼雲連接

返回目錄


學習感悟和思考

  • 第九章主要介紹了虛擬內存系統的工做方式和特性,將計算機系統中的硬件和軟件結合起來,進行了詳細地闡述。本章還介紹了標準庫的malloc和free等操做,加深了對虛擬內存系統運做過程的理解。最後總結了C語言中與存儲器有關的錯誤,的確都是初學者容易忽略的問題,在從此編程的過程當中需格外注意。
  • 本週學習時比以往更加註重實踐,遇到單靠讀教材難以理解的問題就查閱相關資料,動手驗證。例如mmap()和read()/write()等函數的性能差別等等。而解決問題每每須要一些其餘知識的積累,或是總能遇見與之相關的知識拓展。若是學有餘力,不妨順便了解了解,這樣一來,點狀的知識結構就能在一次次拓展與關聯中編織成網狀,更加穩固堅實了。

返回目錄


學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 20篇 400小時
第一週 50/50 1/1 8/8 瞭解計算機系統、靜態連接與動態連接
第三週 451/501 2/3 27/35 深刻學習計算機算術運算的特性
第四周 503 / 1004 1/4 20/55 掌握程序崩潰處理、Linux系統編程等知識,利用所學知識優化myod,並實現head和tail命令
第五週 315 / 1319 3/7 29/84 掌握「進程」的概念,並學習應用相關函數;瞭解程序如何在機器上表示
第七週 264 / 1583 1/8 15/99 瞭解處理器的體系結構,學習多線程的概念
第九周 634 / 2217 2/10 10/109 學習存儲器系統的層次結構,以及改善程序性能的方法
第十一週 486 / 2703 2/12 23/132 理解虛擬內存的工做原理和特性

返回目錄


參考資料

相關文章
相關標籤/搜索