- 在栈上静态分配
当给每个进程分配一个固定大小的内核栈,不但可以减少内存的消耗,而且内核也无需负担太重的栈管理任务。
每个进程的内核栈大小既取决于体系结构,也与编译时的选项有关。历史上,每个进程都有两页的内核栈,在2.6系列内核早期,引入了一个选项可以设置单页内核栈。当这个选项激活时,每个进程的内核栈只有一页这么大。这么做出于两个原因:
首先,可以让每个进程减少内存消耗;更重要的是,随着运行时间的增加,寻找两个未分配的、连续的页变得越来越困难。还有一个更复杂的原因。每个进程的整个调用链必须放在自己的内核栈中,中断程序也曾经使用它们所中断的进程的内核栈。当把栈移到只有一页的内核栈时,中断处理程序就不放在栈中了。因此实现了一个附加选项:中断栈。中断栈为每个进程提供一个用于中断处理程序的栈。中断程序就不再和被中断的进程共享一个内核栈,他们可以使用自己的栈。
历史上,中断处理程序和被中断进程共享一个栈。当1页栈的选项被激活时,中断处理程序获得了自己的栈。
在栈上静态分配
在具体的函数中让所有局部变量(即所谓的自动变量)所占空间之和不要超过几百字节。不要在内核栈上进行大量的静态分配,比如分配大型数组和大型数据结构。栈溢出发生时悄无声息,势必会引起严重的问题,因为内核没有在管理内核栈上做足工作,因此,当溢出时,多出的数据就会覆盖掉紧邻栈末端的东西,首先就是thread_info结构。在堆栈之外,任何内核数据都可能存在潜在的危险。
因此,进行动态分配是一种明智的选择。
- 高端内存的映射
根据定义,在高端内存中的页不能永久地映射到内核地址空间上,因此,通过alloc_pages()函数以__GFP_HIGHMEM标志获得的页不可能有逻辑地址。
在x86体系结构上,高于896MB的所有物理内存的范围大都是高端内存,它并不会永久地或自动地映射到内核地址空间,尽管x86处理器能够寻址物理地址RAM的范围达到4GB(启用PAE可以寻址到64GB)。一旦这些页被分配,就必须映射到内核的逻辑地址空间上。在x86上,高端内存中的页被映射到3GB和4GB之间。
1.永久映射
要映射一个给定的page结构到内核地址空间,可以使用kmap()函数,这个函数在高端内存或低端内存上都能用:
- 在<Highmem.h(include/linux)>中
- #ifndef ARCH_HAS_KMAP
- static inline void *kmap(struct page *page)
- {
- might_sleep();
- return page_address(page);
- }
- 在<Highmem.c(arch/i386/mm)>
- void *kmap(struct page *page)
- {
- might_sleep();
- if (!PageHighMem(page))
- return page_address(page);
- return kmap_high(page);
- }
- 在<Page-flags.h(include/linux)>中
- #ifdef CONFIG_HIGHMEM
- #define PageHighMem(page) is_highmem(page_zone(page))
- #else
- #define PageHighMem(page) 0 /* needed to optimize away at compile time */
- #endif
如果page结构对应的是低端内存中的一页,函数只会单纯地返回该页的虚拟地址。如果页位于高端内存,则会建立一个永久映射,再返回地址。这个函数可以睡眠,因此只能用在进程上下文中。
以你此允许永久映射的数量是有限的,当不再需要高端内存时,应该解除映射,使用kunmap()函数:
- 在<Highmem.h(include/linux)>中
- #ifndef ARCH_HAS_KMAP
- #define kunmap(page) do { (void) (page); } while (0)
- 在<Highmem.c(arch/i386/mm)>
- void kunmap(struct page *page)
- {
- if (in_interrupt())
- BUG();
- if (!PageHighMem(page))
- return;
- kunmap_high(page);
- }
2. 临时映射
当必须创建一个映射而当前的上下文又不能睡眠时,内核提供了临时映射(也就是原子映射)。有一组保留的映射中,它们可以存放新创建的临时映射。内核可以原子地把高端内存中的一个页映射到某个保留的映射中。因此,临时映射可以用在不能睡眠的地方,比如中断处理程序。
可以通过函数kmap_atomic()建立一个临时映射,这个函数不会阻塞,因此可以用在中断上下文和其他不能重新调度的地方。它还能禁止抢占,这是必要的,因为映射对每个处理器都是唯一的(调度可能对哪个处理器执行哪个进程做变动)。可以使用kunmap_atomic()函数取消映射:
- 在<Highmem.h(include/linux)>中
- #ifndef ARCH_HAS_KMAP
- #define kmap_atomic(page, idx) /
- ({ pagefault_disable(); page_address(page); })
- #define kunmap_atomic(addr, idx) do { pagefault_enable(); } while (0)
- 在<Highmem.c(arch/i386/mm)>
- /*
- * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because
- * no global lock is needed and because the kmap code must perform a global TLB
- * invalidation when the kmap pool wraps.
- *
- * However when holding an atomic kmap is is not legal to sleep, so atomic
- * kmaps are appropriate for short, tight code paths only.
- */
- void *kmap_atomic(struct page *page, enum km_type type)
- {
- enum fixed_addresses idx;
- unsigned long vaddr;
- /* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */
- pagefault_disable();
- idx = type + KM_TYPE_NR*smp_processor_id();
- BUG_ON(!pte_none(*(kmap_pte-idx)));
- if (!PageHighMem(page))
- return page_address(page);
- vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
- set_pte(kmap_pte-idx, mk_pte(page, kmap_prot));
- arch_flush_lazy_mmu_mode();
- return (void*) vaddr;
- }
- void kunmap_atomic(void *kvaddr, enum km_type type)
- {
- unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
- enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id();
- /*
- * Force other mappings to Oops if they'll try to access this pte
- * without first remap it. Keeping stale mappings around is a bad idea
- * also, in case the page changes cacheability attributes or becomes
- * a protected page in a hypervisor.
- */
- if (vaddr == __fix_to_virt(FIX_KMAP_BEGIN+idx))
- kpte_clear_flush(kmap_pte-idx, vaddr);
- else {
- #ifdef CONFIG_DEBUG_HIGHMEM
- BUG_ON(vaddr < PAGE_OFFSET);
- BUG_ON(vaddr >= (unsigned long)high_memory);
- #endif
- }
- pagefault_enable();
- }
- 在<Kmap_types.h(include/asm-i386)>中
- enum km_type {
- D(0) KM_BOUNCE_READ,
- D(1) KM_SKB_SUNRPC_DATA,
- D(2) KM_SKB_DATA_SOFTIRQ,
- D(3) KM_USER0,
- D(4) KM_USER1,
- D(5) KM_BIO_SRC_IRQ,
- D(6) KM_BIO_DST_IRQ,
- D(7) KM_PTE0,
- D(8) KM_PTE1,
- D(9) KM_IRQ0,
- D(10) KM_IRQ1,
- D(11) KM_SOFTIRQ0,
- D(12) KM_SOFTIRQ1,
- D(13) KM_TYPE_NR
- };