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

地址映射与共享

实验名称:地址映射与共享

实验日期:2021年6月2号

班级:物联网193班

姓名:邹胡健

学号:1930110718

一、实验目的

  • 深入理解操作系统的段、页式内存管理,深入理解段表、页表、逻辑地址、线性地址、物理地址等概念;
  • 实践段、页式内存管理的地址映射过程;
  • 编程实现段、页式内存管理上的内存共享,从而深入理解操作系统的内存管理。

二、实验环境

  • oslab实验室

三、实验内容

1.实验相关知识的阐述:

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

2.实验原理的预习情况:

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

  • 答:第一步为找到需要的各个选择符,DS:S=0X0017,LDT:s=0x0068,GDT:BASE=0X0000 5CB8。第二步为找到数据段的基址。实验中进程的NR为4,即第5个进程,所以按照LINUX 0.11的设计,其基址应为 4*64M,转换为16进制即为0x1000 0000。实验结果也验证了这一点。第三步根据找到的线性地址,通过查看页目录表就可以找到对应页表的物理地地址。实验中页表的物理地址为0x00fa 70000。 第四步根据找到的页表所对就应的物理地址就可以找到变量所在页的物理地址。实验中变量所在页的物理地址为0x00fa6000。这里有个很有趣的现象,页表的物理地址比具体一页的物理地址高。而页表一定是先分配的内存,这也说明linux 0.11中物理内存的分配是从高到低进行的。这一点通过查看get_free_page函数得到印证。

  • test.c退出后,如果马上再运行一次,并再进行地址跟踪,你发现有哪些异同?为什么?

  • 答:若马上再运行一次,则可以看到得到的线性地址为0x1400 3004,即进程的数据段基地多了0x0400 0000。就是多了一个64M。这也正是linux 0.11中对进程线性地址分配的方法即:nr*64M。因为马上运行此时进程4并没有被删除,而只是状态个性为3,所以再运行的进程正好是5。所以其分配的线性地址基址为0x1400 0000。

3.实验注意事项:

  • 在第一次运行test.c,在命令行窗口输入ctrl+c,Bochs就会暂停运行的步骤时: 1.如果000f处位置显示的是0008,表示按下Ctrl+c键中断发生在内核中, 这时需要输入c继续执行,然后在按下Ctrl+c键,直到变成000f为止。 2.如果cmp处显示的不是这个,就用’n’命令单步运行几次,直到停在cmp处,实际上就是要停留在test.c的while(i)语句处,方便下面的继续调试。

四、实验步骤

1. 跟踪地址翻译过程

  • 环境配置
    cd ~/oslab
    tar -zxvf hit-oslab-linux-20110823.tar.gz -C /home/shiyanlou
    ls -al
    
    copy
    图片描述
  • 一定要make all!!!!
  • 跟踪地址翻译过程 1.挂载hdc,在usr/root下添加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

图片描述

  • 使用汇编级调试 ./dbg-asm 图片描述
  • 输入c 图片描述
  • 编译并运行test.c
gcc -o test test.c
./test
copy

图片描述

  • 这时候会进入死循环,我们按下Ctrl+C暂停bochs进行调试 图片描述
  • 找到物理地址 输入sreg,得知GDT表的物理地址 图片描述
  • 输入xp /2w 0x00005cb8+13*8得到LDT段描述符
  • 输入xp /68w 0可查看页目录表内容
  • 输入xp /w 0xfa6004得到的内容即test.c中的变量的值,输入setpmem 0xfa6004 4 0将它设为0。
  • 在终端中输入c让bochs继续运行,发现test跳出循环 图片描述

2. 实现共享内存

  • 在实验六的基础上完成该实验,添加系统调用(具体原理及操作可参考实验三)。
  • 在unitsd.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
copy
  • 完成后将该文件复制到usr/include中。
  • 在sys.h中添加函数声明
extern int sys_shmget();
extern int sys_shmat();
/*sys_sem_unlink,*/sys_shmget, sys_shmat/*};*/
copy
  • 修改系统调用个数为78
  • 在kernel中新建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
  • 修改MakeFile文件
OBJS=.......sem.o
.....

### 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
  • 上述修改工作完成后,进行编译工作,通过。 图片描述

3. 编写消费者和生产者程序

  • producer.c
#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.c
#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
  • 挂载虚拟磁盘进行编译
gcc -o pro producer.c
gcc -o con consumer.c
copy
pro > proOutput &
con > conOutput &
sync
copy

图片描述 图片描述

五、实验总结

这次实验使我对操作系统的段、页式内存管理,深入理解段表、页表、逻辑地址、线性地址、物理地址等概念更加理解透彻。

最新评论
暂无评论~