kernel 内存 I/O

内存 I/O#

内存管理单元##

MMU辅助操作系统进行内存管理,提供虚拟地址和物理地址的映射、内存访问权限保护和cache缓存控制等硬件支持

TLB(translation lookaside buffer):转换旁路缓存,TLB是MMU的核心部件,它缓存少量的虚拟地址与物理地址的转换关系,是转换表的cache

TTW(translation table walk):转换表漫游,当TLB中没有缓冲对应的地址转换关系,需要通过对内存转换表的访问来获得

内存管理##

内核地址空间 划分为 物理内存映射区、虚拟内存分配器区、高端页面映射区、专用页面映射区、系统保留映射区。

物理内存映射区:最大长度为896MB,系统的物理内存被顺序映射在内核空间的这个区域中。在低于16MB的区域,ISA设备可以做DMA,DMA区域;16MB~896MB之间为常规区域。

高端页面映射区:当系统物理内存大于896MB时,超过物理内存映射区的那部分内存称为高端内存。

系统保留映射区:linux保留内核空间最顶部的区域作为保留区。

虚拟内存分配区:用于 vmalloc() 函数,它的前部与物理内存映射区有一个隔离带,后部与高端映射区也有一个隔离带。

virt_to_phys()phy_to_virt() 仅适用于DMA和常规区域,高端内存的虚拟地址与物理地址之间不存在如此简单的换算关系

buddy算法###

linux最底层的内存申请都是以 2^n 为单位,避免外部碎片,任何时候区域里的空闲内存都能以2的n次方进行拆分或合并

内存存取##

linux内核采用按需调页,因此当malloc()成功返回,但是内核并没有真正给这个进程内存,这个时候如果去读申请的内存,内容全为0,这个页面的映射是只读的。只有当写到页面时,内核才在页错误后,真正分配页给进程

kmalloc & __get_free_pages###

kmalloc()__get_free_pages() 申请的内存位于DMA和常规区域的映射区,物理上也是连续的,与真实的物理地址只有一个固定偏移

  • GFP_KERNEL
    若暂时不能满足,则进程会睡眠等待页,引起阻塞,因此不能在中断上下文或持有自旋锁的时候使用GFP_KERNEL

  • GFP_ATOMIC
    若不存在空闲页,则不等待,直接返回

  • GFP_USER
    用来为用户空间页分配内存,可能阻塞

  • GFP_HIGHUSER
    类似GFP_USER,但是从高端内存分配

  • GFP_DMA
    从DMA区域分配内存

  • __GFP_COLD
    请求一个较长时间不访问的页

  • __GFP_HIGH
    高优先级请求,允许获得被内核保留给紧急状况使用的最后内存页

vmalloc###

vmalloc() 在虚拟内存空间给出一块连续内存区,实际物理内存并不一定连续

vmalloc() 不能用在原子上下文中,因为内部实现标志为GFP_KERNEL的 kmalloc()

slab机制###

建立于buddy算法之上,进行二次管理。slab申请的内存与物理内存之间是一个简单的线性偏移

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内存访问流程##

request_mem_region() 申请资源
ioremap() 映射到内核空间虚拟地址
readb()/readw()/readl()/writeb()
iounmap()
release_mem_region()

内存映射###

remap_pfn_range() 创建页表项

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() 函数

  1. 找到缺页的虚拟地址所在的VMA
  2. 如果必要,分配中间页目录表和页表
  3. 如果页表项对应的物理页面不存在,则调用这个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;    
}

对于显示、视频等设备,建立映射可减少用户空间和内核空间之间的内存复制

DMA##

内存中用于与外设交互数据的一块区域称为DMA缓冲区,在设备不支持scatter/gather操作的情况下,DMA缓冲区在物理上必须是连续的

  • 基于DMA的硬件使用的是总线地址而不是物理地址,总线地址是从设备角度看到的内存地址
  • 物理地址 是从CPU MMU控制器外围角度上看到的内存地址
  • 虚拟地址 是CPU核角度看到的内存地址

DMA 映射包括两方面:
1. 分配DMA缓冲区
2. 为该缓冲区产生设备可访问的总线地址

一致性DMA缓冲区###

dma_alloc_coherent() 申请一片DMA缓冲区,以进行地址映射并保证该DMA缓冲区的cache一致性, dma_alloc_writecombine() 分配写合并的DMA缓冲区

dma_free_coherent() 释放DMA缓冲区(unmap)

流式DMA映射###

流式DMA映射操作在本质上大多进行cache的 flush或invalidate ,解决cache一致性问题
dma_map_single() dma_unmap_single()

通常情况下,设备驱动不应该访问unmap的流式DMA缓冲区,如果一定要这么做,应先获得DMA缓冲区的拥有权:
dma_sync_single_for_cpu()
在驱动访问完DMA缓冲区后,应将其所有权返还给设备:
dma_sync_single_for_device()

如果设备要求较大的DMA缓冲区,在其支持SG模式的情况下,申请多个相对较小的不连续DMA缓冲区通常是防止申请太大的连续物理空间的方法
dma_map_sg() dma_unmap_sg()

SG映射属于流式DMA映射,如果设备驱动一定要访问映射情况下的SG缓冲区,应先获得DMA缓冲区的拥有权:
dma_sync_sg_for_cpu()
访问完后,将所有权返回给设备:
dma_sync_sg_for_device()

dma engine API###

  • 申请DMA通道
    dma_request_slave_channel()

  • 获取DMA描述符
    dmaengine_prep_slave_single()

  • 将描述符插入队列
    dmaengine_submit()

  • 发起DMA操作
    dma_async_issue_pending()

  • 中断返回,调用回调函数

  • 释放通道
    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);
}

总结#

I/O内存访问流程一般为:
申请资源->映射->访问->去映射->释放资源

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容