“操作系统实验”实验报告

地址映射与共享

实验名称:地址映射与共享
实验日期:7月5日
班级:软嵌191
姓名:游金明
学号:193110795
一、实验目的

  • 深入理解操作系统的段、页式内存管理,深入理解段表、页表、逻辑地址、线性地址、物理地址等概念;
  • 实践段、页式内存管理的地址映射过程;
  • 编程实现段、页式内存管理上的内存共享,从而深入理解操作系统的内存管理。 二、实验环境
    本操作系统实验的硬件环境是 IA-32(x86) 架构的 PC 机(在实验楼的环境中就是右侧的窗口),主要软件环境是 Bochs + gcc + 你最喜欢的编辑器 / IDE + 你最喜欢的操作系统 + Linux 0.11 源代码。

三、实验内容

  • 用 Bochs 调试工具跟踪 Linux 0.11 的地址翻译(地址映射)过程,了解 IA-32 和 Linux 0.11 的内存管理机制;
  • 在 Ubuntu 上编写多进程的生产者—消费者程序,用共享内存做缓冲区;
  • 在信号量实验的基础上,为 Linux 0.11 增加共享内存功能,并将生产者—消费者程序移植到 Linux 0.11。 四、实验过程及数据记录

用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

图片描述

  • 输入c使bochs继续并编译test.c
gcc -o test test.c
./test
copy

图片描述

  • 这里出现的是逻辑地址。接下来我们来寻找其物理地址。 在命令行中按Ctrl+c ,暂停Bochs的运行,进入调试状态。命令行中显示如下信息 图片描述

寻找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 编写生产者消费者

  1. 修改unistd.h
#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
  • 声明

    define __NR_shmget 76

    define __NR_shmat 77

    图片描述

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。

图片描述

五、实验结果分析

  • 对于地址映射实验部分,列出你认为最重要的那几步(不超过 4 步),并给出你获得的实验数据。

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。

  • test.c 退出后,如果马上再运行一次,并再进行地址跟踪,你发现有哪些异同?为什么? 将test.c退出后再运行一次,我的结果中逻辑地址线性地址没有变,物理地址改变,可能是有其它进程占用了物理地址。 从理论上讲,逻辑地址是不会变的,因为逻辑地址是用户层面的,它只与test.c文件本身相关。我认为对于linux0.11来说线性地址是系统层面的,它们是否改变取决于其它的进程情况,因为linux0.11中各个进程是共用一个段表和页表的,各个进程的线性地址是不能重叠的,也就是说线性地址是否改变取决于test程序的进程号,linux0.11是根据进程号来分配基址的(对于新版本的linux系统,每个进程都独自占用4G线性空间,独自用一个段表,线性地址就不会改变?)。物理地址的话是可能改变的,在test.c退出后可能有其它进程占用了之前的物理地址。 六、实验心得
    了解了bochs的一些基本使用,熟悉了往linux中移植程序的一般步骤。
最新评论
暂无评论~