操作系統實驗06-地址映射與共享


實驗內容

  • 1.在保護模式下(啟動了分段和分頁機制以后)工作的Linux 0.11代碼中加上一個內存尋址指令,並且在該內存地址處放置一個自己構造的數據,應用Bochs調試工具跟蹤該地址的從邏輯地址、GDT表、線性地址、頁表、物理地址的過程,最后驗證是否是自己放置的數據?實際上就是手動進行一次地址翻譯工作。
    1. 實現兩個進程通過頁共享來進行相互通信。

步驟

1.創建test.c

掛載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;
}

代碼添加過程截圖如下:

2.尋找物理地址

首先進入Linux-0.11目錄內make編譯系統,然后回退至oslab目錄內運行系統,再使用下述命令編譯運行test.c文件

gcc -o test test.c
./test

運行效果如下圖所示:

此時會進入在test.c內的while死循環,然后ctrl+c暫停bochs。
此時需要讓Linux-0.11的test跳出死循環,所以需要找到邏輯地址ds:0X00003004對應的物理地址,將它的內容更改為0。
在終端中輸入sreg,得到gdtr的基址值為0x00005cb8,ldtr為0x0068即0000 0000 0110 1000 b,可知索引為1101b即13,TI位為0,即GDT中的第13項為LDT的段描述符。

輸入xp /2w 0x00005cb8+138得到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 644獲取頁目錄項,可知頁表基地址為0x00fa6000。
輸入xp /w 0x00fa6000+3
4得到物理基址為0xfa5000。
輸入xp /w 0xfa5000+4得到的內容即test.c中的變量的值,輸入setpmem 0xfa5004 4 0將它設為0。

然后在終端內輸入c繼續讓系統運行,發現test而已跳出循環。

3.添加系統調用

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

然后增加兩個系統調用

#define __NR_shmget 76
#define __NR_shmat 77

4.修改sys.h文件

在此文件中增加函數聲明:

extern int sys_shmget();
extern int sys_shmat();

在system_call.s中把nr_system_calls改為78

5.增加shm.c

shm.c的位置在kernel目錄下
代碼如下:

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

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

修改截圖如下:

6.編寫消費者和生產者程序

在hdc的root目錄下增加producer.c和consumer.c文件,這兩個文件的代碼如下:

/*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;    
}

consumer.c代碼如下

/*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;    
}

7.運行驗證

運行bochs,輸入:

gcc -o pro producer.c
gcc -o con consumer.c

編譯這兩個程序,
然后輸入

pro > proOutput &
con > conOutput &

來同時運行這兩個程序,並將結果保存到proOutput和conOutput中。
最后輸入sync,結果如下

關閉linux-0.11回到ubunt終端,輸入sudo less hdc/usr/root/conOutput查看結果如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM