免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1812 | 回复: 0
打印 上一主题 下一主题

LINUX系统调用mlock的代码分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2004-03-06 22:36 |只看该作者 |倒序浏览
一、        LINUX内存管理简介
1.1.LINUX的分页管理机制
在LINUX中,每一个用户进程都可以访问4GB的线性虚拟内存空间。其中从0到3GB的虚拟内存地址是用户空间,用户进程可以直接对其进行访问。从3GB到4GB的虚拟内存地址为核心空间,存放仅供核心态访问的代码和数据,用户进程不可访问。当用户进程通过中断或系统调用对其访问时,就会触发处理器的特权级转换(从处理器的特权级3切换到特权级0),即从操作系统的用户态切换到核心态。
所有进程从3GB到4GB的虚拟空间映像都是一样的,LINUX以此方式让核心态进程共享代码段和数据段。
核心态虚拟空间从3GB到3GB+4M的一段(也就是进程页目录第768项所管辖的范围),被影射到物理空间0到4M段。因此,进程处于核心态时,只要通过访问虚拟空间3GB到3GB+4M段,即访问了物理空间0到4M段。
上述两种空间对用户进程来说都是透明的,用户进程所访问的内存地址都是连续的4GB线性虚拟地址。因此,我们首先关心的是LINUX是如何划分虚拟空间的。
LINUX采用“按需调页”(Demand Paging)技术管理虚拟内存。标准LINUX的虚存页表应为三级页表,依次为页目录(PGD,Page Directory)、中间页目录(PMD,Page Middle Directory)和页表(PTE,Page Table)。如图1-1所示。






            PGD            PMD            PTE         Page Frame

图1-1,LINUX的三级页表结构

在INTEL微机上,LINUX的页表结构实际为两级。80386体系结构之页管理机制中的页目录就是PGD,页表就是PTE。而PMD和PGD实际上是合二为一的。所有有关PMD的操作实际上是对PGD的操作。所以源代码中形如*_pgd_*()和*_pmd_*()的函数所实现的功能也是一样的。其实现方法是提供一组转换宏(/include/asm/pgtable.h),使得转换页表时不需要知道页表的入口格式。宏定义形如:

#define _PAGE_4M        0x080        /* 4 MB page */
/* PMD_SHIFT determines the size of the area a second-level page table can map */
#define PMD_SHIFT        22  
#define PMD_SIZE        (1UL<<MD_SHIFT)        /*PMD页表大小为4G=1024*4K
#define PMD_MASK        (~(PMD_SIZE-1))

/* PGDIR_SHIFT determines what a third-level page table entry can map */
#define PGDIR_SHIFT        22
#define PGDIR_SIZE        (1UL << PGDIR_SHIFT)  
#define PGDIR_MASK        (~(PGDIR_SIZE-1))
#define PTRS_PER_PTE        1024
#define PTRS_PER_PMD        1
#define PTRS_PER_PGD        1024

extern inline pmd_t * pmd_alloc_kernel(pgd_t * pgd, unsigned long address)
{
        return (pmd_t *) pgd;
}
extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address)
{
        return (pmd_t *) pgd;
}

每当启动一个新进程,LINUX都为其分配一个task_struct结构,内含saved_kernel_stack、kernel_stack_page、ldt、tss、mm等内存管理信息,详见进程管理部分。其中,task_struct结构内嵌mm_struct结构,此结构包含了用户进程中与存储有关的信息。
进程控制块中有关存储管理的数据结构有如下五项:
1.        unsigned long saved_kernel_stack:在虚拟86方式下,用于保存核心堆栈的地址;
2.        unsigned long kernel_stack_page:每个进程都要分配一个页框用作核心堆栈;
3.        struct desc_struct *ldt:进程局部描述符表描述符的指针;
4.        struct thread_struct tss:进程的任务状态段;
5.        struct mm_struct mm[1]:进程用于存储管理的数据都存放于此。如图1-2所示:

此外,每一个进程都有一个页目录,存储该进程所使用的内存页面情况。LINUX按照“按需调页”的原则只分配必需的内存页面,从而避免了页表过多占用存储空间(最坏情况下多达4MB)的情况出现。例如,系统调用fork分配内存页面的情况如下:
l        进程控制块(task_struct结构)1页
l        核心态堆栈1页
l        页目录1页
l        页表占几页(视需要)

系统调用exec分配内存页面的情况如下:
l        可执行文件的文件头1页
l        用户堆栈1页或几页(视需要)

这样,当进程开始运行时,如果执行代码不在内存中,将产生第一次缺页中断,让操作系统分配内存页面,并将执行代码装入内存中。此后,按需要逐渐分配更多的内存页面,并参与页面调度。当系统内存不足时,由操作系统决定是否将该进程的一部分页面换出到磁盘交换区或交换文件中。进程终止时,操作系统释放所有该进程占用的资源,包括内存页面。
1.2.虚存段(vma)的组织和管理
用户共有4GB的虚存空间,但并不是所有的4GB空间用户都可以读写或申请使用。用户实际可申请的虚存空间为0至3GB。在用户进程创建时,已由系统调用fork()将核心的代码段和数据段映射到3GB以后的虚存空间。
所有进程的3GB至4GB的虚存空间的映象都是相同的,以此方式共享核心的代码段和数据段。
        为了便于管理,LINUX 将进程虚存空间以虚存段(vma,即virtual memory area)进行管理。这在后面的分析系统调用mlock的数据结构时会详细介绍。
1.3 内存的共享和保护


图1-3,LINUX页共享结构

有些UNIX的内存共享以页表共享的形式实现:设立一临时的公共页表,各进程原本指向共享页表的表项改为指向公共页表的表项,公共页表的表项再指向共享页的地址。这种结构执行效率高:当共享页状态发生变化时,只需修改公共页表的表项即可,只有一次页表的访问操作。
LINUX中内存共享却以页共享的形式实现,共享该页的各进程的页表表项直接指向共享页,见图1-3。这种结构不需设立共享页表,节约内存的占用;但效率较低,当共享页状态发生变化时,共享该页的各进程的页表共享该页的各进程的页表均需修改,要多次访问页表。
LINUX可对虚存段中任一部分加锁或保护(mm/mprotect.c,mm/mlock.c)。虚存段加锁、保护操作可以有四种方式(图1-4):
l        对整个虚存段加锁或保护
l        对虚存段前部加锁或保护
l        对虚存段后部加锁或保护
l        对虚存段中部加锁或保护

图1-4

后三种方式下,需将原vma段分割成两个或三个。分割后的vma段经调用insert_vm_struct()插入vma段的AVL树和链表中。为了减少多余的vma结构,还需调用merge_segment()(mm/mmap.c),尽量合并相邻的vma段。
保护操作的函数调用层次如图1-5,加锁操作的函数调用层次如图1-6。



图1-5



图1-6
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP