pbmpay3zt
9d08d04802
|
2 years ago | |
---|---|---|
README.md | 2 years ago | |
fork.c | 2 years ago | |
memory.c | 2 years ago | |
memory1.c | 2 years ago | |
memory2.c | 2 years ago | |
munmap.c | 2 years ago | |
munmap2.c | 2 years ago | |
sched.h | 2 years ago | |
system_call.s | 2 years ago |
README.md
mmap函数实验
函数的参数介绍:
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
long mmap()
prot 页面的权限标志
flags 页面私有/共享的标志
off :文件的偏移量,从文件的哪一部分开始映射到页面中
vmarea_struct 结构
因为涉及虚拟地址的管理,所以定义了如下结构:
struct vmarea_struct{
unsigned long vm_start;//虚拟地址开始的地址
unsigned long vm_end;//虚拟地址结束的地址
int mode;//页面权限
int flag;//页面是否共享
int size;//大小 需要与PAGE_SIZE对齐
off_t off;//映射文件的偏移量
struct vmarea_struct *next;//下一个vmarea_struct,链表结构比较简便,会牺牲一些速度
//struct file *dir;//要映射文件的结构
int fd;//映射文件的文件描述符
};
在task_struct加入该结构
struct task_struct {
..............
struct vmarea_struct *mmap;//映射虚拟地址的链表
};
在INIT_TASK需要进行进行初始化
#define INIT_TASK \
......
/*mmap*/ NULL,\
}
mmap是将文件的一部分内容映射到进程地址空间之中,原理如下图:
step1:在当前进程的虚拟地址空间中,寻找一段空闲的连续的地址空间(寻址方式)
step2:寻找并将空闲页映射到对应的虚拟地址空间之中(get_free_page和put_page),并根据flags判断是否将该页面映射到其他进程对应的虚拟内存中(参考了try_to_page函数的实现)
step3:将文件内容写到空闲页之中(参考了sys_read和find_entry的实现)
mmap内存映射的实现过程,总的来说可以分为三个阶段:
*(一)进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域*
-
进程在用户空间调用库函数mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-
在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址。
-
为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化。
-
将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表。这里采用的链表是最简单的单向链表,会牺牲一些速度,但胜在简便
注意:寻址方式
为简便期间并防止进程调用malloc导致的堆生长与 映射的虚拟地址产生冲突,所以从0x2000000开始映射一直到0x4000000
分配及初始化的代码如下:
if(start==NULL){ //需要由程序分配合适的地址空间 //由于是借助链表实现 所以需要搜索来找到合适的地址进行使用 if(current->mmap==NULL) buf = 0x2000000;//如果初始化为NULL if(!buf){ struct vmarea_struct *m2 = current->mmap; struct vmarea_struct *m3 = current->mmap->next; for(m3;m3->next;m3 = m3->next){ if(m3->vm_start-m2->vm_end>=size){ buf = m2->vm_end; break; } m2 = m3; } if(!buf) buf = m3->vm_end; } } //初始化 struct vmarea_struct *m1 = (struct vmarea_struct *)malloc(sizeof(struct vmarea_struct)); m1->mode = prot; m1->flag = flags; m1->next = NULL; m1->size = size; m1->vm_start = buf; m1->vm_end = (unsigned long)buf+size; m1->fd = fd; m1->off = off; if(current->mmap==NULL) current->mmap = m1; else{//插到最后一个位置 struct vmarea_struct *m2=current->mmap; for(m2;m2->next;m2=m2->next) ; m2->next = m1; }
*(二)实现文件物理地址和进程虚拟地址的一一映射关系*
-
用get_free_page获得空闲页
-
修改页目录项和页表项的内容使其能够指向空闲页,这片虚拟地址并没有任何数据关联到主存中
//接下来进行映射等操作
//参考了try_to_share的实现
unsigned long from;
unsigned long to;
unsigned long from_page;
unsigned long to_page;
unsigned long phys_addr;
unsigned long page;
unsigned long tmp;
unsigned long vm_address=m1->vm_start;
while(vm_address<m1->vm_end){
from_page = to_page = ((vm_address>>20)&0xffc);//逻辑的页目录项偏移
from_page +=((current->start_code>>20)&0xffc);//该进程目录项地址
//from处是否存在页目录项 否则进行申请
from = *(unsigned long *) from_page;
from &=0xfffff000;
from_page = from +((vm_address>>10)&0xffc);//页表项指针
phys_addr = *(unsigned long *) from_page;//页表项内容
if(!(page = get_free_page())){
printk("no free page\n");
return -1;
}
/* phys_addr = page |0x7;
mem_map[(page-LOW_MEM)>>12]++;*/
/*if(!(*(unsigned long *) from_page & 0x1)){
if(page = get_free_page())
phys_addr = page |0x7,mem_map[(page-LOW_MEM)>>12]++;
else
oom();
}*/
put_page(page,vm_address);
//对页面分配权限
*(unsigned long *)from_page &= (prot&PROT_WRITE)?0xffffffff:0xfffffffd;
//对flags进行处理,重点处理的是MAP_SHARE,私有的话不需要进行多余的处理
//需要将所有进程共享同一页面
if(flags&MAP_SHARED){
struct task_struct ** task;
for (task = &LAST_TASK ; task>&FIRST_TASK;--task){
if (!*task)
continue;
else if (current == *task)
continue;
else if(!try_to_share(vm_address,*task))
printk("current pid %d cann't share memory\n",(*task)->pid);
}
}
vm_address += PAGE_SIZE;
}
*(三)进程实现文件内容到物理内存的拷贝*
这里采用的实现是在函数内就将文件内容拷贝到物理页面上
为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体(struct file),每个文件结构体维护着和这个已打开文件相关各项信息。
9.参考find_entry和sys_read函数,读取文件内容,并写到内存空间中
if (!(block = dir->i_zone[0]))
return NULL;
if (!(bh = bread(dir->i_dev,block)))
return NULL;
char *s = (char *) bh->b_data;
char *p1=(char *)s;
char *p2=(char *)buf;
while(len--)
put_fs_byte(*(p1++),p2++);
munmap实验
munmap函数的功能是解除内存映射
实现原理及步骤
*(一)在当前进程的链表中能够寻找到对应的vmarea_struct,并将之从链表中删除*
1.从当前进程链表开头寻找,找到为止
*(二)根据寻找到的vmarea_struct结构判断页面的权限,如果是有写权限的,需要将页面内容写到对应的文件之中*
将buf内容写到fd之中的函数,参照了sys_write的实现
int mmap_wirte(int fd,off_t pos,char *buf,int count){
struct file * file;
struct m_inode * inode;
if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd]))
return -EINVAL;
if (!count)
return 0;
inode=file->f_inode;
file->f_pos = pos;
if (inode->i_pipe)
return (file->f_mode&2)?write_pipe(inode,buf,count):-EIO;
if (S_ISCHR(inode->i_mode))
return rw_char(WRITE,inode->i_zone[0],buf,count,&file->f_pos);
if (S_ISBLK(inode->i_mode))
return block_write(inode->i_zone[0],&file->f_pos,buf,count);
if (S_ISREG(inode->i_mode))
return file_write(inode,file,buf,count);
printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode);
return -EINVAL;
}
*(三)解除虚拟地址对物理页面的映射,需要判断该页面是否是共享的,如果是共享的需要进行改动*
与mmap的映射相似,只是对页表项的最后一位进行了改动
//取消进程p的共享 与try_share很类似 只是少了几步
static int cancle_share(unsigned long address, struct task_struct * p)
{
unsigned long from;
unsigned long from_page;
unsigned long phys_addr;
from_page = ((address>>20) & 0xffc);
from_page += ((p->start_code>>20) & 0xffc);
/* is there a page-directory at from? */
from = *(unsigned long *) from_page;
from &= 0xfffff000;
from_page = from + ((address>>10) & 0xffc);
phys_addr = *(unsigned long *) from_page;
/* is the page clean and present? */
phys_addr &= 0xfffff000;
if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM)
return 0;
/* share them: write-protect */
*(unsigned long *) from_page &= ~1;
phys_addr -= LOW_MEM;
phys_addr >>= 12;
mem_map[phys_addr]--;
return 1;
}
函数验证
mmap
测试文件的函数
测试的思路是,打开一个文件并将该文件映射到内存中,输出对应位置的字符串
void test_mmap(void){
char *array;
const char *str = " Hello, mmap success.";
int fd;
/*打开文件并将文件内容写入到test_mmap.txt中*/
fd = open("test_mmap.txt", O_RDWR | O_CREAT, S_IRUSR|S_IWUSR);
write(fd, str, strlen(str));
fstat(fd, &kst);
printf("file len: %d\n", (int)kst.st_size);
//将test_mmap.txt文件映射到内存中,虚拟地址是array
array = mmap(NULL, kst.st_size, PROT_WRITE | PROT_READ, MAP_FILE | MAP_SHARED, fd, 0);
printf("mmap addr: %x\n", (unsigned int)array);
if (array == MAP_FAILED) {
printf("mmap error.\n");
}else{
printf("mmap content: %s\n", array);
munmap(array, kst.st_size);
}
close(fd);
}
测试结果如下:可以看到与str对应的字符串相同
munmap
clone
问题与建议
1.初始化问题