kernel 內(nèi)存 I/O

內(nèi)存 I/O#

內(nèi)存管理單元##

MMU輔助操作系統(tǒng)進(jìn)行內(nèi)存管理踢步,提供虛擬地址和物理地址的映射峭沦、內(nèi)存訪問權(quán)限保護(hù)和cache緩存控制等硬件支持

TLB(translation lookaside buffer):轉(zhuǎn)換旁路緩存逃糟,TLB是MMU的核心部件,它緩存少量的虛擬地址與物理地址的轉(zhuǎn)換關(guān)系绰咽,是轉(zhuǎn)換表的cache

TTW(translation table walk):轉(zhuǎn)換表漫游,當(dāng)TLB中沒有緩沖對(duì)應(yīng)的地址轉(zhuǎn)換關(guān)系琐谤,需要通過對(duì)內(nèi)存轉(zhuǎn)換表的訪問來獲得

內(nèi)存管理##

內(nèi)核地址空間 劃分為 物理內(nèi)存映射區(qū)玩敏、虛擬內(nèi)存分配器區(qū)质礼、高端頁面映射區(qū)织阳、專用頁面映射區(qū)、系統(tǒng)保留映射區(qū)唧躲。

物理內(nèi)存映射區(qū):最大長度為896MB,系統(tǒng)的物理內(nèi)存被順序映射在內(nèi)核空間的這個(gè)區(qū)域中饭入。在低于16MB的區(qū)域肛真,ISA設(shè)備可以做DMA,DMA區(qū)域毁欣;16MB~896MB之間為常規(guī)區(qū)域。

高端頁面映射區(qū):當(dāng)系統(tǒng)物理內(nèi)存大于896MB時(shí)饭耳,超過物理內(nèi)存映射區(qū)的那部分內(nèi)存稱為高端內(nèi)存执解。

系統(tǒng)保留映射區(qū):linux保留內(nèi)核空間最頂部的區(qū)域作為保留區(qū)。

虛擬內(nèi)存分配區(qū):用于 vmalloc() 函數(shù)衰腌,它的前部與物理內(nèi)存映射區(qū)有一個(gè)隔離帶,后部與高端映射區(qū)也有一個(gè)隔離帶琼稻。

virt_to_phys()phy_to_virt() 僅適用于DMA和常規(guī)區(qū)域饶囚,高端內(nèi)存的虛擬地址與物理地址之間不存在如此簡(jiǎn)單的換算關(guān)系

buddy算法###

linux最底層的內(nèi)存申請(qǐng)都是以 2^n 為單位,避免外部碎片嘀掸,任何時(shí)候區(qū)域里的空閑內(nèi)存都能以2的n次方進(jìn)行拆分或合并

內(nèi)存存取##

linux內(nèi)核采用按需調(diào)頁规惰,因此當(dāng)malloc()成功返回,但是內(nèi)核并沒有真正給這個(gè)進(jìn)程內(nèi)存,這個(gè)時(shí)候如果去讀申請(qǐng)的內(nèi)存勋陪,內(nèi)容全為0文狱,這個(gè)頁面的映射是只讀的。只有當(dāng)寫到頁面時(shí)瞄崇,內(nèi)核才在頁錯(cuò)誤后,真正分配頁給進(jìn)程

kmalloc & __get_free_pages###

kmalloc()__get_free_pages() 申請(qǐng)的內(nèi)存位于DMA和常規(guī)區(qū)域的映射區(qū)等浊,物理上也是連續(xù)的摹蘑,與真實(shí)的物理地址只有一個(gè)固定偏移

  • GFP_KERNEL
    若暫時(shí)不能滿足,則進(jìn)程會(huì)睡眠等待頁衅鹿,引起阻塞大渤,因此不能在中斷上下文或持有自旋鎖的時(shí)候使用GFP_KERNEL

  • GFP_ATOMIC
    若不存在空閑頁制妄,則不等待泵三,直接返回

  • GFP_USER
    用來為用戶空間頁分配內(nèi)存,可能阻塞

  • GFP_HIGHUSER
    類似GFP_USER俺抽,但是從高端內(nèi)存分配

  • GFP_DMA
    從DMA區(qū)域分配內(nèi)存

  • __GFP_COLD
    請(qǐng)求一個(gè)較長時(shí)間不訪問的頁

  • __GFP_HIGH
    高優(yōu)先級(jí)請(qǐng)求较曼,允許獲得被內(nèi)核保留給緊急狀況使用的最后內(nèi)存頁

vmalloc###

vmalloc() 在虛擬內(nèi)存空間給出一塊連續(xù)內(nèi)存區(qū),實(shí)際物理內(nèi)存并不一定連續(xù)

vmalloc() 不能用在原子上下文中弛饭,因?yàn)閮?nèi)部實(shí)現(xiàn)標(biāo)志為GFP_KERNEL的 kmalloc()

slab機(jī)制###

建立于buddy算法之上伏恐,進(jìn)行二次管理栓霜。slab申請(qǐng)的內(nèi)存與物理內(nèi)存之間是一個(gè)簡(jiǎn)單的線性偏移

static kmem_cache_t *xxx_cachep;
xxx_cachep = kmem_cache_create("xxx", sizeof(struct xxx), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);

struct xxx *ctx;
ctx = lmem_cache_alloc(xxx_cachep, GFP_KERNEL);

...

kmem_cache_free(xxx_cachep, ctx);
kmem_cache_destroy(xxx_cachep);

IO##

IO內(nèi)存訪問流程##

request_mem_region() 申請(qǐng)資源
ioremap() 映射到內(nèi)核空間虛擬地址
readb()/readw()/readl()/writeb()
iounmap()
release_mem_region()

內(nèi)存映射###

remap_pfn_range() 創(chuàng)建頁表項(xiàng)

static int xxx_mmap(struct file *filp, struct vm_area_struct *vma)
{
    if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot))
        return -EAGAIN;
    
    vma->vm_ops = &xxx_remap_vm_ops;
    xxx_vma_open(vma);

    return 0;
}

static void xxx_vma_open(struct vm_area_struct *vma)
{
    ...
    printk("xxx VMA open, virt %lx, phys %lx\n", vma->vm_start, vma->vm_pgoff << PAGE_SHIFT);
}

static void xxx_vma_close(struct vm_area_struct *vma)
{
    ...
    printk("xxx VMA close.\n");
}

static struct vm_operations_struct xxx_remap_vm_ops = {
    .open = xxx_vma_open,
    .close = xxx_vma_close,
    ...
};

fault() 函數(shù)

  1. 找到缺頁的虛擬地址所在的VMA
  2. 如果必要,分配中間頁目錄表和頁表
  3. 如果頁表項(xiàng)對(duì)應(yīng)的物理頁面不存在销凑,則調(diào)用這個(gè)VMA的 fault() 方法
  4. 將物理頁面的地址填充到頁表中
static int xxx_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
    unsigned long paddr;
    unsigned long pfn;
    pgoff_t index = vmf->pgoff;
    struct vma_data *vdata = vma->vm_private_data;
    
    ...
    pfn = paddr >> PAGE_SHIFT;
    vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);

    return VM_FAULT_NOPAGE;    
}

對(duì)于顯示、視頻等設(shè)備澎蛛,建立映射可減少用戶空間和內(nèi)核空間之間的內(nèi)存復(fù)制

DMA##

內(nèi)存中用于與外設(shè)交互數(shù)據(jù)的一塊區(qū)域稱為DMA緩沖區(qū)蜕窿,在設(shè)備不支持scatter/gather操作的情況下,DMA緩沖區(qū)在物理上必須是連續(xù)的

  • 基于DMA的硬件使用的是總線地址而不是物理地址毁兆,總線地址是從設(shè)備角度看到的內(nèi)存地址
  • 物理地址 是從CPU MMU控制器外圍角度上看到的內(nèi)存地址
  • 虛擬地址 是CPU核角度看到的內(nèi)存地址

DMA 映射包括兩方面:
1. 分配DMA緩沖區(qū)
2. 為該緩沖區(qū)產(chǎn)生設(shè)備可訪問的總線地址

一致性DMA緩沖區(qū)###

dma_alloc_coherent() 申請(qǐng)一片DMA緩沖區(qū)阴挣,以進(jìn)行地址映射并保證該DMA緩沖區(qū)的cache一致性, dma_alloc_writecombine() 分配寫合并的DMA緩沖區(qū)

dma_free_coherent() 釋放DMA緩沖區(qū)(unmap)

流式DMA映射###

流式DMA映射操作在本質(zhì)上大多進(jìn)行cache的 flush或invalidate 畔咧,解決cache一致性問題
dma_map_single() dma_unmap_single()

通常情況下,設(shè)備驅(qū)動(dòng)不應(yīng)該訪問unmap的流式DMA緩沖區(qū)梅桩,如果一定要這么做蔽介,應(yīng)先獲得DMA緩沖區(qū)的擁有權(quán):
dma_sync_single_for_cpu()
在驅(qū)動(dòng)訪問完DMA緩沖區(qū)后,應(yīng)將其所有權(quán)返還給設(shè)備:
dma_sync_single_for_device()

如果設(shè)備要求較大的DMA緩沖區(qū)虹蓄,在其支持SG模式的情況下,申請(qǐng)多個(gè)相對(duì)較小的不連續(xù)DMA緩沖區(qū)通常是防止申請(qǐng)?zhí)蟮倪B續(xù)物理空間的方法
dma_map_sg() dma_unmap_sg()

SG映射屬于流式DMA映射外臂,如果設(shè)備驅(qū)動(dòng)一定要訪問映射情況下的SG緩沖區(qū)律胀,應(yīng)先獲得DMA緩沖區(qū)的擁有權(quán):
dma_sync_sg_for_cpu()
訪問完后,將所有權(quán)返回給設(shè)備:
dma_sync_sg_for_device()

dma engine API###

  • 申請(qǐng)DMA通道
    dma_request_slave_channel()

  • 獲取DMA描述符
    dmaengine_prep_slave_single()

  • 將描述符插入隊(duì)列
    dmaengine_submit()

  • 發(fā)起DMA操作
    dma_async_issue_pending()

  • 中斷返回罪佳,調(diào)用回調(diào)函數(shù)

  • 釋放通道
    dma_release_channel()

static void xxx_dma_fini_callback(void *data)
{
    struct completion *dma_complete = data;

    complete(dma_complete);
}

issue_xxx_dma(...)
{
    rx_desc = dmaengine_prep_slave_single(xxx->rx_chan, xxx->dst_start, t->len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    
    rx_desc->callback = xxx_dma_fini_callback;
    rx_desc->callback_param = &xxx->rx_done;

    dmaengine_submit(rx_desc);
    dma_async_issue_pending(xxx->rx_chan);
}

總結(jié)#

I/O內(nèi)存訪問流程一般為:
申請(qǐng)資源->映射->訪問->去映射->釋放資源

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末黑低,一起剝皮案震驚了整個(gè)濱河市酌毡,隨后出現(xiàn)的幾起案子蕾管,更是在濱河造成了極大的恐慌,老刑警劉巖旭蠕,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旷坦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡秒梅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門岗屏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漱办,“玉大人,你說我怎么就攤上這事暇屋。” “怎么了咐刨?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵扬霜,是天一觀的道長。 經(jīng)常有香客問我联予,道長材原,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任卷胯,我火速辦了婚禮,結(jié)果婚禮上窑睁,老公的妹妹穿的比我還像新娘。我一直安慰自己卵慰,他們只是感情好佛呻,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布吓著。 她就那樣靜靜地躺著,像睡著了一般绑莺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上纺裁,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天欺缘,我揣著相機(jī)與錄音,去河邊找鬼谚殊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛丛肢,可吹牛的內(nèi)容都是我干的蜂怎。 我是一名探鬼主播置尔,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼篮愉,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了试躏?” 一聲冷哼從身側(cè)響起设褐,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤泣刹,失蹤者是張志新(化名)和其女友劉穎犀被,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寡键,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡西轩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了马僻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡韭邓,死狀恐怖溶弟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情可很,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布苇本,位于F島的核電站菜拓,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏纳鼎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一劝贸、第九天 我趴在偏房一處隱蔽的房頂上張望逗宁。 院中可真熱鬧映九,春花似錦瞎颗、人聲如沸捌议。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽譬正。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間穿铆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工虐秦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凤优,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓筑辨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親暮现。 傳聞我的和親對(duì)象是個(gè)殘疾皇子楚昭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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