发新话题
打印

关于linux内存管理的疑问

关于linux内存管理的疑问

陈老师,你好
看了你的内核之旅中(http://www.kerneltravel.net/004.htm)电子杂志下的linux内存管理一文,有些疑问,希望能得到您的指点。
1、您给了一个虚拟字符设备的驱动程序,中间有一个把vmalloc分配的虚拟地址转换内核物理内存映射区域中的地址的函数vaddress_to_kaddress(),vmalloc分配的虚拟地址位于非连续内存分配区,其映射的页框有可能属于ZONE_HIGHMEM内存管理区,这样的话可能vmalloc分配的虚拟地址映射的物理页框在内核的物理内存映射区域中根本就没有对应的页表,那page_address(pte_page(pte))返回应该为NULL,如果计算机的物理内存小于896M的,那应该没有这个问题,不知道我理解正确没有
附:
volatile void *vaddr_to_kaddr(volatile void *address)
{
pgd_t *pgd;
pmd_t *pmd;
pte_t *ptep, pte;
unsigned long va, ret = 0UL;
va = (unsigned long)address;
/* get the page directory. Use the kernel memory map. */
pgd = pgd_offset_k(va);
/* check whether we found an entry */
if (!pgd_none(*pgd)) {
  /* get the page middle directory */
  pmd = pmd_offset(pgd, va);
  /* check whether we found an entry */
  if (!pmd_none(*pmd)) {
   /* get a pointer to the page table entry */
   ptep = pte_offset_kernel(pmd, va);
   pte = *ptep;
   /* check for a valid page */
   if (pte_present(pte)) {
    /* get the address the page is refering to */
    ret =
        (unsigned long)page_address(pte_page(pte));
    /* add the offset within the page to the page address */
    ret |= (va & (PAGE_SIZE - 1));
   }
  }
}
return ((volatile void *)ret);
}


2、

还是那篇文章中的一幅图
内核程序通过vmalloc分配空间后,它已经修改了主内核页表中的映射,也通过alloc_pages分配了页框,那就不会通过请页异常然后get_free_pages得到空闲页框了啊!这个内核程序指的是内核线程还是用户进程通过系统调用进入内核态运行的程序?这个内核程序它使用的是主内核页表还是用户进程页表?那这个虚拟字符设备驱动它属于内核线程不?
首先,你前面的理解是正确的。
其次,vaddr_to_kaddr()函数操作的实际上是内核页表。
另外,这个程序以模块形式加载到内核中的。既不是内核线程也不是系统调用。当用insmod命令插入模块时,就开始执行。
透析真谛,似拨云穿雾;共享智慧,如春风沐浴
http://www.kerneltravel.net
page_address()返回页框对应的线性地址,如果页框不在高端内存中,则通过_va((unsigned long)(page_map)<<12),可以得到其线性地址,也就是内核逻辑地址,但是如果页框在高端内存中,则查找page_address_htable散列表,如果找到就返回它的线性地址,但是函数virt_to_page()需要的是内核逻辑地址,如果页框在高端内存中,vaddr_to_kaddr()返回的是线性地址不是逻辑地址,所以virt_to_page就不能返回正确的页描述符,不知道理解正确不?

回复 #3 shaohaigod 的帖子

你的问题提的很好,理解也基本到位。这个例子没有考虑高端内存,你尝试改造这个程序,看一下对高端内存的处理如何,你也可以尝试一下,当在内核空间存放的数据量达到多少时,会造成系统崩溃。
透析真谛,似拨云穿雾;共享智慧,如春风沐浴
http://www.kerneltravel.net
请问陈老师,访问HIGHMEM是用ioremap()吗?我在一个arm的u-boot上用mem=112M启动的内核,0~112M内存应该是由page_init()建立页表。后面再用ioremap()映射HIGHMEM,并且在内核态重写这些地址。可是问题来了,如果映射112M~128M还可以,但是映射到128M以上就Oops了。我做了几次试验,发现可以成功映射的地址和启动内核的mem参数有关系。我的物理内存是256M的。请问,ioremap()有地址限制还是怎么回事?

回复 #5 muddle 的帖子

ioremap()最终调用的是__ioremap()函数,读这个函数的源代码,可以看到,在进行映射前,有几项检查,看看你的问题是否就落在了这个范围内?
/*
  25 * Generic mapping function (not visible outside):
  26 */
  27
  28/*
  29 * Remap an arbitrary physical address space into the kernel virtual
  30 * address space. Needed when the kernel wants to access high addresses
  31 * directly.
  32 *
  33 * NOTE! We need to allow non-page-aligned mappings too: we will obviously
  34 * have to convert them into an offset in a page-aligned mapping, but the
  35 * caller shouldn't need to know that small detail.
  36 */
  37void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)
  38{
  39        void __iomem * addr;
  40        struct vm_struct * area;
  41        unsigned long offset, last_addr;
  42        pgprot_t prot;
  43
  44        /* Don't allow wraparound or zero size */
  45        last_addr = phys_addr + size - 1;
  46        if (!size || last_addr < phys_addr)
  47                return NULL;
  48
  49        /*
  50         * Don't remap the low PCI/ISA area, it's always mapped..
  51         */
  52        if (phys_addr >= ISA_START_ADDRESS && last_addr < ISA_END_ADDRESS)
  53                return (void __iomem *) phys_to_virt(phys_addr);
  54
  55        /*
  56         * Don't allow anybody to remap normal RAM that we're using..
  57         */
  58        if (phys_addr <= virt_to_phys(high_memory - 1)) {
  59                char *t_addr, *t_end;
  60                struct page *page;
  61
  62                t_addr = __va(phys_addr);
  63                t_end = t_addr + (size - 1);
  64           
  65                for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++)
  66                        if(!PageReserved(page))
  67                                return NULL;
  68        }
  69
  70        prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY
  71                        | _PAGE_ACCESSED | flags);
  72
  73        /*
  74         * Mappings have to be page-aligned
  75         */
  76        offset = phys_addr & ~PAGE_MASK;
  77        phys_addr &= PAGE_MASK;
  78        size = PAGE_ALIGN(last_addr+1) - phys_addr;
  79
  80        /*
  81         * Ok, go for it..
  82         */
  83        area = get_vm_area(size, VM_IOREMAP | (flags << 20));
  84        if (!area)
  85                return NULL;
  86        area->phys_addr = phys_addr;
  87        addr = (void __iomem *) area->addr;
  88        if (ioremap_page_range((unsigned long) addr,
  89                        (unsigned long) addr + size, phys_addr, prot)) {
  90                vunmap((void __force *) addr);
  91                return NULL;
  92        }
  93        return (void __iomem *) (offset + (char __iomem *)addr);
  94}
  95EXPORT_SYMBOL(__ioremap);
透析真谛,似拨云穿雾;共享智慧,如春风沐浴
http://www.kerneltravel.net
发新话题