内核代码阅读(11) - ioremap

简介: ioremap

外部设备存储空间的地址映射 ioremap

将外设设备上的存储地址反向映射到内核的虚拟地址空间。
设备相关的,下面的 __ioremap 是i386的版本。
void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)
    {
        void * addr;
        struct vm_struct * area;
        unsigned long offset, last_addr;
        last_addr = phys_addr + size - 1;
        if (!size || last_addr < phys_addr)
                return NULL;
        if (phys_addr >= 0xA0000 && last_addr < 0x100000)
                return phys_to_virt(phys_addr);
        if (phys_addr < virt_to_phys(high_memory)) {
                char *t_addr, *t_end;
                struct page *page;
                t_addr = __va(phys_addr);
                t_end = t_addr + (size - 1);
                for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++)
                        if(!PageReserved(page))
                                return NULL;
        }
        offset = phys_addr & ~PAGE_MASK;
        phys_addr &= PAGE_MASK;
        size = PAGE_ALIGN(last_addr) - phys_addr;
        area = get_vm_area(size, VM_IOREMAP);
        if (!area)
                return NULL;
        addr = area->addr;
        if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) {
                vfree(addr);
                return NULL;
        }
        return (void *) (offset + (char *)addr);
    }
1) 首先是一些sanity check。
2) last_addr = phys_addr + size - 1;
   if (!size || last_addr < phys_addr)
           return NULL;
   检查映射区间大于0.
3) if (phys_addr >= 0xA0000 && last_addr < 0x100000)
           return phys_to_virt(phys_addr);
   VGA卡和BIOS的物理地址始终映射。无需ioremap.
4) if (phys_addr < virt_to_phys(high_memory))
   high_memory: 这个变量是在系统初始化的时候,检测到内存条最大的物理地址所对应的虚拟地址。
   phys_addr 小于最大的地址,说明phys_addr和系统的内存地址冲突了。
5) if(!PageReserved(page))
   如果地址冲突了,检查是否内核的虚拟地址空间留有空洞。
6) get_vm_area
   获取一段内核中的空的虚拟地址。
7)         if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags))

get_vm_area 内核获取空闲的虚拟地址空间

struct vm_struct * get_vm_area(unsigned long size, unsigned long flags)
    {
        unsigned long addr;
        struct vm_struct **p, *tmp, *area;
        area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
        if (!area)
                return NULL;
        size += PAGE_SIZE;
        addr = VMALLOC_START;
        write_lock(&vmlist_lock);
        for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
                if ((size + addr) < addr) {
                        write_unlock(&vmlist_lock);
                        kfree(area);
                        return NULL;
                }
                if (size + addr < (unsigned long) tmp->addr)
                        break;
                addr = tmp->size + (unsigned long) tmp->addr;
                if (addr > VMALLOC_END-size) {
                        write_unlock(&vmlist_lock);
                        kfree(area);
                        return NULL;
                }
        }
        area->flags = flags;
        area->addr = (void *)addr;
        area->size = size;
        area->next = *p;
        *p = area;
        write_unlock(&vmlist_lock);
        return area;
    }
1) struct vm_struct * vmlist;
   内核的为自己保留一个虚拟空间地址的队列。
   struct vm_struct {
    unsigned long flags;
    void * addr;
    unsigned long size;
    struct vm_struct * next;
   };
2) size += PAGE_SIZE;
   每一段虚存空间后面跟一个空洞。
3) addr = VMALLOC_START;
   内核需要的虚拟地址空间是从 high_memory-8MB处开始。

remap_area_pages 内核中的页式映射

static int remap_area_pages(unsigned long address, unsigned long phys_addr,
                                 unsigned long size, unsigned long flags)
    {
        pgd_t * dir;
        unsigned long end = address + size;
        phys_addr -= address;
        dir = pgd_offset(&init_mm, address);
        flush_cache_all();
        if (address >= end)
                BUG();
        do {
                pmd_t *pmd;
                pmd = pmd_alloc_kernel(dir, address);
                if (!pmd)
                        return -ENOMEM;
                if (remap_area_pmd(pmd, address, end - address,
                                         phys_addr + address, flags))
                        return -ENOMEM;
                address = (address + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
        } while (address && (address < end));
        flush_tlb_all();
        return 0;
    }
1) 内核中没有task_struct, 用init_mm描述内核的虚拟地址管理。
2) phys_addr -= address;
   在do-while循环中每次都要把物理地址的开始地址传进去。
3) remap_area_pmd
   逐级映射pmd, pte
相关文章
|
安全 测试技术 UED
专项测试
专项测试
498 0
|
开发者 iOS开发
iOS 源码分析(三):MLeaksFinder
iOS 源码分析(三):MLeaksFinder
897 0
iOS 源码分析(三):MLeaksFinder
|
算法 Java 程序员
内存回收
【10月更文挑战第9天】
473 5
在 ArkTS 中,如何有效地进行内存管理和避免内存泄漏?
【9月更文挑战第25天】在ArkTS中,有效进行内存管理并避免内存泄漏的方法包括:及时释放不再使用的资源,如关闭监听器和清理定时器;避免循环引用,通过弱引用打破循环;合理使用单例模式,确保单例对象正确释放;及时处理不再使用的页面和组件,在卸载时清理相关资源。
565 9
|
存储 算法 Linux
深入理解Linux虚拟内存管理(二)(下)
深入理解Linux虚拟内存管理(二)
383 0
|
存储 算法 Linux
深入理解Linux虚拟内存管理(二)(中)
深入理解Linux虚拟内存管理(二)
251 0
|
存储 机器学习/深度学习 缓存
【数据结构】布隆过滤器原理详解及其代码实现
【数据结构】布隆过滤器原理详解及其代码实现
|
存储 Linux Shell
【Linux】—— 进程的环境变量
【Linux】—— 进程的环境变量
549 0
【Linux】—— 进程的环境变量
|
机器学习/深度学习 JSON JavaScript
【JavaScript】JS 函数式编程入门指南:从概念到实践 (二)
【JavaScript】JS 函数式编程入门指南:从概念到实践 (二)
|
存储 机器学习/深度学习 Web App开发
布隆过滤器(BloomFilter)原理 实现和性能测试
可以看出,在同等数据量的情况下,BloomFilter的存储空间和ln(fpp)呈反比,所以增长速率其实不算快,即便误判率减少9个量级,其存储空间也只是增加了10倍。
283 0