linux-0.11源碼分析:內(nèi)存管理

linux/mm/memory.c

/* passed
 *  linux/mm/memory.c
 *
 *  (C) 1991  Linus Torvalds
 */
#include <set_seg.h>

/*
 * 需求加載是從01.12.91 開始編寫的- 在程序編制表中似乎是最重要的程序,
 * 并且應(yīng)該是很容易編制的- linus
 */

/*
 * OK饰恕,需求加載是比較容易編寫的训枢,而共享頁面卻需要有點(diǎn)技巧咆爽。共享頁面程序是
 * 02.12.91 開始編寫的,好象能夠工作- Linus睬罗。
 *
 * 通過執(zhí)行大約30 個(gè)/bin/sh 對(duì)共享操作進(jìn)行了測(cè)試:在老內(nèi)核當(dāng)中需要占用多于
 * 6M 的內(nèi)存诗赌,而目前卻不用。現(xiàn)在看來工作得很好盔沫。
 *
 * 對(duì)"invalidate()"函數(shù)也進(jìn)行了修正- 在這方面我還做的不夠医咨。
 */

// 信號(hào)頭文件。定義信號(hào)符號(hào)常量架诞,信號(hào)結(jié)構(gòu)以及信號(hào)操作函數(shù)原型拟淮。
#include <signal.h>

// 系統(tǒng)頭文件。定義了設(shè)置或修改描述符/中斷門等的嵌入式匯編宏谴忧。
#include <asm/system.h>

// 調(diào)度程序頭文件很泊,定義了任務(wù)結(jié)構(gòu)task_struct、初始任務(wù)0 的數(shù)據(jù)俏蛮,
// 還有一些有關(guān)描述符參數(shù)設(shè)置和獲取的嵌入式匯編函數(shù)宏語句撑蚌。
#include <linux/sched.h>
// head 頭文件,定義了段描述符的簡(jiǎn)單結(jié)構(gòu)搏屑,和幾個(gè)選擇符常量。
#include <linux/head.h>
// 內(nèi)核頭文件粉楚。含有一些內(nèi)核常用函數(shù)的原形定義辣恋。
#include <linux/kernel.h>

void do_exit(long code);// 進(jìn)程退出處理函數(shù),在kernel/exit.c模软。

//// 顯示內(nèi)存已用完出錯(cuò)信息伟骨,并退出。
static _inline void oom(void)
{
    printk("out of memory\n\r");
    do_exit(SIGSEGV);// do_exit()應(yīng)該使用退出代碼燃异,這里用了信號(hào)值SIGSEGV(11)
                        // 相同值的出錯(cuò)碼含義是“資源暫時(shí)不可用”携狭,正好同義。
}

// 刷新頁變換高速緩沖宏函數(shù)回俐。
// 為了提高地址轉(zhuǎn)換的效率逛腿,CPU 將最近使用的頁表數(shù)據(jù)存放在芯片中高速緩沖中。
// 在修改過頁表信息之后仅颇,就需要刷新該緩沖區(qū)单默。這里使用重新加載頁目錄基址
// 寄存器cr3 的方法來進(jìn)行刷新。下面eax = 0忘瓦,是頁目錄的基址搁廓。
//#define invalidate() \
//__asm__("movl %%eax,%%cr3"::"a" (0))
#define invalidate() \
_asm{_asm xor eax,eax _asm mov cr3,eax}

/* 下面定義若需要改動(dòng),則需要與head.s 等文件中的相關(guān)信息一起改變 */
// linux 0.11 內(nèi)核默認(rèn)支持的最大內(nèi)存容量是16M耕皮,可以修改這些定義以適合更多的內(nèi)存境蜕。
#define LOW_MEM 0x100000    // 內(nèi)存低端(1MB)。
#define PAGING_MEMORY (15*1024*1024)// 分頁內(nèi)存15MB凌停。主內(nèi)存區(qū)最多15M粱年。
#define PAGING_PAGES (PAGING_MEMORY>>12)// 分頁后的物理內(nèi)存頁數(shù)。
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)// 指定物理內(nèi)存地址映射為頁號(hào)苦锨。
#define USED 100// 頁面被占用標(biāo)志逼泣。

// 該宏用于判斷給定地址是否位于當(dāng)前進(jìn)程的代碼段中趴泌。
#define CODE_SPACE(addr) \
((((addr)+4095)&~4095) < current->start_code + current->end_code)

static unsigned long HIGH_MEMORY = 0;// 全局變量,存放實(shí)際物理內(nèi)存最高端地址拉庶。

// 復(fù)制1 頁內(nèi)存(4K 字節(jié))嗜憔。
//#define copy_page(from,to) \
//__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
#define copy_page(from,to) _copy_page((void *)(from),(void *)(to))
_inline void _copy_page(void *from, void *to)
{_asm{
    pushf
    mov ecx,1024 
    mov esi,from 
    mov edi,to 
    cld 
    rep movsd 
    popf
}}

// 內(nèi)存映射字節(jié)圖(1 字節(jié)代表1 頁內(nèi)存),每個(gè)頁面對(duì)應(yīng)
// 的字節(jié)用于標(biāo)志頁面當(dāng)前被引用(占用)次數(shù)氏仗。
static unsigned char mem_map [ PAGING_PAGES ] = {0,};

/*
 * 獲取首個(gè)(實(shí)際上是最后1 個(gè):-)物理空閑頁面吉捶,并標(biāo)記為已使用。如果沒有空閑頁面皆尔,
 * 就返回0呐舔。
 */
//// 取物理空閑頁面。如果已經(jīng)沒有可用內(nèi)存了慷蠕,則返回0珊拼。
// 輸入:%1(ax=0) - 0;%2(LOW_MEM)流炕;%3(cx=PAGING PAGES)澎现;%4(edi=mem_map+PAGING_PAGES-1)。
// 輸出:返回%0(ax=頁面起始地址)每辟。
// 上面%4 寄存器實(shí)際指向mem_map[]內(nèi)存字節(jié)圖的最后一個(gè)字節(jié)剑辫。本函數(shù)從字節(jié)圖末端開始向前掃描
// 所有頁面標(biāo)志(頁面總數(shù)為PAGING_PAGES),若有頁面空閑(其內(nèi)存映像字節(jié)為0)則返回頁面地址渠欺。
// 注意妹蔽!本函數(shù)只是指出在主內(nèi)存區(qū)的一頁空閑頁面,但并沒有映射到某個(gè)進(jìn)程的線性地址去挠将。后面
// 的put_page()函數(shù)就是用來作映射的胳岂。
unsigned long get_free_page(void)
{
//  unsigned long __res = mem_map+PAGING_PAGES-1;
    __asm {
        pushf
        xor eax, eax
        mov ecx,PAGING_PAGES
//      mov edi,__res 
        mov edi,offset mem_map + PAGING_PAGES - 1
        std
        repne scasb     // 方向位置位,將al(0)與對(duì)應(yīng)(di)每個(gè)頁面的內(nèi)容比較捐名,
        jne l1      // 如果沒有等于0 的字節(jié)旦万,則跳轉(zhuǎn)結(jié)束(返回0)。
        mov byte ptr [edi+1],1  // 將對(duì)應(yīng)頁面的內(nèi)存映像位置1镶蹋。
        sal ecx,12  // 頁面數(shù)*4K = 相對(duì)頁面起始地址成艘。
        add ecx,LOW_MEM // 再加上低端內(nèi)存地址,即獲得頁面實(shí)際物理起始地址贺归。
        mov edx,ecx // 將頁面實(shí)際起始地址 -> edx 寄存器淆两。
        mov ecx,1024    // 寄存器ecx 置計(jì)數(shù)值1024。
        lea edi,[edx+4092]// 將4092+edx 的位置 -> edi(該頁面的末端)拂酣。
        rep stosd   // 將edi 所指內(nèi)存清零(反方向秋冰,也即將該頁面清零)。
//      mov __res,edx   // 將頁面起始地址 -> __res(返回值)婶熬。
        mov eax,edx
    l1: popf
    }
//  return __res;// 返回空閑頁面地址(如果無空閑也則返回0)剑勾。
}
//unsigned long get_free_page(void)
//{
//register unsigned long __res asm("ax");
//
//__asm__("std ; repne ; scasb\n\t" // 方向位置位埃撵,將al(0)與對(duì)應(yīng)每個(gè)頁面的(di)內(nèi)容比較,
//  "jne 1f\n\t"    // 如果沒有等于0 的字節(jié)虽另,則跳轉(zhuǎn)結(jié)束(返回0)暂刘。
//  "movb $1,1(%%edi)\n\t"http:// 將對(duì)應(yīng)頁面的內(nèi)存映像位置1。
//  "sall $12,%%ecx\n\t"http:// 頁面數(shù)*4K = 相對(duì)頁面起始地址捂刺。
//  "addl %2,%%ecx\n\t"http:// 再加上低端內(nèi)存地址谣拣,即獲得頁面實(shí)際物理起始地址。
//  "movl %%ecx,%%edx\n\t"http:// 將頁面實(shí)際起始地址 -> edx 寄存器族展。
//  "movl $1024,%%ecx\n\t"http:// 寄存器ecx 置計(jì)數(shù)值1024森缠。
//  "leal 4092(%%edx),%%edi\n\t"http:// 將4092+edx 的位置 -> edi(該頁面的末端)。
/// "rep ; stosl\n\t"http:// 將edi 所指內(nèi)存清零(反方向仪缸,也即將該頁面清零)贵涵。
//  "movl %%edx,%%eax\n"http:// 將頁面起始地址 -> eax(返回值)。
//  "1:"
//  :"=a" (__res)
//  :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
//  "D" (mem_map+PAGING_PAGES-1)
//  :"di","cx","dx");
//return __res;// 返回空閑頁面地址(如果無空閑也則返回0)腹殿。
//}

/*
 * 釋放物理地址'addr'開始的一頁內(nèi)存独悴。用于函數(shù)'free_page_tables()'。
 */
//// 釋放物理地址addr 開始的一頁面內(nèi)存锣尉。
// 1MB 以下的內(nèi)存空間用于內(nèi)核程序和緩沖,不作為分配頁面的內(nèi)存空間决采。
void free_page(unsigned long addr)
{
    if (addr < LOW_MEM) return;// 如果物理地址addr 小于內(nèi)存低端(1MB)自沧,則返回。
    if (addr >= HIGH_MEMORY)// 如果物理地址addr>=內(nèi)存最高端树瞭,則顯示出錯(cuò)信息拇厢。
        panic("trying to free nonexistent page");
    addr -= LOW_MEM;// 物理地址減去低端內(nèi)存位置,再除以4KB晒喷,得頁面號(hào)孝偎。
    addr >>= 12;
    if (mem_map[addr]--) return;// 如果對(duì)應(yīng)內(nèi)存頁面映射字節(jié)不等于0,則減1 返回凉敲。
    mem_map[addr]=0;// 否則置對(duì)應(yīng)頁面映射字節(jié)為0衣盾,并顯示出錯(cuò)信息,死機(jī)爷抓。
    panic("trying to free free page");
}

/*
 * 下面函數(shù)釋放頁表連續(xù)的內(nèi)存塊势决,'exit()'需要該函數(shù)。與copy_page_tables()
 * 類似蓝撇,該函數(shù)僅處理4Mb 的內(nèi)存塊果复。
 */
//// 根據(jù)指定的線性地址和限長(zhǎng)(頁表個(gè)數(shù)),釋放對(duì)應(yīng)內(nèi)存頁表所指定的內(nèi)存塊并置表項(xiàng)空閑渤昌。
// 頁目錄位于物理地址0 開始處虽抄,共1024 項(xiàng)走搁,占4K 字節(jié)。每個(gè)目錄項(xiàng)指定一個(gè)頁表迈窟。
// 頁表從物理地址0x1000 處開始(緊接著目錄空間)私植,每個(gè)頁表有1024 項(xiàng),也占4K 內(nèi)存菠隆。
// 每個(gè)頁表項(xiàng)對(duì)應(yīng)一頁物理內(nèi)存(4K)兵琳。目錄項(xiàng)和頁表項(xiàng)的大小均為4 個(gè)字節(jié)。
// 參數(shù):from - 起始基地址骇径;size - 釋放的長(zhǎng)度躯肌。
int free_page_tables(unsigned long from,unsigned long size)
{
    unsigned long *pg_table;
    unsigned long * dir, nr;

    if (from & 0x3fffff)// 要釋放內(nèi)存塊的地址需以4M 為邊界。
        panic("free_page_tables called with wrong alignment");
    if (!from)// 出錯(cuò)破衔,試圖釋放內(nèi)核和緩沖所占空間清女。
        panic("Trying to free up swapper memory space");
// 計(jì)算所占頁目錄項(xiàng)數(shù)(4M 的進(jìn)位整數(shù)倍),也即所占頁表數(shù)晰筛。
    size = (size + 0x3fffff) >> 22;
// 下面一句計(jì)算起始目錄項(xiàng)嫡丙。對(duì)應(yīng)的目錄項(xiàng)號(hào)=from>>22,因每項(xiàng)占4 字節(jié)读第,并且由于頁目錄是從
// 物理地址0 開始曙博,因此實(shí)際的目錄項(xiàng)指針=目錄項(xiàng)號(hào)<<2,也即(from>>20)怜瞒。與上0xffc 確保
// 目錄項(xiàng)指針范圍有效父泳。
    dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
    for ( ; size-->0 ; dir++) {// size 現(xiàn)在是需要被釋放內(nèi)存的目錄項(xiàng)數(shù)。
        if (!(1 & *dir))// 如果該目錄項(xiàng)無效(P 位=0)吴汪,則繼續(xù)惠窄。
            continue;// 目錄項(xiàng)的位0(P 位)表示對(duì)應(yīng)頁表是否存在。
        pg_table = (unsigned long *) (0xfffff000 & *dir);// 取目錄項(xiàng)中頁表地址漾橙。
        for (nr=0 ; nr<1024 ; nr++) {// 每個(gè)頁表有1024 個(gè)頁項(xiàng)杆融。
            if (1 & *pg_table)// 若該頁表項(xiàng)有效(P 位=1),則釋放對(duì)應(yīng)內(nèi)存頁霜运。
                free_page(0xfffff000 & *pg_table);
            *pg_table = 0;// 該頁表項(xiàng)內(nèi)容清零脾歇。
            pg_table++;// 指向頁表中下一項(xiàng)。
        }
        free_page(0xfffff000 & *dir);// 釋放該頁表所占內(nèi)存頁面觉渴。但由于頁表在
                                        // 物理地址1M 以內(nèi)介劫,所以這句什么都不做。
        *dir = 0;// 對(duì)相應(yīng)頁表的目錄項(xiàng)清零案淋。
    }
    invalidate();// 刷新頁變換高速緩沖座韵。
    return 0;
}

/*
 * 好了,下面是內(nèi)存管理mm 中最為復(fù)雜的程序之一。它通過只復(fù)制內(nèi)存頁面
 * 來拷貝一定范圍內(nèi)線性地址中的內(nèi)容誉碴。希望代碼中沒有錯(cuò)誤宦棺,因?yàn)槲也幌? * 再調(diào)試這塊代碼了 :-)
 *
 * 注意!我們并不是僅復(fù)制任何內(nèi)存塊- 內(nèi)存塊的地址需要是4Mb 的倍數(shù)(正好
 * 一個(gè)頁目錄項(xiàng)對(duì)應(yīng)的內(nèi)存大星痢)代咸,因?yàn)檫@樣處理可使函數(shù)很簡(jiǎn)單。不管怎樣成黄,
 * 它僅被fork()使用(fork.c)
 *
 * 注意!!當(dāng)from==0 時(shí)呐芥,是在為第一次fork()調(diào)用復(fù)制內(nèi)核空間。此時(shí)我們
 * 不想復(fù)制整個(gè)頁目錄項(xiàng)對(duì)應(yīng)的內(nèi)存奋岁,因?yàn)檫@樣做會(huì)導(dǎo)致內(nèi)存嚴(yán)重的浪費(fèi)- 我們
 * 只復(fù)制頭160 個(gè)頁面- 對(duì)應(yīng)640kB思瘟。即使是復(fù)制這些頁面也已經(jīng)超出我們的需求,
 * 但這不會(huì)占用更多的內(nèi)存- 在低1Mb 內(nèi)存范圍內(nèi)我們不執(zhí)行寫時(shí)復(fù)制操作闻伶,所以
 * 這些頁面可以與內(nèi)核共享滨攻。因此這是nr=xxxx 的特殊情況(nr 在程序中指頁面數(shù))。
 */
//// 復(fù)制指定線性地址和長(zhǎng)度(頁表個(gè)數(shù))內(nèi)存對(duì)應(yīng)的頁目錄項(xiàng)和頁表蓝翰,從而被復(fù)制的頁目錄和
//// 頁表對(duì)應(yīng)的原物理內(nèi)存區(qū)被共享使用光绕。
// 復(fù)制指定地址和長(zhǎng)度的內(nèi)存對(duì)應(yīng)的頁目錄項(xiàng)和頁表項(xiàng)。需申請(qǐng)頁面來存放新頁表畜份,原內(nèi)存區(qū)被共享诞帐;
// 此后兩個(gè)進(jìn)程將共享內(nèi)存區(qū),直到有一個(gè)進(jìn)程執(zhí)行寫操作時(shí)爆雹,才分配新的內(nèi)存頁(寫時(shí)復(fù)制機(jī)制)景埃。
int copy_page_tables(unsigned long from,unsigned long to,long size)
{
    unsigned long * from_page_table;
    unsigned long * to_page_table;
    unsigned long this_page;
    unsigned long * from_dir, * to_dir;
    unsigned long nr;

    // 源地址和目的地址都需要是在4Mb 的內(nèi)存邊界地址上。否則出錯(cuò)顶别,死機(jī)。
    if ((from&0x3fffff) || (to&0x3fffff))
        panic("copy_page_tables called with wrong alignment");
    // 取得源地址和目的地址的目錄項(xiàng)(from_dir 和to_dir)拒啰。參見對(duì)115 句的注釋驯绎。
    from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
    to_dir = (unsigned long *) ((to>>20) & 0xffc);
    // 計(jì)算要復(fù)制的內(nèi)存塊占用的頁表數(shù)(也即目錄項(xiàng)數(shù))。
    size = ((unsigned) (size+0x3fffff)) >> 22;
    // 下面開始對(duì)每個(gè)占用的頁表依次進(jìn)行復(fù)制操作谋旦。
    for( ; size-->0 ; from_dir++,to_dir++) {
        if (1 & *to_dir)// 如果目的目錄項(xiàng)指定的頁表已經(jīng)存在(P=1)剩失,則出錯(cuò),死機(jī)册着。
            panic("copy_page_tables: already exist");
        if (!(1 & *from_dir))// 如果此源目錄項(xiàng)未被使用拴孤,則不用復(fù)制對(duì)應(yīng)頁表,跳過甲捏。
            continue;
        // 取當(dāng)前源目錄項(xiàng)中頁表的地址 -> from_page_table演熟。
        from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
// 為目的頁表取一頁空閑內(nèi)存,如果返回是0 則說明沒有申請(qǐng)到空閑內(nèi)存頁面。返回值=-1芒粹,退出兄纺。
        if (!(to_page_table = (unsigned long *) get_free_page()))
            return -1;  /* Out of memory, see freeing */
        // 設(shè)置目的目錄項(xiàng)信息。7 是標(biāo)志信息化漆,表示(Usr, R/W, Present)估脆。
        *to_dir = ((unsigned long) to_page_table) | 7;
        // 針對(duì)當(dāng)前處理的頁表,設(shè)置需復(fù)制的頁面數(shù)座云。如果是在內(nèi)核空間疙赠,則僅需復(fù)制頭160 頁,
        // 否則需要復(fù)制1 個(gè)頁表中的所有1024 頁面朦拖。
        nr = (from==0)?0xA0:1024;
        // 對(duì)于當(dāng)前頁表圃阳,開始復(fù)制指定數(shù)目nr 個(gè)內(nèi)存頁面。
        for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
            this_page = *from_page_table;// 取源頁表項(xiàng)內(nèi)容贞谓。
            if (!(1 & this_page))// 如果當(dāng)前源頁面沒有使用限佩,則不用復(fù)制。
                continue;
// 復(fù)位頁表項(xiàng)中R/W 標(biāo)志(置0)裸弦。(如果U/S 位是0祟同,則R/W 就沒有作用。如果U/S 是1理疙,而R/W 是0晕城,
// 那么運(yùn)行在用戶層的代碼就只能讀頁面。如果U/S 和R/W 都置位窖贤,則就有寫的權(quán)限砖顷。)
            this_page &= ~2;
            *to_page_table = this_page;// 將該頁表項(xiàng)復(fù)制到目的頁表中。
// 如果該頁表項(xiàng)所指頁面的地址在1M 以上赃梧,則需要設(shè)置內(nèi)存頁面映射數(shù)組mem_map[]滤蝠,于是計(jì)算
// 頁面號(hào),并以它為索引在頁面映射數(shù)組相應(yīng)項(xiàng)中增加引用次數(shù)授嘀。
            if (this_page > LOW_MEM) {
// 下面這句的含義是令源頁表項(xiàng)所指內(nèi)存頁也為只讀物咳。因?yàn)楝F(xiàn)在開始有兩個(gè)進(jìn)程共用內(nèi)存區(qū)了。
// 若其中一個(gè)內(nèi)存需要進(jìn)行寫操作蹄皱,則可以通過頁異常的寫保護(hù)處理览闰,為執(zhí)行寫操作的進(jìn)程分配
// 一頁新的空閑頁面,也即進(jìn)行寫時(shí)復(fù)制的操作巷折。
                *from_page_table = this_page;// 令源頁表項(xiàng)也只讀压鉴。
                this_page -= LOW_MEM;
                this_page >>= 12;
                mem_map[this_page]++;
            }
        }
    }
    invalidate();// 刷新頁變換高速緩沖。
    return 0;
}

/*
 * 下面函數(shù)將一內(nèi)存頁面放置在指定地址處锻拘。它返回頁面的物理地址油吭,如果
 * 內(nèi)存不夠(在訪問頁表或頁面時(shí)),則返回0。
 */
//// 把一物理內(nèi)存頁面映射到指定的線性地址處上鞠。
// 主要工作是在頁目錄和頁表中設(shè)置指定頁面的信息际邻。若成功則返回頁面地址。
unsigned long put_page(unsigned long page,unsigned long address)
{
    unsigned long tmp, *page_table;

/* 注意!!!這里使用了頁目錄基址_pg_dir=0 的條件 */

// 如果申請(qǐng)的頁面位置低于LOW_MEM(1Mb)或超出系統(tǒng)實(shí)際含有內(nèi)存高端HIGH_MEMORY芍阎,則發(fā)出警告世曾。
    if (page < LOW_MEM || page >= HIGH_MEMORY)
        printk("Trying to put page %p at %p\n",page,address);
    // 如果申請(qǐng)的頁面在內(nèi)存頁面映射字節(jié)圖中沒有置位,則顯示警告信息谴咸。
    if (mem_map[(page-LOW_MEM)>>12] != 1)
        printk("mem_map disagrees with %p at %p\n",page,address);
    // 計(jì)算指定地址在頁目錄表中對(duì)應(yīng)的目錄項(xiàng)指針轮听。
    page_table = (unsigned long *) ((address>>20) & 0xffc);
// 如果該目錄項(xiàng)有效(P=1)(也即指定的頁表在內(nèi)存中),則從中取得指定頁表的地址 -> page_table岭佳。
    if ((*page_table)&1)
        page_table = (unsigned long *) (0xfffff000 & *page_table);
    else {
// 否則血巍,申請(qǐng)空閑頁面給頁表使用,并在對(duì)應(yīng)目錄項(xiàng)中置相應(yīng)標(biāo)志7(User, U/S, R/W)珊随。然后將
// 該頁表的地址 -> page_table述寡。
        if (!(tmp=get_free_page()))
            return 0;
        *page_table = tmp|7;
        page_table = (unsigned long *) tmp;
    }
    // 在頁表中設(shè)置指定地址的物理內(nèi)存頁面的頁表項(xiàng)內(nèi)容。每個(gè)頁表共可有1024 項(xiàng)(0x3ff)。
    page_table[(address>>12) & 0x3ff] = page | 7;
/* 不需要刷新頁變換高速緩沖 */
    return page;// 返回頁面地址。
}

//// 取消寫保護(hù)頁面函數(shù)替废。用于頁異常中斷過程中寫保護(hù)異常的處理(寫時(shí)復(fù)制)。
// 輸入?yún)?shù)為頁表項(xiàng)指針螟炫。
// [ un_wp_page 意思是取消頁面的寫保護(hù):Un-Write Protected。]
void un_wp_page(unsigned long * table_entry)
{
    unsigned long old_page,new_page;

    old_page = 0xfffff000 & *table_entry;// 取原頁面對(duì)應(yīng)的目錄項(xiàng)號(hào)艺晴。
// 如果原頁面地址大于內(nèi)存低端LOW_MEM(1Mb)昼钻,并且其在頁面映射字節(jié)圖數(shù)組中值為1(表示僅
// 被引用1 次,頁面沒有被共享)封寞,則在該頁面的頁表項(xiàng)中置R/W 標(biāo)志(可寫)然评,并刷新頁變換
// 高速緩沖,然后返回狈究。
    if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
        *table_entry |= 2;
        invalidate();
        return;
    }
    // 否則沾瓦,在主內(nèi)存區(qū)內(nèi)申請(qǐng)一頁空閑頁面。
    if (!(new_page=get_free_page()))
        oom();// Out of Memory谦炒。內(nèi)存不夠處理。
// 如果原頁面大于內(nèi)存低端(則意味著mem_map[]>1风喇,頁面是共享的)宁改,則將原頁面的頁面映射
// 數(shù)組值遞減1。然后將指定頁表項(xiàng)內(nèi)容更新為新頁面的地址魂莫,并置可讀寫等標(biāo)志(U/S, R/W, P)还蹲。
// 刷新頁變換高速緩沖。最后將原頁面內(nèi)容復(fù)制到新頁面。
    if (old_page >= LOW_MEM)
        mem_map[MAP_NR(old_page)]--;
    *table_entry = new_page | 7;
    invalidate();
    copy_page(old_page,new_page);
}   

/*
 * 當(dāng)用戶試圖往一個(gè)共享頁面上寫時(shí)谜喊,該函數(shù)處理已存在的內(nèi)存頁面潭兽,(寫時(shí)復(fù)制)
 * 它是通過將頁面復(fù)制到一個(gè)新地址上并遞減原頁面的共享頁面計(jì)數(shù)值實(shí)現(xiàn)的。
 *
 * 如果它在代碼空間斗遏,我們就以段錯(cuò)誤信息退出山卦。
 */
//// 頁異常中斷處理調(diào)用的C 函數(shù)。寫共享頁面處理函數(shù)诵次。在page.s 程序中被調(diào)用账蓉。
// 參數(shù)error_code 是由CPU 自動(dòng)產(chǎn)生,address 是頁面線性地址逾一。
// 寫共享頁面時(shí)铸本,需復(fù)制頁面(寫時(shí)復(fù)制)。
void do_wp_page(unsigned long error_code,unsigned long address)
{
#if 0
/* 我們現(xiàn)在還不能這樣做:因?yàn)閑stdio 庫(kù)會(huì)在代碼空間執(zhí)行寫操作 */
/* 真是太愚蠢了遵堵。我真想從GNU 得到libc.a 庫(kù)箱玷。 */
    if (CODE_SPACE(address))    // 如果地址位于代碼空間,則終止執(zhí)行程序陌宿。
        do_exit(SIGSEGV);
#endif
// 處理取消頁面保護(hù)锡足。參數(shù)指定頁面在頁表中的頁表項(xiàng)指針,其計(jì)算方法是:
// ((address>>10) & 0xffc):計(jì)算指定地址的頁面在頁表中的偏移地址限番;
// (0xfffff000 &((address>>20) &0xffc)):取目錄項(xiàng)中頁表的地址值舱污,
// 其中((address>>20) &0xffc)計(jì)算頁面所在頁表的目錄項(xiàng)指針;
// 兩者相加即得指定地址對(duì)應(yīng)頁面的頁表項(xiàng)指針弥虐。這里對(duì)共享的頁面進(jìn)行復(fù)制扩灯。
    un_wp_page(
        (unsigned long *)(((address>>10) & 0xffc) + 
        (0xfffff000 & *((unsigned long *) ((address>>20) &0xffc))))
    );

}

//// 寫頁面驗(yàn)證。
// 若頁面不可寫霜瘪,則復(fù)制頁面珠插。在fork.c 第34 行被調(diào)用。
void write_verify(unsigned long address)
{
    unsigned long page;

// 判斷指定地址所對(duì)應(yīng)頁目錄項(xiàng)的頁表是否存在(P)颖对,若不存在(P=0)則返回捻撑。
    if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
        return;
// 取頁表的地址,加上指定地址的頁面在頁表中的頁表項(xiàng)偏移值缤底,得對(duì)應(yīng)物理頁面的頁表項(xiàng)指針顾患。
    page &= 0xfffff000;
    page += ((address>>10) & 0xffc);
// 如果該頁面不可寫(標(biāo)志R/W 沒有置位),則執(zhí)行共享檢驗(yàn)和復(fù)制頁面操作(寫時(shí)復(fù)制)个唧。
    if ((3 & *(unsigned long *) page) == 1)  /* non-writeable, present */
        un_wp_page((unsigned long *) page);
    return;
}

//// 取得一頁空閑內(nèi)存并映射到指定線性地址處江解。
// 與get_free_page()不同。get_free_page()僅是申請(qǐng)取得了主內(nèi)存區(qū)的一頁物理內(nèi)存徙歼。而該函數(shù)
// 不僅是獲取到一頁物理內(nèi)存頁面犁河,還進(jìn)一步調(diào)用put_page()鳖枕,將物理頁面映射到指定的線性地址
// 處。
void get_empty_page(unsigned long address)
{
    unsigned long tmp;

// 若不能取得一空閑頁面桨螺,或者不能將頁面放置到指定地址處宾符,則顯示內(nèi)存不夠的信息。
// 279 行上英文注釋的含義是:即使執(zhí)行g(shù)et_free_page()返回0 也無所謂灭翔,因?yàn)閜ut_page()
// 中還會(huì)對(duì)此情況再次申請(qǐng)空閑物理頁面的魏烫,見210 行。
    if (!(tmp=get_free_page()) || !put_page(tmp,address)) {
        free_page(tmp);     /* 0 is ok - ignored */
        oom();
    }
}

/*
 * try_to_share()在任務(wù)"p"中檢查位于地址"address"處的頁面缠局,看頁面是否存在则奥,是否干凈。
 * 如果是干凈的話狭园,就與當(dāng)前任務(wù)共享读处。
 *
 * 注意!這里我們已假定p !=當(dāng)前任務(wù)唱矛,并且它們共享同一個(gè)執(zhí)行程序罚舱。
 */
//// 嘗試對(duì)進(jìn)程指定地址處的頁面進(jìn)行共享操作。
// 同時(shí)還驗(yàn)證指定的地址處是否已經(jīng)申請(qǐng)了頁面绎谦,若是則出錯(cuò)管闷,死機(jī)。
// 返回1-成功窃肠,0-失敗包个。
static int try_to_share(unsigned long address, struct task_struct * p)
{
    unsigned long from;
    unsigned long to;
    unsigned long from_page;
    unsigned long to_page;
    unsigned long phys_addr;

// 求指定內(nèi)存地址的頁目錄項(xiàng)。
    from_page = to_page = ((address>>20) & 0xffc);
// 計(jì)算進(jìn)程p 的代碼起始地址所對(duì)應(yīng)的頁目錄項(xiàng)冤留。
    from_page += ((p->start_code>>20) & 0xffc);
// 計(jì)算當(dāng)前進(jìn)程中代碼起始地址所對(duì)應(yīng)的頁目錄項(xiàng)碧囊。
    to_page += ((current->start_code>>20) & 0xffc);
/* 在from 處是否存在頁目錄? */
// *** 對(duì)p 進(jìn)程頁面進(jìn)行操作纤怒。
// 取頁目錄項(xiàng)內(nèi)容糯而。如果該目錄項(xiàng)無效(P=0),則返回泊窘。否則取該目錄項(xiàng)對(duì)應(yīng)頁表地址-> from熄驼。
    from = *(unsigned long *) from_page;
    if (!(from & 1))
        return 0;
    from &= 0xfffff000;
// 計(jì)算地址對(duì)應(yīng)的頁表項(xiàng)指針值,并取出該頁表項(xiàng)內(nèi)容 -> phys_addr烘豹。
    from_page = from + ((address>>10) & 0xffc);
    phys_addr = *(unsigned long *) from_page;
/* 頁面干凈并且存在嗎瓜贾? */
// 0x41 對(duì)應(yīng)頁表項(xiàng)中的Dirty 和Present 標(biāo)志。如果頁面不干凈或無效則返回携悯。
    if ((phys_addr & 0x41) != 0x01)
        return 0;
// 取頁面的地址 -> phys_addr阐虚。如果該頁面地址不存在或小于內(nèi)存低端(1M)也返回退出。
    phys_addr &= 0xfffff000;
    if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM)
        return 0;
// *** 對(duì)當(dāng)前進(jìn)程頁面進(jìn)行操作蚌卤。
// 取頁目錄項(xiàng)內(nèi)容 -> to实束。如果該目錄項(xiàng)無效(P=0),則取空閑頁面逊彭,并更新to_page 所指的目錄項(xiàng)咸灿。
    to = *(unsigned long *) to_page;
    if (!(to & 1))
        if (to = get_free_page())
            *(unsigned long *) to_page = to | 7;
        else
            oom();
// 取對(duì)應(yīng)頁表地址 -> to,頁表項(xiàng)地址 to_page侮叮。如果對(duì)應(yīng)的頁面已經(jīng)存在避矢,則出錯(cuò),死機(jī)囊榜。
    to &= 0xfffff000;
    to_page = to + ((address>>10) & 0xffc);
    if (1 & *(unsigned long *) to_page)
        panic("try_to_share: to_page already exists");
/* 對(duì)它們進(jìn)行共享處理:寫保護(hù) */
// 對(duì)p 進(jìn)程中頁面置寫保護(hù)標(biāo)志(置R/W=0 只讀)审胸。并且當(dāng)前進(jìn)程中的對(duì)應(yīng)頁表項(xiàng)指向它。
    *(unsigned long *) from_page &= ~2;
    *(unsigned long *) to_page = *(unsigned long *) from_page;
    // 刷新頁變換高速緩沖卸勺。
    invalidate();
    // 計(jì)算所操作頁面的頁面號(hào)砂沛,并將對(duì)應(yīng)頁面映射數(shù)組項(xiàng)中的引用遞增1。
    phys_addr -= LOW_MEM;
    phys_addr >>= 12;
    mem_map[phys_addr]++;
    return 1;
}

/*
 * share_page()試圖找到一個(gè)進(jìn)程曙求,它可以與當(dāng)前進(jìn)程共享頁面碍庵。參數(shù)address 是
 * 當(dāng)前數(shù)據(jù)空間中期望共享的某頁面地址。
 *
 * 首先我們通過檢測(cè)executable->i_count 來查證是否可行悟狱。如果有其它任務(wù)已共享
 * 該inode静浴,則它應(yīng)該大于1。
 */
//// 共享頁面挤渐。在缺頁處理時(shí)看看能否共享頁面
// 返回1 - 成功苹享,0 - 失敗。浴麻。
static int share_page(unsigned long address)
{
    struct task_struct ** p;

// 如果是不可執(zhí)行的得问,則返回。excutable 是執(zhí)行進(jìn)程的內(nèi)存i 節(jié)點(diǎn)結(jié)構(gòu)白胀。
    if (!current->executable)
        return 0;
// 如果只能單獨(dú)執(zhí)行(executable->i_count=1)椭赋,也退出。
    if (current->executable->i_count < 2)
        return 0;
// 搜索任務(wù)數(shù)組中所有任務(wù)或杠。尋找與當(dāng)前進(jìn)程可共享頁面的進(jìn)程哪怔,
// 并嘗試對(duì)指定地址的頁面進(jìn)行共享。
    for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
        if (!*p)// 如果該任務(wù)項(xiàng)空閑向抢,則繼續(xù)尋找认境。
            continue;
        if (current == *p)// 如果就是當(dāng)前任務(wù),也繼續(xù)尋找挟鸠。
            continue;
        if ((*p)->executable != current->executable)// 如果executable 不等叉信,也繼續(xù)。
            continue;
        if (try_to_share(address,*p))// 嘗試共享頁面艘希。
            return 1;
    }
    return 0;
}

//// 頁異常中斷處理調(diào)用的函數(shù)硼身。處理缺頁異常情況硅急。在page.s 程序中被調(diào)用。
// 參數(shù)error_code 是由CPU 自動(dòng)產(chǎn)生佳遂,address 是頁面線性地址营袜。
void do_no_page(unsigned long error_code,unsigned long address)
{
    int nr[4];
    unsigned long tmp;
    unsigned long page;
    int block,i;

    address &= 0xfffff000;// 頁面地址。
// 首先算出指定線性地址在進(jìn)程空間中相對(duì)于進(jìn)程基址的偏移長(zhǎng)度值丑罪。
    tmp = address - current->start_code;
// 若當(dāng)前進(jìn)程的executable 空荚板,或者指定地址超出代碼+數(shù)據(jù)長(zhǎng)度,則申請(qǐng)一頁物理內(nèi)存吩屹,并映射
// 影射到指定的線性地址處跪另。executable 是進(jìn)程的i 節(jié)點(diǎn)結(jié)構(gòu)。該值為0煤搜,表明進(jìn)程剛開始設(shè)置免绿,
// 需要內(nèi)存;而指定的線性地址超出代碼加數(shù)據(jù)長(zhǎng)度宅楞,表明進(jìn)程在申請(qǐng)新的內(nèi)存空間针姿,也需要給予。
// 因此就直接調(diào)用get_empty_page()函數(shù)厌衙,申請(qǐng)一頁物理內(nèi)存并映射到指定線性地址處即可距淫。
// start_code 是進(jìn)程代碼段地址,end_data 是代碼加數(shù)據(jù)長(zhǎng)度婶希。對(duì)于linux 內(nèi)核榕暇,它的代碼段和
// 數(shù)據(jù)段是起始基址是相同的。
    if (!current->executable || tmp >= current->end_data) {
        get_empty_page(address);
        return;
    }
// 如果嘗試共享頁面成功喻杈,則退出彤枢。
    if (share_page(tmp))
        return;
// 取空閑頁面,如果內(nèi)存不夠了筒饰,則顯示內(nèi)存不夠缴啡,終止進(jìn)程。
    if (!(page = get_free_page()))
        oom();
/* 記住瓷们,(程序)頭要使用1 個(gè)數(shù)據(jù)塊 */
// 首先計(jì)算缺頁所在的數(shù)據(jù)塊項(xiàng)业栅。BLOCK_SIZE = 1024 字節(jié),因此一頁內(nèi)存需要4 個(gè)數(shù)據(jù)塊谬晕。
    block = 1 + tmp/BLOCK_SIZE;
// 根據(jù)i 節(jié)點(diǎn)信息碘裕,取數(shù)據(jù)塊在設(shè)備上的對(duì)應(yīng)的邏輯塊號(hào)。
    for (i=0 ; i<4 ; block++,i++)
        nr[i] = bmap(current->executable,block);
// 讀設(shè)備上一個(gè)頁面的數(shù)據(jù)(4 個(gè)邏輯塊)到指定物理地址page 處攒钳。
    bread_page(page,current->executable->i_dev,nr);
// 在增加了一頁內(nèi)存后帮孔,該頁內(nèi)存的部分可能會(huì)超過進(jìn)程的end_data 位置。下面的循環(huán)即是對(duì)物理
// 頁面超出的部分進(jìn)行清零處理不撑。
    i = tmp + 4096 - current->end_data;
    tmp = page + 4096;
    while (i-- > 0) {
        tmp--;
        *(char *)tmp = 0;
    }
// 如果把物理頁面映射到指定線性地址的操作成功文兢,就返回晤斩。否則就釋放內(nèi)存頁,顯示內(nèi)存不夠姆坚。
    if (put_page(page,address))
        return;
    free_page(page);
    oom();
}

//// 物理內(nèi)存初始化尸昧。
// 參數(shù):start_mem - 可用作分頁處理的物理內(nèi)存起始位置(已去除RAMDISK 所占內(nèi)存空間等)。
// end_mem - 實(shí)際物理內(nèi)存最大地址旷偿。
// 在該版的linux 內(nèi)核中,最多能使用16Mb 的內(nèi)存爆侣,大于16Mb 的內(nèi)存將不于考慮萍程,棄置不用。
// 0 - 1Mb 內(nèi)存空間用于內(nèi)核系統(tǒng)(其實(shí)是0-640Kb)兔仰。
void mem_init(long start_mem, long end_mem)
{
    int i;

    HIGH_MEMORY = end_mem;// 設(shè)置內(nèi)存最高端茫负。
    for (i=0 ; i<PAGING_PAGES ; i++)// 首先置所有頁面為已占用(USED=100)狀態(tài),
        mem_map[i] = USED;// 即將頁面映射數(shù)組全置成USED乎赴。
    i = MAP_NR(start_mem);// 然后計(jì)算可使用起始內(nèi)存的頁面號(hào)忍法。
    end_mem -= start_mem;// 再計(jì)算可分頁處理的內(nèi)存塊大小。
    end_mem >>= 12;// 從而計(jì)算出可用于分頁處理的頁面數(shù)榕吼。
    while (end_mem-->0)// 最后將這些可用頁面對(duì)應(yīng)的頁面映射數(shù)組清零饿序。
        mem_map[i++]=0;
}

// 計(jì)算內(nèi)存空閑頁面數(shù)并顯示。
void calc_mem(void)
{
    int i,j,k,free=0;
    long * pg_tbl;

// 掃描內(nèi)存頁面映射數(shù)組mem_map[]羹蚣,獲取空閑頁面數(shù)并顯示原探。
    for(i=0 ; i<PAGING_PAGES ; i++)
        if (!mem_map[i]) free++;
    printk("%d pages free (of %d)\n\r",free,PAGING_PAGES);
// 掃描所有頁目錄項(xiàng)(除0,1 項(xiàng))顽素,如果頁目錄項(xiàng)有效咽弦,則統(tǒng)計(jì)對(duì)應(yīng)頁表中有效頁面數(shù),并顯示胁出。
    for(i=2 ; i<1024 ; i++) {
        if (1&pg_dir[i]) {
            pg_tbl=(long *) (0xfffff000 & pg_dir[i]);
            for(j=k=0 ; j<1024 ; j++)
                if (pg_tbl[j]&1)
                    k++;
            printk("Pg-dir[%d] uses %d pages\n",i,k);
        }
    }
}


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末型型,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子全蝶,更是在濱河造成了極大的恐慌闹蒜,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裸诽,死亡現(xiàn)場(chǎng)離奇詭異嫂用,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)丈冬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門嘱函,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人埂蕊,你說我怎么就攤上這事往弓∈柰伲” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵函似,是天一觀的道長(zhǎng)槐脏。 經(jīng)常有香客問我,道長(zhǎng)撇寞,這世上最難降的妖魔是什么顿天? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蔑担,結(jié)果婚禮上牌废,老公的妹妹穿的比我還像新娘。我一直安慰自己啤握,他們只是感情好鸟缕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著排抬,像睡著了一般懂从。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蹲蒲,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天番甩,我揣著相機(jī)與錄音,去河邊找鬼悠鞍。 笑死对室,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咖祭。 我是一名探鬼主播掩宜,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼么翰!你這毒婦竟也來了牺汤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤浩嫌,失蹤者是張志新(化名)和其女友劉穎檐迟,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體码耐,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡追迟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了骚腥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敦间。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出廓块,到底是詐尸還是另有隱情厢绝,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布带猴,位于F島的核電站昔汉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拴清。R本人自食惡果不足惜靶病,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望口予。 院中可真熱鬧嫡秕,春花似錦、人聲如沸苹威。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽牙甫。三九已至,卻和暖如春调违,著一層夾襖步出監(jiān)牢的瞬間窟哺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工技肩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留且轨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓虚婿,卻偏偏與公主長(zhǎng)得像旋奢,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子然痊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 內(nèi)存是計(jì)算機(jī)非常關(guān)鍵的部件之一至朗,是暫時(shí)存儲(chǔ)程序以及數(shù)據(jù)的空間,CPU只有有限的寄存器可以用于 存儲(chǔ)計(jì)算數(shù)據(jù)剧浸,而大部...
    dreamer_lk閱讀 1,198評(píng)論 2 10
  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom閱讀 2,696評(píng)論 0 3
  • 經(jīng)典內(nèi)存異常:Out of Memory (OOM) Killer 我的內(nèi)存利用率為什么特別高锹引? Linux 內(nèi)存...
    RiboseYim閱讀 1,354評(píng)論 0 1
  • 借了一個(gè)親戚家的款,有兩年了唆香,雖然錢不是很多嫌变,但是一直沒有還的意思!為此躬它,讓我感覺到她不遵守承諾腾啥! 一.思維筆的空...
    悅辰4134閱讀 180評(píng)論 0 0
  • 補(bǔ)上前兩天落下的學(xué)習(xí)記錄,加上今天的學(xué)習(xí)總結(jié)。 老師講述了很多經(jīng)典的習(xí)題碑宴,如楊輝三角软啼、輸出菱形和倒三角...
    泡面小佳閱讀 107評(píng)論 0 1