- 论坛徽章:
- 0
|
二、 mlock系统调用简介
本组分析的系统调用包括mlock、munlock、mlockall、munlockall(分别对应sys_mlock、sys_munlock、sys_mlockall、sys_munlockall四个函数)四个有关内存加锁的系统调用。这四个系统调用代码基本上类似的,所以本组每个人(本组共四个人)分别取其中一个系统调用作为各自代码分析的重点,而本人分析的重点是系统调用mlock。
本文主要分析在LINUX中系统调用mlock的处理机制,包括初始化、处理过程、结束等。
2.1 mlock介绍
系统调用mlock的作用是屏蔽内存中某些用户进程所要求的页。
mlock调用的语法为:
int sys_mlock(unsigned long start, size_t len);
初始化为:
len=(len+(start &~PAGE_MASK)+ ~PAGE_MASK)& AGE_MASK;
start &=PAGE_MASK;
其中mlock又调用do_mlock(),语法为:
int do_mlock(unsigned long start, size_t len,int on);
初始化为:
len=(len+~PAGE_MASK)& AGE_MASK;
由mlock的参数可看出,mlock对由start所在页的起始地址开始,长度为len(注:len=(len+(start&~PAGE_MASK)+ ~PAGE_MASK)& AGE_MASK)的内存区域的页进行加锁。start和len的初始设置有下面两种情况(图2-1,图2-2):
(图2-1 第一种情况:start相对页内偏移量+len<page_size)
(图2-2 第二种情况:start相对页内偏移量+len>;page_size)
sys_mlock如果调用成功返回,这其中所有的包含具体内存区域的页必须是常驻内存的,或者说在调用munlock 或 munlockall之前这部分被锁住的页面必须保留在内存。当然,如果调用mlock的进程终止或者调用exec执行其他程序,则这部分被锁住的页面被释放。通过fork()调用所创建的子进程不能够继承由父进程调用mlock锁住的页面。
内存屏蔽主要有两个方面的应用:实时算法和高度机密数据的处理。实时应用要求严格的分时,比如调度,调度页面是程序执行延时的一个主要因素。保密安全软件经常处理关键字节,比如密码或者密钥等数据结构。页面调度的结果是有可能将这些重要字节写到外存(如硬盘)中去。这样一些黑客就有可能在这些安全软件删除这些在内存中的数据后还能访问部分在硬盘中的数据。 而对内存进行加锁完全可以解决上述难题。
内存加锁不使用压栈技术,即那些通过调用mlock或者mlockall被锁住多次的页面可以通过调用一次munlock或者munlockall释放相应的页面
mlock的返回值分析:若调用mlock成功,则返回0;若不成功,则返回-1,并且errno被置位,进程的地址空间保持原来的状态。返回错误代码分析如下:
ENOMEM:部分具体地址区域没有相应的进程地址空间与之对应或者超出了进程所允许的最大可锁页面。
EPERM:调用mlock的进程没有正确的优先权。只有root进程才允许锁住要求的页面。
EINVAL:输入参数len不是个合法的正数。
2.2 mlock所用到的主要数据结构和重要常量
1.mm_struct
struct mm_struct {
int count;
pgd_t * pgd; /* 进程页目录的起始地址,如图2-3所示 */
unsigned long context;
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack, start_mmap;
unsigned long arg_start, arg_end, env_start, env_end;
unsigned long rss, total_vm, locked_vm;
unsigned long def_flags;
struct vm_area_struct * mmap; /* 指向vma双向链表的指针 */
struct vm_area_struct * mmap_avl; /* 指向vma AVL树的指针 */
struct semaphore mmap_sem;
}
l start_code、end_code:进程代码段的起始地址和结束地址。
l start_data、end_data:进程数据段的起始地址和结束地址。
l arg_start、arg_end:调用参数区的起始地址和结束地址。
l env_start、env_end:进程环境区的起始地址和结束地址。
l rss:进程内容驻留在物理内存的页面总数。
图2-3,进程的虚存管理数据结构
2. 虚存段(vma)数据结构:vm_area_atruct
虚存段vma由数据结构vm_area_atruct(include/linux/mm.h)描述:
struct vm_area_struct {
struct mm_struct * vm_mm; /* VM area parameters */
unsigned long vm_start;
unsigned long vm_end;
pgprot_t vm_page_prot;
unsigned short vm_flags;
/* AVL tree of VM areas per task, sorted by address */
short vm_avl_height;
struct vm_area_struct * vm_avl_left;
struct vm_area_struct * vm_avl_right;
/* linked list of VM areas per task, sorted by address */
struct vm_area_struct * vm_next;
/* for areas with inode, the circular list inode->;i_mmap */
/* for shm areas, the circular list of attaches */
/* otherwise unused */
struct vm_area_struct * vm_next_share;
struct vm_area_struct * vm_prev_share;
/* more */
struct vm_operations_struct * vm_ops;
unsigned long vm_offset;
struct inode * vm_inode;
unsigned long vm_pte; /* shared mem */
};
图 2-4: vm_area_struct所对应的虚存区域(Areas of Virtual Memory)
l vm_start;//所对应内存区域的开始地址
l vm_end; //所对应内存区域的结束地址
l vm_flags; //进程对所对应内存区域的访问权限
l vm_avl_height;//avl树的高度
l vm_avl_left; //avl树的左儿子
l vm_avl_right; //avl树的右儿子
l vm_next;// 进程所使用的按地址排序的vm_area链表指针
l vm_ops;//一组对内存的操作
这些对内存的操作是当对虚存进行操作的时候Linux系统必须使用的一组方法。比如说,当进程准备访问某一虚存区域但是发现此区域在物理内存不存在时(缺页中断),就激发某种对内存的操作执行正确的行为。这种操作是空页(nopage)操作。当Linux系统按需调度可执行的页面映象进入内存时就使用这种空页(nopage)操作。
当一个可执行的页面映象映射到进程的虚存地址时,一组vm_area_struct结构的数据结构(vma)就会生成。每一个vm_area_struct的数据结构(vma)代表可执行的页面映象的一部分:可执行代码,初始化数据(变量),非初始化数据等等。Linux系统可以支持大量的标准虚存操作,当vm_area_struct数据结构(vma)一被创建,它就对应于一组正确的虚存操作。
属于同一进程的vma段通过vm_next指针连接,组成链表。如图2-3所示,struct mm_struct结构的成员struct vm_area_struct * mmap 表示进程的vma链表的表头。
为了提高对vma段 查询、插入、删除操作的速度,LINUX同时维护了一个AVL(Adelson-Velskii and Landis)树。在树中,所有的vm_area_struct虚存段均有左指针vm_avl_left指向相邻的低地址虚存段,右指针vm_avl_right指向相邻的高地址虚存段,如图2-5。struct mm_struct结构的成员struct vm_area_struct * mmap_avl表示进程的AVL树的根,vm_avl_height表示AVL树的高度。
对平衡树mmap_avl的任何操作必须满足平衡树的一些规则:
Consistency and balancing rulesJ(一致性和平衡规则):
l tree->;vm_avl_height==1+max(heightof(tree->;vm_avl_left),heightof(
tree->;vm_avl_right))
l abs( heightof(tree->;vm_avl_left) - heightof(tree->;vm_avl_right) ) <= 1
l foreach node in tree->;vm_avl_left: node->;vm_avl_key <= tree->;vm_avl_key, foreach node in tree->;vm_avl_right: node->;vm_avl_key >;= tree->;vm_avl_key.
注:其中node->;vm_avl_key= node->;vm_end
对vma可以进行加锁、加保护、共享和动态扩展等操作。
vm_area_struct
图2-5
例如,虚存段的动态扩展和移动可以通过函数sys_remap()来实现(mm/remap.c)。其调用层次如下:
图2-6
3.重要常量
mlock系统调用所用到的重要常量有:PAGE_MASK、PAGE_SIZE、PAGE_SHIFT、RLIMIT_MEMLOCK、VM_LOCKED、 PF_SUPERPRIV等。它们的值分别如下:
PAGE_SHIFT 12 // PAGE_SHIFT determines the page size
PAGE_SIZE 0x1000 //1UL<< AGE_SHIFT
PAGE_MASK ~(PAGE_SIZE-1) //a very useful constant variable
RLIMIT_MEMLOCK 8 //max locked-in-memory address space
VM_LOCKED 0x2000 //8*1024=8192, vm_flags的标志之一。
PF_SUPERPRIV 0x00000100 //512, |
|