实验名称:地址映射与共享
实验日期:7月5日
班级:软嵌191
姓名:游金明
学号:193110795
一、实验目的
三、实验内容
用Bochs 调试工具跟踪 Linux 0.11 的地址翻译
1 实验环境配置
$ cd /home/shiyanlou/oslab/
$ tar -zxvf hit-oslab-linux-20110823.tar.gz
-C /home/shiyanlou/
切换目录
$ cd /home/shiyanlou/oslab/
确认路径 $ pwd
查看目录内容 $ ls -l
2挂载
3编写test.c
#include <stdio.h>
int i = 0x12345678;
int main(void)
{
printf("The logical/virtual address of i is 0x%08x", &i);
fflush(stdout);
while (i)
;
return 0;
}
copy
4编译linux,启动bochs调试
./dbg-asm
copy
gcc -o test test.c
./test
copy
寻找LDT表: GDT(全局描述符表,操作系统使用的段表)中保存着 LDT 的物理地址,而ldtr 寄存器中记录着LDT物理地址在GDT表中的位置。 在命令行窗口输入sreg命令后,显示出各寄存器的信息,如下图,可以看到ldtr的值是 0x0068(不同人的机器可能得到的值不一样,之后注意对应好位置就好),转换成二进制是0000000001101000,其中1101这四位表示LDT的物理地址在GDT表中的位置(各个位的意义可以参考linux0.11注释书或者实验楼中的提示),1101转换成十进制是13。 下图中gdtr的值是GDT表的物理地址,即0x00005cb8。
用 xp /32w 0x00005cb8 命令查看从GDT表的物理地址开始,32 个单位(不知道这32是指什么单位,如图能看到是有32个数)的内容,即 GDT 表的前 16 项(每项对应2个单位,可以看到每个单位是由8位16进制组成,即每个单位占32位,那么就是每项占64位),如下:
32位二进制线性地址的前10位对应页目录号,中间10位对应页表号,最后12位对应页内偏移。所以 0x10003004 的页目录号是 64,页号 3,页内偏移是 4(转换的时候先把16进制的线性地址变成2进制)。
页目录表的位置由 CR3 寄存器指引。输入“creg”命令可以看到CR3=0x00000000,如下图
最终我们知道此机器对于物理地址为 0x00fa6004
输入setpmem 0x00fa7004 4 0
切回boch界面
循环结束了test中的i被改为了0
编写生产者消费者
#define SHM_SIZE 64
typedef struct shm_ds
{
unsigned int key;
unsigned int size;
unsigned long page;
}shm_ds;
int sys_shmget(unsigned int key,size_t size);
void * sys_shmat(int shmid);
copy
2.sys.h修改
修改系统调用为74
3.添加shm.c
#define __LIBRARY__
#include <unistd.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <errno.h>
static shm_ds shm_list[SHM_SIZE] = {{0,0,0}};
int sys_shmget(unsigned int key, size_t size)
{
int i;
void *page;
if(size > PAGE_SIZE)
return -EINVAL;
page = get_free_page();
if(!page)
return -ENOMEM;
printk("shmget get memory's address is 0x%08x\n",page);
for(i=0; i<SHM_SIZE; i++)
{
if(shm_list[i].key == key)
return i;
}
for(i=0; i<SHM_SIZE; i++)
{
if(shm_list[i].key == 0)
{
shm_list[i].page = page;
shm_list[i].key = key;
shm_list[i].size = size;
return i;
}
}
return -1;
}
void * sys_shmat(int shmid)
{
int i;
unsigned long data_base, brk;
if(shmid < 0 || SHM_SIZE <= shmid || shm_list[shmid].page==0 || shm_list[shmid].key <= 0)
return (void *)-EINVAL;
data_base = get_base(current->ldt[2]);
printk("current's data_base = 0x%08x,new page = 0x%08x\n",data_base,shm_list[shmid].page);
brk = current->brk + data_base;
current->brk += PAGE_SIZE;
if(put_page(shm_list[shmid].page, brk) == 0)
return (void *)-ENOMEM;
return (void *)(current->brk - PAGE_SIZE);
}
copy
4.修改Kernel下的Makefile文件
### Dependencies:
sem.s sem.o: sem.c ../include/linux/sem.h ../include/linux/kernel.h \
../include/unistd.h
shm.s shm.o:shm.c ../include/unistd.h ../include/linux/kernel.h ../include/linux/sched.h ../include/linux/mm.h ../include/errno.h
copy
5.编写生产者消费者
生产者
/*producer*/
#define __LIBRARY__
#include <stdio.h>
#include <unistd.h>
#include <linux/kernel.h>
#include <fcntl.h>
#include <sys/types.h>
#include <linux/sem.h>
_syscall2(sem_t *,sem_open,const char *,name,int,value);
_syscall1(int,sem_post,sem_t *,sem);
_syscall1(int,sem_wait,sem_t *,sem);
_syscall1(int, shmat, int, shmid);
_syscall2(int, shmget, unsigned int, key, size_t, size);
#define PRODUCE_NUM 200
#define BUFFER_SIZE 10
#define SHM_KEY 2018
int main(int argc, char* argv[])
{
sem_t *Empty,*Full,*Mutex;
int i, shm_id, location=0;
int *p;
Empty = sem_open("Empty", BUFFER_SIZE);
Full = sem_open("Full", 0);
Mutex = sem_open("Mutex", 1);
if((shm_id = shmget(SHM_KEY, BUFFER_SIZE*sizeof(int))) < 0)
printf("shmget failed!");
if((p = (int * )shmat(shm_id)) < 0)
printf("shmat error!");
for(i=0; i<PRODUCE_NUM; i++)
{
sem_wait(Empty);
sem_wait(Mutex);
p[location] = i;
sem_post(Mutex);
sem_post(Full);
location = (location+1) % BUFFER_SIZE;
}
return 0;
}
copy
消费者
/*consumer*/
#define __LIBRARY__
#include <stdio.h>
#include <unistd.h>
#include <linux/kernel.h>
#include <fcntl.h>
#include <sys/types.h>
#include <linux/sem.h>
_syscall2(sem_t *,sem_open,const char *,name,int,value);
_syscall1(int,sem_post,sem_t *,sem);
_syscall1(int,sem_wait,sem_t *,sem);
_syscall1(int,sem_unlink,const char*,name);
_syscall1(int, shmat, int, shmid);
_syscall2(int, shmget, unsigned int, key, size_t, size);
#define PRODUCE_NUM 200
#define BUFFER_SIZE 10
#define SHM_KEY 2018
int main(int argc, char* argv[])
{
sem_t *Empty,*Full,*Mutex;
int used = 0, shm_id,location = 0;
int *p;
Empty = sem_open("Empty", BUFFER_SIZE);
Full = sem_open("Full", 0);
Mutex = sem_open("Mutex", 1);
if((shm_id = shmget(SHM_KEY, BUFFER_SIZE*sizeof(int))) < 0)
printf("shmget failed!\n");
if((p = (int * )shmat(shm_id)) < 0)
printf("link error!\n");
while(1)
{
sem_wait(Full);
sem_wait(Mutex);
printf("pid %d:\tconsumer consumes item %d\n", getpid(), p[location]);
fflush(stdout);
sem_post(Mutex);
sem_post(Empty);
location = (location+1) % BUFFER_SIZE;
if(++used == PRODUCE_NUM)
break;
}
sem_unlink("Mutex");
sem_unlink("Full");
sem_unlink("Empty");
return 0;
}
copy
运行bochs,输入: gcc -o pro producer.c gcc -o con consumer.c 编译这两个程序, 然后输入 pro > proOutput & con > conOutput & 来同时运行这两个程序,并将结果保存到proOutput和conOutput中。 最后输入sync。
五、实验结果分析
1.得到虚拟地址
通过反汇编,得到变量i的虚拟地址是ds:0x3004
2.找到LDT表。
在命令行窗口输入sreg命令后,显示出各寄存器的信息。ldtr 寄存器中记录着LDT物理地址在GDT表中的位置,它的值是0x0068,转换成二进制是0000000001101000,其中1101这四位表示LDT的物理地址在GDT表中的位置即13;gdtr寄存器的值是GDT表的物理地址,它是0x00005cb8。要查找的项的地址是 0x00005cb8+13* 8即可得到LDT表的物理地址。所以输入 xp / 2w 0x00005cb8 + 13 * 8,得到0x52d00068 0x000082fd如下图,将图中加粗位组合即得到LDT表的物理地址0x00fd52d0
3.通过LDT表得到基址,从而计算得到线性地址
首先通过sreg命令查看段寄存器ds(段选择子)的内容,得到ds的值是0x0017,转换成2进制后是0000000000010111,可以看到索引值为 10(二进制)= 2(十进制)也就是要找 LDT 表中2号位置(是第3项,因为从0开始编号)。根据之前得到的LDT表的物理地址,输入xp /8w 0x00fd52d0指令,得到了LDT表的前四项内容,可以看到LDT表的第3项段描述符是0x00003fff 0x10c0f300,由该描述符拼成的基址是0x10000000,段基址+偏移即可得到线性地址,所以ds:0x3004 的线性地址是0x10003004
4.通过查页表得到物理地址:
32位二进制线性地址的前10位对应页目录号,中间10位对应页表号,最后12位对应页内偏移。所以 0x10003004 对应的的页目录号是 64,页号是3,页内偏移是4。页目录表的基址是 0,因此查找64号页目录即0+64*4处,所以输入指令xp /w 0+64 * 4查看,结果如下:
可知页表所在物理页框号为 0x00fa7(027是属性,与位置无关),即页表在物理内存的 0x00fa7000 位置。从该位置开始查找 3 号页表项,所以输入指令xp /w 0x00fa7000+3 *4查看
可得对应的页框号是0x00fa6,加上页内偏移0x004后,即得到最终的物理地址0x00fa6004。
学习时间 307分钟
操作时间 111分钟
按键次数 3424次
实验次数 5次
报告字数 9517字
是否完成 完成