http://eduunix.jlbtc.edu.cn/index/html/linux/OReilly.Understanding.the.Linux.Kernel.3rd.Edition.Nov.2005.HAPPY.NEW.YEAR/0596005652/understandlk-CHP-8-SECT-3.html#understandlk-CHP-8-SECT-38.3.3. Allocating a Noncontiguous Memory AreaThe vmalloc( ) function allocates a noncontiguous memory area to the kernel. The parameter size denotes the size of the requested area. If the function is able to satisfy the request, it then returns the initial linear address of the new area; otherwise, it returns a NULL pointer: void * vmalloc(unsigned long size) { struct vm_struct *area; struct page **pages; unsigned int array_size, i; size = (size + PAGE_SIZE - 1) & PAGE_MASK; area = get_vm_area(size, VM_ALLOC); if (!area) return NULL; area->nr_pages = size >> PAGE_SHIFT; array_size = (area->nr_pages * sizeof(struct page *)); area->pages = pages = kmalloc(array_size, GFP_KERNEL); if (!area_pages) { remove_vm_area(area->addr); kfree(area); return NULL; } memset(area->pages, 0, array_size); for (i=0; i<area->nr_pages; i++) { area->pages[i] = alloc_page(GFP_KERNEL|_ _GFP_HIGHMEM); if (!area->pages[i]) { area->nr_pages = i; fail: vfree(area->addr); return NULL; } } if (map_vm_area(area, _ _pgprot(0x63), &pages)) goto fail; return area->addr; } The function starts by rounding up the value of the size parameter to a multiple of 4,096 (the page frame size). Then vmalloc( ) invokes get_vm_area( ), which creates a new descriptor and returns the linear addresses assigned to the memory area. The flags field of the descriptor is initialized with the VM_ALLOC flag, which means that the noncontiguous page frames will be mapped into a linear address range by means of the vmalloc( ) function. Then the vmalloc( ) function invokes kmalloc( ) to request a group of contiguous page frames large enough to contain an array of page descriptor pointers. The memset( ) function is invoked to set all these pointers to NULL. Next the alloc_page( ) function is called repeatedly, once for each of the nr_pages of the region, to allocate a page frame and store the address of the corresponding page descriptor in the area->pages array. Observe that using the area->pages array is necessary because the page frames could belong to the ZONE_HIGHMEM memory zone, thus right now they are not necessarily mapped to a linear address. Now comes the tricky part. Up to this point, a fresh interval of contiguous linear addresses has been obtained and a group of noncontiguous page frames has been allocated to map these linear addresses. The last crucial step consists of fiddling with the page table entries used by the kernel to indicate that each page frame allocated to the noncontiguous memory area is now associated with a linear address included in the interval of contiguous linear addresses yielded by vmalloc( ). This is what map_vm_area( ) does. |