操作系统实验(5 :地址映射与内存共享)

实验目的

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

实验内容

本次实验的基本内容是先用Bochs调试工具跟踪Linux 0.11的地址翻译(地址映射)过程,然后在此基础上编程实现一个基于内存共享的进程间通信机制。本实验主要完成如下三项基本工作:

  1. 在保护模式下(启动了分段和分页机制以后)工作的Linux 0.11代码中加上一个内存寻址指令,并且在该内存地址处放置一个自己构造的数据,应用Bochs调试工具跟踪该地址的从逻辑地址、GDT表、线性地址、页表、物理地址的过程,最后验证是否是自己放置的数据?实际上就是手动进行一次地址翻译工作。

  2. 实现两个进程通过页共享来进行相互通信

实验步骤

实验步骤参考博客:
https://www.cnblogs.com/tradoff/p/5774355.html
https://www.shiyanlou.com/courses/reports/1320023/
(侵删)

1. 添加test.c文件

挂载hdc,在usr/root下添加test.c文件用来测试地址映射,代码如下:

#include 

int i = 0x12345678;

int main(void)
{
    printf("The logical/virtual address of i is 0x%08x", &i);
    fflush(stdout);

    while (i)
        ;

    return 0;
}

2. 调试Linux-0.11

输入./dbg-asm用汇编级调试启动linux-0.11
然后输入以下命令编译并运行test.c
gcc -o test test.c
./test
操作系统实验(5 :地址映射与内存共享)_第1张图片
这时候会进入死循环,我们切换到ubuntu终端,按下Ctrl+c暂停bochs。

3. 找到物理地址

为了让linux-0.11中运行的test跳出循环,需要找到逻辑地址ds:0x3004对应的物理地址,将其内容(变量i)改为0。

在终端中输入sreg,得到gdtr的基址值为0x00005cb8,ldtr为0x0068即0000 0000 0110 1000 b,可知索引为1101b即13,TI位为0,即GDT中的第13项为LDT的段描述符。
操作系统实验(5 :地址映射与内存共享)_第2张图片
输入xp /2w 0x00005cb8+13*8得到LDT段描述符,可以得到LDT的基址为0x00f9a2d0
ds段选择子为0x0017 => 0000 0000 0001 0111 b,可知索引为10b即2,TI位为1,即LDT中的第2项为ds的段描述符,输入xp/2w 0x00f9a2d0+2*8得到ds段描述符,可以知道ds的基址为0x10000000,所以0x3004对应的线性地址为0x10000000+0x3004=0x10003004
在这里插入图片描述
输入xp /w 64*4获取页目录项,可知页表基地址为0x00fa6000。
输入xp /w 0x00fa6000+3*4得到物理基址为0xfa5000。
输入xp /w 0xfa5000+4得到的内容即test.c中的变量的值,输入setpmem 0xfa5004 4 0将它设为0。
操作系统实验(5 :地址映射与内存共享)_第3张图片
在终端中输入c让bochs继续运行,发现test跳出循环。
操作系统实验(5 :地址映射与内存共享)_第4张图片

4. 添加系统调用

在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);

操作系统实验(5 :地址映射与内存共享)_第5张图片
已及定义两个系统调用:
#define __NR_shmget 76
#define __NR_shmat 77
操作系统实验(5 :地址映射与内存共享)_第6张图片
修改完后把此文件覆盖usr/include里的unistd.h。

在sys.h中添加函数声明
extern int sys_shmget();
extern int sys_shmat();
操作系统实验(5 :地址映射与内存共享)_第7张图片
在system_call.s中把nr_system_calls改为78(系统调用个数)

接下来在kernel下添加shm.c,代码如下:

#define __LIBRARY__
#include 
#include 
#include 
#include 
#include 

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);
}

然后修改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
...

操作系统实验(5 :地址映射与内存共享)_第8张图片
操作系统实验(5 :地址映射与内存共享)_第9张图片

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

在usr/root下添加producer.c和consumer.c
Producer.c代码如下:
里面的相关函数参考之前写的shm.c,因为我在第四个实验中把sem.h分出来了,所有需要在这里面#include ,consumer.c也是一样。

/*producer*/ 
#define __LIBRARY__
#include 
#include 
#include 
#include 
#include 
#include 

_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;    
}

Consumer.c代码如下:

/*consumer*/ 
#define __LIBRARY__
#include 
#include 
#include 
#include 
#include 
#include 

_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;    
}

运行bochs,输入:
gcc -o pro producer.c
gcc -o con consumer.c
编译这两个程序,
然后输入
pro > proOutput &
con > conOutput &
来同时运行这两个程序,并将结果保存到proOutput和conOutput中。
最后输入sync
操作系统实验(5 :地址映射与内存共享)_第10张图片
关闭linux-0.11回到ubunt终端,输入sudo less hdc/usr/root/conOutput查看结果如下:
操作系统实验(5 :地址映射与内存共享)_第11张图片

你可能感兴趣的:(操作系统实验)