内存管理[6]

 
  • 在栈上静态分配

   当给每个进程分配一个固定大小的内核栈,不但可以减少内存的消耗,而且内核也无需负担太重的栈管理任务。

   每个进程的内核栈大小既取决于体系结构,也与编译时的选项有关。历史上,每个进程都有两页的内核栈,在2.6系列内核早期,引入了一个选项可以设置单页内核栈。当这个选项激活时,每个进程的内核栈只有一页这么大。这么做出于两个原因:

首先,可以让每个进程减少内存消耗;更重要的是,随着运行时间的增加,寻找两个未分配的、连续的页变得越来越困难。还有一个更复杂的原因。每个进程的整个调用链必须放在自己的内核栈中,中断程序也曾经使用它们所中断的进程的内核栈。当把栈移到只有一页的内核栈时,中断处理程序就不放在栈中了。因此实现了一个附加选项:中断栈。中断栈为每个进程提供一个用于中断处理程序的栈。中断程序就不再和被中断的进程共享一个内核栈,他们可以使用自己的栈。

   历史上,中断处理程序和被中断进程共享一个栈。当1页栈的选项被激活时,中断处理程序获得了自己的栈。

在栈上静态分配

  在具体的函数中让所有局部变量(即所谓的自动变量)所占空间之和不要超过几百字节。不要在内核栈上进行大量的静态分配,比如分配大型数组和大型数据结构。栈溢出发生时悄无声息,势必会引起严重的问题,因为内核没有在管理内核栈上做足工作,因此,当溢出时,多出的数据就会覆盖掉紧邻栈末端的东西,首先就是thread_info结构。在堆栈之外,任何内核数据都可能存在潜在的危险。

   因此,进行动态分配是一种明智的选择。

  • 高端内存的映射

  根据定义,在高端内存中的页不能永久地映射到内核地址空间上,因此,通过alloc_pages()函数以__GFP_HIGHMEM标志获得的页不可能有逻辑地址。

  在x86体系结构上,高于896MB的所有物理内存的范围大都是高端内存,它并不会永久地或自动地映射到内核地址空间,尽管x86处理器能够寻址物理地址RAM的范围达到4GB(启用PAE可以寻址到64GB)。一旦这些页被分配,就必须映射到内核的逻辑地址空间上。在x86上,高端内存中的页被映射到3GB和4GB之间。

 

1.永久映射

  要映射一个给定的page结构到内核地址空间,可以使用kmap()函数,这个函数在高端内存或低端内存上都能用:

 

  1. 在<Highmem.h(include/linux)>中
  2. #ifndef ARCH_HAS_KMAP
  3. static inline void *kmap(struct page *page)
  4. {
  5.     might_sleep();
  6.     return page_address(page);
  7. }
  1. 在<Highmem.c(arch/i386/mm)>
  2. void *kmap(struct page *page)
  3. {
  4.     might_sleep();
  5.     if (!PageHighMem(page))
  6.         return page_address(page);
  7.     return kmap_high(page);
  8. }
  9. 在<Page-flags.h(include/linux)>中 
  10. #ifdef CONFIG_HIGHMEM 
  11. #define PageHighMem(page)   is_highmem(page_zone(page)) 
  12. #else 
  13. #define PageHighMem(page)   0 /* needed to optimize away at compile time */ 
  14. #endif

  如果page结构对应的是低端内存中的一页,函数只会单纯地返回该页的虚拟地址。如果页位于高端内存,则会建立一个永久映射,再返回地址。这个函数可以睡眠,因此只能用在进程上下文中。

  以你此允许永久映射的数量是有限的,当不再需要高端内存时,应该解除映射,使用kunmap()函数:

  1. 在<Highmem.h(include/linux)>中 
  2. #ifndef ARCH_HAS_KMAP 
  3. #define kunmap(page) do { (void) (page); } while (0)
  1. 在<Highmem.c(arch/i386/mm)>
  2. void kunmap(struct page *page)
  3. {
  4.     if (in_interrupt())
  5.         BUG();
  6.     if (!PageHighMem(page))
  7.         return;
  8.     kunmap_high(page);
  9. }

2. 临时映射

  当必须创建一个映射而当前的上下文又不能睡眠时,内核提供了临时映射(也就是原子映射)。有一组保留的映射中,它们可以存放新创建的临时映射。内核可以原子地把高端内存中的一个页映射到某个保留的映射中。因此,临时映射可以用在不能睡眠的地方,比如中断处理程序。

  可以通过函数kmap_atomic()建立一个临时映射,这个函数不会阻塞,因此可以用在中断上下文和其他不能重新调度的地方。它还能禁止抢占,这是必要的,因为映射对每个处理器都是唯一的(调度可能对哪个处理器执行哪个进程做变动)。可以使用kunmap_atomic()函数取消映射:

  1. 在<Highmem.h(include/linux)>中 
  2. #ifndef ARCH_HAS_KMAP 
  3. #define kmap_atomic(page, idx) /
  4.     ({ pagefault_disable(); page_address(page); })
  5. #define kunmap_atomic(addr, idx)    do { pagefault_enable(); } while (0)
  1. 在<Highmem.c(arch/i386/mm)>
  2. /*
  3.  * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because
  4.  * no global lock is needed and because the kmap code must perform a global TLB
  5.  * invalidation when the kmap pool wraps.
  6.  *
  7.  * However when holding an atomic kmap is is not legal to sleep, so atomic
  8.  * kmaps are appropriate for short, tight code paths only.
  9.  */
  10. void *kmap_atomic(struct page *page, enum km_type type)
  11. {
  12.     enum fixed_addresses idx;
  13.     unsigned long vaddr;
  14.     /* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */
  15.     pagefault_disable();
  16.     idx = type + KM_TYPE_NR*smp_processor_id();
  17.     BUG_ON(!pte_none(*(kmap_pte-idx)));
  18.     if (!PageHighMem(page))
  19.         return page_address(page);
  20.     vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
  21.     set_pte(kmap_pte-idx, mk_pte(page, kmap_prot));
  22.     arch_flush_lazy_mmu_mode();
  23.     return (void*) vaddr;
  24. }
  25. void kunmap_atomic(void *kvaddr, enum km_type type)
  26. {
  27.     unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
  28.     enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id();
  29.     /*
  30.      * Force other mappings to Oops if they'll try to access this pte
  31.      * without first remap it.  Keeping stale mappings around is a bad idea
  32.      * also, in case the page changes cacheability attributes or becomes
  33.      * a protected page in a hypervisor.
  34.      */
  35.     if (vaddr == __fix_to_virt(FIX_KMAP_BEGIN+idx))
  36.         kpte_clear_flush(kmap_pte-idx, vaddr);
  37.     else {
  38. #ifdef CONFIG_DEBUG_HIGHMEM
  39.         BUG_ON(vaddr < PAGE_OFFSET);
  40.         BUG_ON(vaddr >= (unsigned long)high_memory);
  41. #endif
  42.     }
  43.     pagefault_enable();
  44. }
  1. 在<Kmap_types.h(include/asm-i386)>中
  2. enum km_type {
  3. D(0)    KM_BOUNCE_READ,
  4. D(1)    KM_SKB_SUNRPC_DATA,
  5. D(2)    KM_SKB_DATA_SOFTIRQ,
  6. D(3)    KM_USER0,
  7. D(4)    KM_USER1,
  8. D(5)    KM_BIO_SRC_IRQ,
  9. D(6)    KM_BIO_DST_IRQ,
  10. D(7)    KM_PTE0,
  11. D(8)    KM_PTE1,
  12. D(9)    KM_IRQ0,
  13. D(10)   KM_IRQ1,
  14. D(11)   KM_SOFTIRQ0,
  15. D(12)   KM_SOFTIRQ1,
  16. D(13)   KM_TYPE_NR
  17. };
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值