实验目标:1)用Bochs调试跟踪linux0.11的地址转换过程;2)实现基于共享物理页框的进程间内存共享。
1)用Bochs调试跟踪linux0.11的地址转换过程
1.汇编级调试linux0.11 启动操作系统 执行test.c
#include <stdio.h>
int i = 0x12345678;
int main(void)
{
printf("The logicla address of i is 0x%08x",&i);
fflush(stdout);
while(i);
return 0;
}
操作系统会打印 i的逻辑地址: The logicla address of i is 0x00003004。 我们的目的是通过调试test这个死循环命令,查看逻辑地址、LDT表、GDT表、页表等信息。 这些信息通过去寻找变量i的物理地址 一一展现。 即下面开始去寻找逻辑地址:DS:0x3004所对应的物理地址,跟踪地址转换过程。 由于采用的段页式内存结构,大步骤就是 先通过段表找到虚拟地址(进程的LDT表。 LDTR存放当前进程LDT表地址在GDT表中的偏移值)
2.由段表找到虚拟地址 可知 GDTR的基址是0x00005cb8。ldtr存放的是ldt地址在gdt表中的偏移值0x0068。ds寄存器在ldt表中的偏移值是0x0017。 由于GDT表每一项占8字节,ldt表地址存放在GDT表0x0068>>2^3 即十进制13 位置上 所以LDT表项的物理地址是“0x00005cb8+13*8"。 查看该处存储内容,加上GDT表格式知识,得LDT表得物理基址是0x00fd52d0. 查看该表项内容 由ds寄存器内容可知,ds的地址存储在LDT表第16个字节开始的位置,由于每项占8字节 所以i在虚拟地址空间的地址是0x10003004
2.由多级页表将虚拟地址转为物理地址 32为虚拟地址的前10位 中间10位 末尾12位代表了页目录号(64、页表号(3)、页内偏移(004)。页目录位置由CR3寄存器指向。用creg命令查看cr3的页目录的基址是0x0000000。 页目录表和页表中的内容都是1024个32位二进制数 后12位是属性代码。 查看第65个页目录项的内容(应该是页表的地址) 为0x00faa027 在该页表内查第4个页表项的内容(页的地址) 为0x00fa7067 然后004是该页的偏移 即i的物理地址是0x00fa004.
2) 实现基于共享物理页框的进程间内存共享
这个很多人结合实践项目5做的,很漂亮,哈哈,我这里给一个最简陋的解决方案吧,可以更方便的理解到核心。 首先新建系统调用 shmget新建/打开一个共享物理页 返回实际物理地址 shmat将物理地址与虚拟地址链接在一起,并返回逻辑地址(程序使用逻辑地址)主要利用put_page(这里指导书上写反了)
#define __LIBRARY__
#include <linux/sched.h>
#include <errno.h>
#include <unistd.h>
#include <linux/kernel.h>
#include <linux/shm.h>
#include <linux/mm.h>
static unsigned long SharedMemory[20] = {0};
unsigned long shmget(key_t key, size_t size, int shmflag)
{
unsigned long shmid;
if(SharedMemory[key] != 0)
return SharedMemory[key];
if(size > 0x10000)
{
printk("size too big\n");
errno = EINVAL;
return -1;
}
shmid = get_free_page();
if(!shmid)
{
printk("no merroy\n");
errno = ENOMEM;
return -1;
}
SharedMemory[key] = shmid;
printk("shmid is %x",shmid);
return shmid;
}
int *shmat(key_t key, const void *shmaddr, int shmflg)
{
unsigned long shmid = SharedMemory[key];
if(shmid == 0)
{
errno = EINVAL;
return (int *)(-1);
}
int tmp;
tmp = get_base(current->ldt[2]) + current->brk;
put_page(shmid,tmp);
printk("Physical address is 0x%08x -> Logical address is 0x%08x\n",shmid,current->brk);
return (int *)current->brk;
}
consumer.c
#define __LIBRARY__
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/shm.h>
_syscall3(unsigned long,shmget,key_t,key, size_t,size,int,shmflg);
_syscall3(int *,shmat,key_t,key, const void*,shmaddr,int,shmflg);
int main()
{
int *addr = NULL;
int key = 1;
addr = shmat(1,0,0);
printf("read 0x%08x from 0x%08x",*addr,addr);
fflush(stdout);
if(addr == (int *)-1)
{
printf("shmat failure!\n");
return 0;
}
while(1)
{
if(*addr == 0x1234)
break;
}
printf("Success,*addr = 0x%08x",*addr);
return 0;
}
producer.c
#define __LIBRARY__
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/shm.h>
_syscall3(unsigned long,shmget,key_t,key, size_t,size,int,shmflg);
_syscall3(int *,shmat,key_t,key, const void*,shmaddr,int,shmflg);
int main()
{
int *addr = NULL;
int key = 1;
unsigned long shmid = 0;
shmid = (unsigned long)shmget(key,1,1);
if (shmid <= 0)
printf("shmget failure!\n");
else
{
addr = shmat(key,0,0);
if (addr == (int *)(-1))
{
printf("shmat failure!");
return 0;
}
*addr = 0x1234;
}
printf("write 0x%08x in 0x%08x",*addr,addr);
return 0;
}
值得一提的是 容易犯错的点在于 两个进程一定要同时运行才可共享
|