Linux 內(nèi)存管理

Linux 內(nèi)存管理

1 頁的概念

linux 內(nèi)核中把物理頁作為內(nèi)存分配的最小單位,32位CPU 頁的大小通常為4K,64位的CPU通常支持8K的也。內(nèi)存管理單元MMU 同樣以頁為大小分配內(nèi)存。

2 內(nèi)核虛擬地址分區(qū)和物理內(nèi)存分區(qū)

在32位內(nèi)核中,內(nèi)核虛擬地址空間為0-4G,其中用戶態(tài)為1-3G空間,內(nèi)核態(tài)為3G-4G,內(nèi)核空間根據(jù)物理地址的特性大概可以分為三個(gè)區(qū):

區(qū) 描述 32位系統(tǒng)物理內(nèi)存大小
ZONE_DMA 和硬件操作相關(guān)的內(nèi)存區(qū)域 < 16M
ZONE_NORMAL 內(nèi)核正常映射的物理頁 16 - 896M
ZONE_HIGH 高端內(nèi)存,由于內(nèi)核空間大小的原理部分頁不能永久的映射到內(nèi)核,需要?jiǎng)討B(tài)映射的 > 896M

下面的圖描述了內(nèi)核地址空間和物理內(nèi)存的映射關(guān)系:


32位 內(nèi)核內(nèi)存分區(qū)
32位 內(nèi)核內(nèi)存分區(qū)

Linux 內(nèi)核啟動(dòng)后的mm 的初始化過程:

/*
 * Set up kernel memory allocators
 */
static void __init mm_init(void)
{
    /*
     * page_ext requires contiguous pages,
     * bigger than MAX_ORDER unless SPARSEMEM.
     */
    page_ext_init_flatmem();
    mem_init();
    kmem_cache_init();
    percpu_init_late();
    pgtable_init();
    vmalloc_init();
    ioremap_huge_init();
}

3伙伴系統(tǒng)算法

3.1 簡(jiǎn)介

在實(shí)際應(yīng)用中,經(jīng)常需要分配一組連續(xù)的頁,而頻繁地申請(qǐng)和釋放不同大小的連續(xù)頁,必然導(dǎo)致在已分配頁框的內(nèi)存塊中分散了許多小塊的空閑頁框。這樣,即使這些頁框是空閑的,其他需要分配連續(xù)頁框的應(yīng)用也很難得到滿足。為了避免出現(xiàn)這種情況,Linux內(nèi)核中引入了伙伴系統(tǒng)算法(buddy system)。把所有的空閑頁框分組為11個(gè)塊鏈表,每個(gè)塊鏈表分別包含大小為1,2,4,8,16,32,64,128,256,512和1024個(gè)連續(xù)頁框的頁框塊。最大可以申請(qǐng)1024個(gè)連續(xù)頁框,對(duì)應(yīng)4MB大小的連續(xù)內(nèi)存。每個(gè)頁框塊的第一個(gè)頁框的物理地址是該塊大小的整數(shù)倍。

假設(shè)要申請(qǐng)一個(gè)256個(gè)頁框的塊,先從256個(gè)頁框的鏈表中查找空閑塊,如果沒有,就去512個(gè)頁框的鏈表中找,找到了則將頁框塊分為2個(gè)256個(gè)頁框的塊,一個(gè)分配給應(yīng)用,另外一個(gè)移到256個(gè)頁框的鏈表中。如果512個(gè)頁框的鏈表中仍沒有空閑塊,繼續(xù)向1024個(gè)頁框的鏈表查找,如果仍然沒有,則返回錯(cuò)誤。頁框塊在釋放時(shí),會(huì)主動(dòng)將兩個(gè)連續(xù)的頁框塊合并為一個(gè)較大的頁框塊。

mem_init() 函數(shù)中會(huì)把內(nèi)核啟動(dòng)后的空閑內(nèi)存用buddy 系統(tǒng)管理。

參考:mem_init bootmem 遷移至伙伴系統(tǒng)

3.2 伙伴系統(tǒng)算法分配函數(shù)

mem_init 初始化完伙伴系統(tǒng)后通過 alloc_page(s) 函數(shù)分配伙伴系統(tǒng)內(nèi)存池的內(nèi)存。

函數(shù) 描述
struct page * alloc_page(unsigned int gfp_mask) 分配一頁物理內(nèi)存并返回該頁物理內(nèi)存的page結(jié)構(gòu)指針
struct page * alloc_pages(unsigned int gfp_mask, unsigned int order) 分配2的order次方連續(xù)的物理頁并返回分配的第一個(gè)物理頁的page結(jié)構(gòu)指針
unsigned long get_free_page(unsigned int gfp_mask) 只分配一頁,返回頁的邏輯地址
unsigned long __get_free_pages(unsigned int gfp_mask, unsigned int order) 分配 2的order頁,返回也是邏輯地址

3.3 get_free_page(s)與alloc_page(s)的差異

alloc_page alloc_pages 分配后還不能直接使用, 需要得到該頁對(duì)應(yīng)的虛擬地址

  • void *page_address(struct page *page);
  • 低端內(nèi)存的映射方式:__va((unsigned long)(page - mem_map) << 12)
  • 高端內(nèi)存到映射方式:struct page_address_map分配一個(gè)動(dòng)態(tài)結(jié)構(gòu)來管理高端內(nèi)存。(內(nèi)核是訪問不到vma的3G以下的虛擬地址的) 具體映射由kmap / kmap_atomic執(zhí)行。

get_free_page(s)與alloc_page(s)系列最大的區(qū)別是無法申請(qǐng)高端內(nèi)存,因?yàn)樗祷氐绞且粋€(gè)邏輯地址,而高端內(nèi)存是需要額外映射才可以

Android x86 的buffyinfo.
以Normal區(qū)域進(jìn)行分析,第二列值為459,表示當(dāng)前系統(tǒng)中normal區(qū)域,可用的連續(xù)兩頁的內(nèi)存大小為459*2^1*PAGE_SIZE;第三列值為52,表示當(dāng)前系統(tǒng)中normal區(qū)域,可用的連續(xù)四頁的內(nèi)存大小為52*2^2*PAGE_SIZE

generic_x86:/ # cat /proc/buddyinfo
Node 0, zone      DMA      4      1      2      2      3      2      3      1      2      0      1
Node 0, zone   Normal   1186    459    220    142     25     13      2      0      1      2    138
Node 0, zone  HighMem     87     74     12      9      0      1      1      0      0      0      0

4 Slab 內(nèi)存分配算法

Slab 內(nèi)存分配算法 和Java中的對(duì)象池是一個(gè)概念。采用buddy算法,解決了外碎片問題,這種方法適合大塊內(nèi)存請(qǐng)求,不適合小內(nèi)存區(qū)請(qǐng)求

4.1 Slab 內(nèi)存分配算法

slab分配器源于 Solaris 2.4 的分配算法,工作于物理內(nèi)存頁框分配器之上,管理特定大小對(duì)象的緩存,進(jìn)行快速而高效的內(nèi)存分配。slab分配器為每種使用的內(nèi)核對(duì)象建立單獨(dú)的緩沖區(qū)。Linux 內(nèi)核已經(jīng)采用了伙伴系統(tǒng)管理物理內(nèi)存頁框,因此 slab分配器直接工作于伙伴系統(tǒng)之上。每種緩沖區(qū)由多個(gè) slab 組成,每個(gè) slab就是一組連續(xù)的物理內(nèi)存頁框,被劃分成了固定數(shù)目的對(duì)象。根據(jù)對(duì)象大小的不同,缺省情況下一個(gè) slab 最多可以由 1024個(gè)頁框構(gòu)成。出于對(duì)齊等其它方面的要求,slab 中分配給對(duì)象的內(nèi)存可能大于用戶要求的對(duì)象實(shí)際大小,這會(huì)造成一定的內(nèi)存浪費(fèi)。

Linux 所使用的 slab 分配器的基礎(chǔ)是 Jeff Bonwick 為SunOS 操作系統(tǒng)首次引入的一種算法。Jeff的分配器是圍繞對(duì)象緩存進(jìn)行的。在內(nèi)核中,會(huì)為有限的對(duì)象集(例如文件描述符和其他常見結(jié)構(gòu))分配大量?jī)?nèi)存。Jeff發(fā)現(xiàn)對(duì)內(nèi)核中普通對(duì)象進(jìn)行初始化所需的時(shí)間超過了對(duì)其進(jìn)行分配和釋放所需的時(shí)間。因此他的結(jié)論是不應(yīng)該將內(nèi)存釋放回一個(gè)全局的內(nèi)存池,而是將內(nèi)存保持為針對(duì)特定目而初始化的狀態(tài)。例如,如果內(nèi)存被分配給了一個(gè)互斥鎖,那么只需在為互斥鎖首次分配內(nèi)存時(shí)執(zhí)行一次互斥鎖初始化函數(shù)(mutex_init)即可。后續(xù)的內(nèi)存分配不需要執(zhí)行這個(gè)初始化函數(shù),因?yàn)閺纳洗吾尫藕驼{(diào)用析構(gòu)之后,它已經(jīng)處于所需的狀態(tài)中了。

4.2 Slab 內(nèi)存結(jié)構(gòu)

kmem_cache_alloc 分配的所有的內(nèi)存塊在內(nèi)核中以鏈表的形式組織。
kmem_cache_alloc 從buddy系統(tǒng)分配到內(nèi)存后,在內(nèi)部被分為 slab 單元,這是一段連續(xù)的內(nèi)存塊(通常都是頁面)。所有的對(duì)象都分配在這些slab 單元上,這些slab 單元被組織為三個(gè)鏈表:

  1. slabs_full 完全分配的 slab
  2. slabs_partial 部分分配的 slab
  3. slabs_free 可以回收的 slab

4.3 slab 著色區(qū)和slab 結(jié)構(gòu)

每個(gè)Slab的首部都有一個(gè)小小的區(qū)域是不用的,稱為“著色區(qū)(coloring area)”。著色區(qū)的大小使Slab中的每個(gè)對(duì)象的起始地址都按高速緩存中的”緩存行(cache line)”大小進(jìn)行對(duì)齊(80386的一級(jí)高速緩存行大小為16字節(jié),Pentium為32字節(jié))。因?yàn)镾lab是由1個(gè)頁面或多個(gè)頁面(最多為32)組成,因此,每個(gè)Slab都是從一個(gè)頁面邊界開始的,它自然按高速緩存的緩沖行對(duì)齊。但是,Slab中的對(duì)象大小不確定,設(shè)置著色區(qū)的目的就是將Slab中第一個(gè)對(duì)象的起始地址往后推到與緩沖行對(duì)齊的位置。每個(gè)Slab上最后一個(gè)對(duì)象以后也有個(gè)小小的區(qū)是不用的,這是對(duì)著色區(qū)大小的補(bǔ)償,其大小取決于著色區(qū)的大小,以及Slab與其每個(gè)對(duì)象的相對(duì)大小。

slab 內(nèi)存分配
slab 內(nèi)存分配

4.4 Slab 內(nèi)存函數(shù)

mm_init --> kmem_cache_init(); kernel 初始化

  • kmem_cache_t* xx_cache; // 鏈表頭
  • 創(chuàng)建: xx_cache = kmem_cache_create("name", sizeof(struct xx), SLAB_HWCACHE_ALIGN, NULL, NULL);
  • 分配: kmem_cache_alloc(xx_cache, GFP_KERNEL);
  • 釋放: kmem_cache_free(xx_cache, addr);
    slab 內(nèi)存用結(jié)構(gòu)體 kmem_cache_t 表示:

4.5 slabinfo對(duì)象

從 /proc/slabinfo 中看一看出,內(nèi)核為大結(jié)構(gòu)體使用了slab 緩存。如ext4_inode_cache vm_area_struct task_struct等。

generic_x86:/ # cat /proc/slabinfo
slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
...
ext4_inode_cache    2025   2025    632   25    4 : tunables    0    0    0 : slabdata     81     81      0
ext4_allocation_context    156    156    104   39    1 : tunables    0    0    0 : slabdata      4      4      0
ext4_prealloc_space    224    224     72   56    1 : tunables    0    0    0 : slabdata      4      4      0
ext4_io_end          408    408     40  102    1 : tunables    0    0    0 : slabdata      4      4      0
ext4_extent_status   2048   2048     32  128    1 : tunables    0    0    0 : slabdata     16     16      0
...
vm_area_struct     20791  22402     88   46    1 : tunables    0    0    0 : slabdata    487    487      0
mm_struct             85     85    480   17    2 : tunables    0    0    0 : slabdata      5      5      0

...
task_struct          621    621   1184   27    8 : tunables    0    0    0 : slabdata     23     23      0
...
kmalloc-8192          28     28   8192    4    8 : tunables    0    0    0 : slabdata      7      7      0
kmalloc-4096          96    104   4096    8    8 : tunables    0    0    0 : slabdata     13     13      0
kmalloc-2048         128    128   2048   16    8 : tunables    0    0    0 : slabdata      8      8      0
kmalloc-1024         336    336   1024   16    4 : tunables    0    0    0 : slabdata     21     21      0
kmalloc-512          752    752    512   16    2 : tunables    0    0    0 : slabdata     47     47      0
kmalloc-256          698    752    256   16    1 : tunables    0    0    0 : slabdata     47     47      0
kmalloc-192          903    903    192   21    1 : tunables    0    0    0 : slabdata     43     43      0
kmalloc-128         1760   1760    128   32    1 : tunables    0    0    0 : slabdata     55     55      0
kmalloc-96          2100   2100     96   42    1 : tunables    0    0    0 : slabdata     50     50      0
kmalloc-64         14272  14272     64   64    1 : tunables    0    0    0 : slabdata    223    223      0
kmalloc-32         26182  28416     32  128    1 : tunables    0    0    0 : slabdata    222    222      0
kmalloc-16         15360  15360     16  256    1 : tunables    0    0    0 : slabdata     60     60      0
kmalloc-8           6656   6656      8  512    1 : tunables    0    0    0 : slabdata     13     13      0
kmem_cache_node      128    128     32  128    1 : tunables    0    0    0 : slabdata      1      1      0
kmem_cache           128    128    128   32    1 : tunables    0    0    0 : slabdata      4      4      0

5 kmalloc 和 vmalloc

5.1 kmalloc

從 4.5 節(jié) /proc/slabinfo 對(duì)象也可以看出,kmalloc 的分配建立在 slab 內(nèi)存對(duì)象池上。
在mm/slab_common.c 中 kmalloc 的分配定義如下:

// mm/slab_common.c
static struct {
    const char *name;
    unsigned long size;
} const kmalloc_info[] __initconst = {
    {NULL,                      0},     {"kmalloc-96",             96},
    {"kmalloc-192",           192},     {"kmalloc-8",               8},
    {"kmalloc-16",             16},     {"kmalloc-32",             32},
    {"kmalloc-64",             64},     {"kmalloc-128",           128},
    {"kmalloc-256",           256},     {"kmalloc-512",           512},
    {"kmalloc-1024",         1024},     {"kmalloc-2048",         2048},
    {"kmalloc-4096",         4096},     {"kmalloc-8192",         8192},
    {"kmalloc-16384",       16384},     {"kmalloc-32768",       32768},
    {"kmalloc-65536",       65536},     {"kmalloc-131072",     131072},
    {"kmalloc-262144",     262144},     {"kmalloc-524288",     524288},
    {"kmalloc-1048576",   1048576},     {"kmalloc-2097152",   2097152},
    {"kmalloc-4194304",   4194304},     {"kmalloc-8388608",   8388608},
    {"kmalloc-16777216", 16777216},     {"kmalloc-33554432", 33554432},
    {"kmalloc-67108864", 67108864}
};

kmalloc 獲取的是以字節(jié)為單位的連續(xù)物理內(nèi)存空間

5.2 gfp_t 結(jié)構(gòu)體

// include/linux/slab.h 
void *kmalloc(size_t size, gfp_t flags)

在 alloc_page(s) get_free_page(s) kmalloc 函數(shù)的定義中 第二個(gè)參數(shù)類型為 gfp_t 類型;
gfp_t 標(biāo)志有3類:(所有的 GFP 標(biāo)志都在 <linux/gfp.h> 中定義)

  1. 行為標(biāo)志 :控制分配內(nèi)存時(shí),分配器的一些行為
  2. 區(qū)標(biāo)志 :控制內(nèi)存分配在那個(gè)區(qū)(ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM 之類)
  3. 類型標(biāo)志 :由上面2種標(biāo)志組合而成的一些常用的場(chǎng)景

區(qū)標(biāo)志主要以下3種:

區(qū)域 描述
__GFP_DMA 從 ZONE_DMA 分配
__GFP_DMA32 只在 ZONE_DMA32 分配
__GFP_HIGHMEM 從 ZONE_HIGHMEM 或者 ZONE_NORMAL 分配

__GFP_HIGHMEM 優(yōu)先從 ZONE_HIGHMEM 分配,如果 ZONE_HIGHMEM 沒有多余的頁則從 ZONE_NORMAL 分配

5.3 vmalloc

vmalloc 分配的內(nèi)存和kmalloc 不同,vmalloc 在邏輯地址上是連續(xù)的,但是在物理地質(zhì)上不一定連續(xù)。

/**
 *  vmalloc  -  allocate virtually contiguous memory
 *  @size:      allocation size
 *  Allocate enough pages to cover @size from the page level
 *  allocator and map them into contiguous kernel virtual space.
 *
 *  For tight control over page level allocator and protection flags
 *  use __vmalloc() instead.
 */
void *vmalloc(unsigned long size)
{
    return __vmalloc_node_flags(size, NUMA_NO_NODE,
                    GFP_KERNEL | __GFP_HIGHMEM);
}


static inline void *__vmalloc_node_flags(unsigned long size,
                    int node, gfp_t flags)
{
    return __vmalloc_node(size, 1, flags, PAGE_KERNEL,
                    node, __builtin_return_address(0));
}


void *__vmalloc_node_range(unsigned long size, unsigned long align,
            unsigned long start, unsigned long end, gfp_t gfp_mask,
            pgprot_t prot, unsigned long vm_flags, int node,
            const void *caller)
{
    struct vm_struct *area;
    void *addr;
    unsigned long real_size = size;

    size = PAGE_ALIGN(size);
    if (!size || (size >> PAGE_SHIFT) > totalram_pages)
        goto fail;

    area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNINITIALIZED |
                vm_flags, start, end, node, gfp_mask, caller);
    if (!area)
        goto fail;

    addr = __vmalloc_area_node(area, gfp_mask, prot, node);
    if (!addr)
        return NULL;

    ......
}

static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
                 pgprot_t prot, int node)
{
    struct page **pages;
    unsigned int nr_pages, array_size, i;
    const gfp_t nested_gfp = (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO;
    const gfp_t alloc_mask = gfp_mask | __GFP_NOWARN;

    nr_pages = get_vm_area_size(area) >> PAGE_SHIFT;
    array_size = (nr_pages * sizeof(struct page *));

    area->nr_pages = nr_pages;
    /* Please note that the recursion is strictly bounded. */
    if (array_size > PAGE_SIZE) {
        pages = __vmalloc_node(array_size, 1, nested_gfp|__GFP_HIGHMEM,
                PAGE_KERNEL, node, area->caller);
    } else {
        pages = kmalloc_node(array_size, nested_gfp, node);
    }
    
    area->pages = pages;
    if (!area->pages) {
        remove_vm_area(area->addr);
        kfree(area);
        return NULL;
    }

    for (i = 0; i < area->nr_pages; i++) {
        struct page *page;

        if (node == NUMA_NO_NODE)
            page = alloc_page(alloc_mask);
        else
            page = alloc_pages_node(node, alloc_mask, 0);

        if (unlikely(!page)) {
            /* Successfully allocated i pages, free them in __vunmap() */
            area->nr_pages = i;
            goto fail;
        }
        area->pages[i] = page;
        if (gfpflags_allow_blocking(gfp_mask))
            cond_resched();
    }
    ......

從vmalloc 函數(shù)的實(shí)現(xiàn)看 最終調(diào)用了alloc_page 系列函數(shù)實(shí)現(xiàn) 從伙伴分配系統(tǒng)中分配內(nèi)存。所以所vmalloc 適用了大塊非物理連續(xù)的內(nèi)存分配。 __vmalloc_node_flags(size, NUMA_NO_NODE, GFP_KERNEL | __GFP_HIGHMEM) 函數(shù)中vmalloc 指定了從高端內(nèi)存分配。

linux 內(nèi)存分配結(jié)構(gòu)
linux 內(nèi)存分配結(jié)構(gòu)

6 malloc

6.1 程序在內(nèi)存中的地址

二進(jìn)制程序通常分為text, Data, Bss, 區(qū), 堆和棧。加載到內(nèi)存后的內(nèi)存鏡像如圖所示:

程序在內(nèi)存中的地址

==圖片來源于網(wǎng)絡(luò)==

6.2 sbrk 系統(tǒng)調(diào)用 和 “program break" (程序間斷點(diǎn))

程序間斷點(diǎn)在最開始指向堆區(qū)的起始位置,同時(shí)也是數(shù)據(jù)段的結(jié)尾。 malloc 分配內(nèi)存后,指向分配的內(nèi)存開始的位置。

linux 系統(tǒng)上malloc 的實(shí)現(xiàn)基于sbrk 系統(tǒng)調(diào)用。

p1 = sbrk(0);               //sbrk(0)返回當(dāng)前的程序間斷點(diǎn)
p = sbrk(1)                 //將堆區(qū)的大小加1,但是返回的是p1的位置

參考
如何實(shí)現(xiàn)一個(gè)malloc

內(nèi)存的頁映射

malloc 調(diào)用后,只是分配了內(nèi)存的邏輯地址,在內(nèi)核的mm_struct 鏈表中插入vm_area_struct結(jié)構(gòu)體,沒有分配實(shí)際的內(nèi)存。當(dāng)分配的區(qū)域?qū)懭霐?shù)據(jù)是,引發(fā)頁中斷,建立物理頁和邏輯地址的映射。下圖表示了這個(gè)過程。

物理頁的分配過程

在Android 上通過procrank 查看 Vss 和 Rss, Rss 總是小于Vss 就是這個(gè)原因。

generic_x86_64:/ # procrank
 PID       Vss      Rss      Pss      Uss  cmdline
1509  1077592K  117132K   66232K   57296K  system_server
1237   901952K   66596K   56300K   52884K  zygote
1623  1061168K   98892K   50847K   44164K  com.android.systemui
1236   916248K   78992K   29529K   20532K  zygote64
1780  1020240K   63484K   20138K   15684K  com.android.phone
2004  1014992K   66748K   20112K   14748K  com.android.launcher3
字段 含義
VSS Virtual Set Size 虛擬耗用內(nèi)存(包含共享庫占用的內(nèi)存)
RSS Resident Set Size 實(shí)際使用物理內(nèi)存(包含共享庫占用的內(nèi)存)
PSS Proportional Set Size 實(shí)際使用的物理內(nèi)存(比例分配共享庫占用的內(nèi)存)
USS Unique Set Size 進(jìn)程獨(dú)自占用的物理內(nèi)存(不包含共享庫占用的內(nèi)存)

一般來說內(nèi)存占用大小有如下規(guī)律:VSS >= RSS >= PSS >= USS
參考:
How the Kernel Manages Your Memory

部分內(nèi)容來源于網(wǎng)絡(luò),沒有一一注明

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 用戶空間 通常 32 位 Linux 虛擬地址空間劃分, 0-3GB為用戶空間,3GB-4GB為內(nèi)核空間。每...
    Fly_Li閱讀 1,936評(píng)論 0 5
  • 第八章 內(nèi)存管理 本章通過三部分內(nèi)容描述內(nèi)核給自己動(dòng)態(tài)分配內(nèi)存: ...
    rlkbk閱讀 523評(píng)論 0 1
  • 最近開始想稍微深入一點(diǎn)地學(xué)習(xí)Linux內(nèi)核,主要參考內(nèi)容是《深入理解Linux內(nèi)核》和《深入理解Linux內(nèi)核架構(gòu)...
    ice_camel閱讀 1,923評(píng)論 0 2
  • >計(jì)算機(jī)系統(tǒng)中有幾類存儲(chǔ)設(shè)備:cache、內(nèi)存、外存。cache的存取速度最高,可以和CPU匹配,因此其代價(jià)最高,...
    一生信仰閱讀 1,273評(píng)論 0 0
  • 目錄 薛曉燕沒有回家,始終沒回家。我以為到過年她就會(huì)回家的,可她的同學(xué)許薇薇告訴我,薛曉燕退學(xué)后沒有回過家。我找遍...
    dd仔閱讀 344評(píng)論 0 3

友情鏈接更多精彩內(nèi)容