《操作系統原理、實現與實踐》實踐項目5&6:信號量&地址映射與共享

教員佈置的操作系統的第一個大作業,其中有一些知識相對而言比較重要,包括一些操作過程都具有重複實驗的價值;所以寫一個blog記錄一下,方便一下以後重複這個大實驗或是復現。

分爲三個部分:實驗環境的配置、實驗5的實現、實驗6的實現。

實驗環境的配置:

KylinOS應該是可以實現的,實現的原理同我下面所敘述的過程。

資源下載:

我這裏採取的實驗環境是VMware上的Ubuntu 20.04 64bit,採取的子環境是從助教學長那裏下載的rar壓縮包,在windows的環境下解壓後會有一個xxx.tar.gz的壓縮包文件以及一個gcc,g++的3.4版本的安裝版,由於這個實驗在3.4環境下做是比較合適的,所以我們需要將我們的gcc,g++的版本調整至3.4;

環境更改,gcc多版本共存

dpkg表示“Debian Packager”,是Debian開發的套件管理系統,

在對應的gcc,g++的那個文件夾裏面,運行如下的指令:

sudo dpkg --force-depends -i gcc-3.4-base_3.4.6-6ubuntu3_amd64.deb
sudo dpkg --force-depends -i gcc-3.4_3.4.6-6ubuntu3_amd64.deb 
sudo dpkg --force-depends -i cpp-3.4_3.4.6-6ubuntu3_amd64.deb 
sudo dpkg --force-depends -i g++-3.4_3.4.6-6ubuntu3_amd64.deb
sudo dpkg --force-depends -i libstdc++6-dev_3.4.6-6ubuntu3_amd64.deb

接着可以用

gcc --version
g++ --version

來查看自己所安裝的版本是不是正確的版本,此時應該顯示的是3.4

環境配置並make

我們需要解壓xxx.tar.gz壓縮包(在linux環境下),然後對於在其解壓後的linux-0.11的文件夾內進行make。

大概率此時的make是會顯示各種錯誤的,所以我們需要配置一些環境。

sudo apt-get install bin86
sudo apt-get install gcc-multilib
sudo apt-get install build-essential
sudo apt-get install bochs bochs-x bochs-sdl
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt install libsm6:i386
sudo apt install libx11-6:i386
sudo apt install libxpm4:i386

按順序嘗試以上的命令進行配置環境,如果還是不成功make就試試非i386版本的安裝。(其實以上的命令就是根據顯示的出的make中的error來配置修改的)

最後make成功之後,會顯示sync的結果,嘗試./run與./dbg-asm測試,成功之後則說明之前的步驟都是沒問題的。

 

實驗5的實現:

實踐目標

實驗五的主要實現部分是sem.c,實質就是一個信號量的交換。

這個地方我在實現的時候關於系統調用的函數寫錯了,在大佬的幫助下才改正的,卡了很久。

實驗5的實踐目標有兩個:

1. 在Linux0.11(沒有實現信號量)上實現信號量有關的如下系統調用:

1 sem_t *sem_open(const char *name, unsigned int value);
2 int sem_wait(sem_t *sem);
3 int sem_post(sem_t *sem);
4 int sem_unlink(const char *name);

第一個函數的功能是創建一個名字爲name的信號量或者打開名字爲name的信號量;

第二個函數的功能是信號量的P操作:將信號量計數器減一,表示“申請佔用一個資源”

第三個函數的功能是信號量的V操作:將信號量計數器加一,表示“釋放信號量”

第四個函數的功能是刪除名字爲name的信號量;

2. 利用上面實現的信號量系統調用,編寫bochs上的測試程序"pc.c"來模擬生產者-消費者之間的同步,類似於握手的操作。

根據題例,建立1個生產者進程(producer),5個消費者進程(consumer);用文件建立一個共享緩存區;

生產者依次向緩存區寫入整數0,1,2,...,499,每個消費者進程從緩存區中讀取100個數,對於其讀取的每一個數字均需要打印到stdout上。(須注意,緩存區文件最多隻能保存10個數。)

實現信號量有關的系統調用

首先要先在oslab/linux-0.11/include/linux文件夾下添加sem.h頭文件,需要實現的內容很簡單,就是實現對於信號量的結構定義,此處我們藉助了sched.h中的tss的隊列結構,節省了一些代碼量,內容如下:

#ifndef _SEM_H_
#define _SEM_H_

#include<linux/sched.h>

typedef struct semaphore_t
{
    char name[20];
    int value;
    struct task_struct *queue;
} sem_t;

#endif

 

最主要的是sem.c文件的編寫,此處我們選擇在oslab/linux-0.11/kernel中編寫sem.c(其實在別的地方也一樣,都要修改對應位置的Makefile)

關於sem.c的實現:

#include <unistd.h>
#include <string.h>
#include <linux/sem.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/kernel.h>

#define SEM_LIST_LENGTH 5 //信號量最多多少,原題爲10,此處設爲5

sem_t sem_list[SEM_LIST_LENGTH] = {
    {"\0",0,NULL}, {"\0",0,NULL}, {"\0",0,NULL}, {"\0",0,NULL}, {"\0",0,NULL}
};

sem_t *sys_sem_open(const char *name, unsigned int value)
{
    if(name == NULL)
    {
        printk("name == NULL\n"); //name not initial
        return NULL;
    }
    char nbuf[20];
    int i = 0;
    for(i = 0; i < 20; i++)
        nbuf[i] = get_fs_byte(name + i); //from asm/segment.h
                                         //從(name+i)中取出一個char
    sem_t * result = NULL;
    for(i = 0; i < SEM_LIST_LENGTH; i++) //遍歷查找
    {
        if(sem_list[i].name[0] == '\0')
            break;
        if(!strcmp(sem_list[i].name, nbuf))
        {
            result = &sem_list[i];
            printk("Found sem %s.\n", result->name);
            return result; //找到了,返回
        }
    }
    // 沒找到,新創建一個
    strcpy(sem_list[i].name, nbuf);
    sem_list[i].value = value;
    sem_list[i].queue = NULL;
    result = &sem_list[i];
    printk("Have created a sem %s, value = %d.\n", result->name, result->value);
    return result;
}

//P operation
//0 succeed, 1 fail
int sys_sem_wait(sem_t * sem)
{
    if(sem == NULL || sem < sem_list || sem > sem_list + SEM_LIST_LENGTH)
    {
        printk("P(sem) error!\n"); //非有效地址
        return -1;
    }
    cli(); //close trap
    while(sem->value <= 0)
        sleep_on(&(sem->queue));
    sem->value--;
    sti(); //open trap
    return 0;
}

//V operation
//0 succeed, 1 fail
int sys_sem_post(sem_t * sem)
{
    if(sem == NULL || sem < sem_list || sem > sem_list + SEM_LIST_LENGTH)
    {
        printk("V(sem) error!\n");
        return -1;
    }
    cli(); //close trap
    sem->value++;
    if(sem->value <= 1) //ATTENTION! 前面加1了這裏應是和1比較
        wake_up(&(sem->queue));
    sti();
    return 0;
}

//delete sem(name)
//0 succeed, 1 fail
int sys_sem_unlink(const char *name)
{
    if(name == NULL)
        return -1;
    char nbuf[20];
    int i = 0;
    for(i = 0; i < 20; i++)
    {
        nbuf[i] = get_fs_byte(name + i); //同上
        if(nbuf[i] == '\0') break;
    }
    i = 0;
    for(i = 0; i < SEM_LIST_LENGTH; i++)
    {
        if(strcmp(sem_list[i].name, nbuf))
        {
            sem_list[i].name[0] == '\0';
            sem_list[i].value = 0;
            sem_list[i].queue = NULL;
        } //operate
    }
    if(i == SEM_LIST_LENGTH) return -1; //Not Found
    return 0; //succeed
}

 在編寫完這一部分的程序之後,我們記得要去修改對應文件夾裏的Makefile,比如我們這裏是在kernel/文件夾下修改的,那麼我們就要對應地修改kernel/文件夾下的Makefile,增添如下信息:

...

OBJS  = sched.o system_call.o traps.o asm.o fork.o \
    panic.o printk.o vsprintf.o sys.o exit.o \
    signal.o mktime.o sem.o

...

### Dependencies:
sem.s sem.o: sem.c ../include/linux/kernel.h ../include/unistd.h \
       ../include/linux/sem.h ../include/linux/sched.h    

...

然後爲了實現對應的系統調用的完整呈現,我們需要修改linux-0.11/include/unistd.h和linux-0.11/include/linux/sys.h,產生完整的系統調用。

對於unistd.h的修改:

#define __NR_sem_open   72
#define __NR_sem_wait   73
#define __NR_sem_post   74
#define __NR_sem_unlink 75

對於sys.h的修改:

extern int sys_sem_open();
extern int sys_sem_wait();
extern int sys_sem_post();
extern int sys_sem_unlink();

sys_call_table[]加上
, sys_sem_open, sys_sem_wait, sys_sem_post, sys_sem_unlink...

修改系統調用的總數,在kernel/system_call.s中修改:

nr_system_calls = 76

實現完上述修改後,在linux-0.11/下進行make clean並make,出現sync之後則表示make成功,可以進行下一步的操作了。

實現bochs中的生產者、消費者的調用

 爲了方便代碼的編寫,我們可以先將bochs掛載到我們編寫代碼的環境下面,在oslab/的文件夾下采用命令:

sudo ./mount-hdc

然後在hdc/usr/root文件夾下編寫對應的程序,此處我們把他命名爲pc.c程序:

第一個版本是我自己編寫的pc.c:

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>  
#include <sys/stat.h>        
#include <semaphore.h>
#include <sys/types.h>  
#include <sys/wait.h>

#define M 530
#define N 5               /*消費者進程數*/
#define BUFSIZE 10

int main()
{
    sem_t *empty, *full, *mutex;
    int fd, i, j, k, child, data;
    pid_t pid;
    int  buf_out = 0; //pos out
    int  buf_in = 0; //pos in
    empty = sem_open("empty", O_CREAT | O_EXCL, 0644, BUFSIZE);
    full = sem_open("full", O_CREAT | O_EXCL, 0644, 0);
    mutex = sem_open("mutex", O_CREAT | O_EXCL, 0644, 1);
    
    fd = open("buffer.txt", O_CREAT | O_TRUNC | O_RDWR,0666); 
    lseek(fd,BUFSIZE*sizeof(int),SEEK_SET);
    write(fd,(char *)&buf_out,sizeof(int));
    
    if(!(pid = fork())) /*Producer*/
    {
        printf("Producer pid = %d\n", getpid());
        for (i = 0 ; i < M; i++)
        {
            sem_wait(empty);
            sem_wait(mutex);
            /*寫入一個字符*/
            lseek(fd, buf_in*sizeof(int), SEEK_SET); 
            write(fd,(char *)&i,sizeof(int));         
            buf_in = (buf_in + 1) % BUFSIZE;
            sem_post(mutex);
            sem_post(full); /*喚醒消費者*/
        }
        printf("Producer end.\n");
        fflush(stdout);
        return 0;
    }
    else if(pid < 0)
    {
        perror("Fail to fork!\n");
        return -1;
    }
    /*Consumer*/
    for( j = 0; j < N ; j++ )
    {
        if((pid=fork())==0)
        {
            for( k = 0; k < M/N; k++ )
            {
                sem_wait(full);/*一開始爲0會阻塞此處*/
                sem_wait(mutex);
                lseek(fd,BUFSIZE*sizeof(int),SEEK_SET);
                read(fd,(char *)&buf_out,sizeof(int));
                /*讀取數據*/
                lseek(fd,buf_out*sizeof(int),SEEK_SET);
                read(fd,(char *)&data,sizeof(int));

                buf_out = (buf_out + 1) % BUFSIZE;
                lseek(fd,BUFSIZE*sizeof(int),SEEK_SET);
                write(fd,(char *)&buf_out,sizeof(int));

                sem_post(mutex);
                sem_post(empty);/*喚醒生產者*/
                /*Consume*/
                printf("%d:  %d\n",getpid(),data);
                fflush(stdout);
            }
            printf("child-%d: pid = %d end.\n", j, getpid());
            return 0;
        }
        else if(pid<0)
        {
            perror("Fail to fork!\n");
            return -1;
        }
    }
    child = N + 1;
    while(child--)
        wait(NULL);
    /*釋放信號量*/
    sem_unlink("full");
    sem_unlink("empty");
    sem_unlink("mutex");
    /*釋放資源*/
    close(fd);
    return 0;
}

第二個版本是大佬編寫的pc.c:

#define __LIBRARY__
#include <unistd.h>
#include <linux/sem.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/sched.h>

_syscall2(sem_t *, sem_open, const char *, name, unsigned int, value)
_syscall1(int, sem_wait, sem_t *, sem)
_syscall1(int, sem_post, sem_t *, sem)
_syscall1(int, sem_unlink, const char *, name)

sem_t *mutex, *fill, *empty;
const int product_num = 12;
const int buffer = 4;
const int consumer_num = 2;
int product_finished = 0;
int item_consumed = 0;
int main()
{
    int pid;
    int i;
    mutex = sem_open("mutex", 1);
    fill = sem_open("fill", 0);
    empty = sem_open("empty", buffer);
    if (mutex == NULL) {
        printf("null\n");
        return 0;
    }
    if ((pid = fork()))
    {
        printf("pid: %d, producer start\n", pid);
        fflush(stdout);
        while(product_finished <= product_num)
        {
            sem_wait(empty);
            sem_wait(mutex);
            printf("pid: %d, produced item %d\n", pid, product_finished);
            fflush(stdout);
            product_finished++;
            sem_post(fill);
            sem_post(mutex);
        }
    }
    else
    { /* consumer */
        i = consumer_num;
        while(i--)
        {
            if (!(pid = fork()))
            {
                pid = getpid();
                printf("pid: %d, consumer start\n", pid);
                fflush(stdout);
                while(1)
                {
                    sem_wait(fill);
                    sem_wait(mutex);
                    printf("pid: %d, consumed item %d\n", pid, item_consumed);
                    fflush(stdout);
                    item_consumed++;
                    sem_post(empty);
                    sem_post(mutex);
                    if (item_consumed == product_num)
                        return 0;
                }
            }
        }
    }
    return 0;
}
View Code

之後還要修改hdc中的unistd.h,要將這個的unistd.h修改的和linux-0.11下的一致。

修改完之後,我們在oslab文件夾下運行:sudo umount hdc取消hdc的掛載。

測試

最後我們在bochs下運行./run,測試一下信號量的實現過程,在運行之前要對之前的pc.c用gcc編譯一下再運行,運行結果如下所示:

經過以上的操作,我們就把信號量的實現完成了,接下來我們就要正式開始實驗6的任務工作了。

實驗6的實現:

 實踐目標

在開始之前,我們仍然是要先明確我們的實踐目標,這個實踐的目標有兩個:(1)用Bochs調試工具跟蹤Linux 0.11的地址轉換過程;(2)實現基於共享物理頁框的進程間內存共享。

其中第一個實踐的目標比較基於理論上的一些知識,需要我們對於概念的理解有一定的要求;第二個實踐的目標與實踐5有一些相像的地方,均需要通過實現相應的函數來達到進程間內存共享的目的。

跟蹤地址轉換過程-獲得虛擬地址

在之前的環境中是已經配置好了我們的過程所需要的環境的,接下來我們直接跟着實踐所描述的步驟進行操作(這個過程實踐所指導的就已經非常詳細了,以下我們將重視演示過程):

要在Linux 0.11上編寫一個test.c程序,要跟蹤的地址就是這個程序中的地址:

#include<stdio.h>
int i = 0x12345678;
int main(void)
{
    printf("The logical address of i is 0x%08x", &i);
    fflush(stdout);
    wihle(i); //控制程序加載
    return 0;
}

要記得用./dbg-asm加載,以方便我們對於bochs進行調試運行;

此時對其進行編譯執行之後會呈現的內容是"The logical address of i is 0x00003004";

此時打印的是i的邏輯地址,又因爲while(i);語句控制了test程序一直運行,不會主動退出,此時我們可以利用此來進行調試命令查看。

 在非內核的運行狀態下時(如果是內核,就在命令行窗口裏面接着使用c+crtl c的組合操作),此後顯示的是000f:00000062的jz指令,我們需要跟蹤到的指令是cmp指令,所以我們採用n命令單步執行直到cmp指令:

 接下來使用“u/7”命令,顯示從當前位置開始的七條指令的反彙編代碼,然後是要開始尋找和邏輯地址DS:0x3004對應的物理地址,開始跟蹤地址轉換過程,先採用sreg看一下各個寄存器的信息:

 LDTR此時,s=0x68,表示的是當前進程的LDT存在GDT的13號位置上(二進制表示後,低三位爲控制符),此時我們通過GDTR知道GDT的起始位置在0x54b8(不同機器此處可能不同),每一項佔64位(即8個字節),所以要查找的物理地址是0x54b8+13*8,用命令"xp/2w 0x5cb8+13*8"可以查看此處的內容:

 對應的數0xc2d00068 0x000082f9中的標紅加粗數字組合以後爲“0x00f9c2d0”,這裏是根據了x86的格式轉換,具體內容請查看x86文檔,此處注意是高8字節在右,低8字節在左。

 

 

接下來我們就可以通過"xp/8w 0x00f9c2d0"查看LDT表的前四項內容:

 此時DS的索引值爲0x17,表示的是LDT中的第16個字節開始的內容,LDT每項佔8個字節,所以第三項"0x00003fff 0x10c0f300"即爲所要查找的內容,以同樣的方法可以得到DS段在虛擬內存空間中的起始地址,加上偏移量0x3004可得DS:0x3004對應的虛擬地址:0x10000000 + 0x3004 = 0x10003004,可知虛擬地址爲0x10003004

跟蹤地址轉換過程-虛擬地址轉化到物理地址

 這一步的內容核心是查找頁表,主要應用的是頁式管理的內容。我們需要算出虛擬地址中的頁目錄號、頁表號和頁內偏移,他們分別對應32位虛擬地址的前10位、中間10位和末尾12位。

我們已知虛擬地址位0x10003004,所以我們可以通過轉換得知頁目錄號、頁表號和頁內偏移分別爲:64,3,4。

採用creg指令查看CR3寄存器的指向,CR3即指向的頁目錄表的基址,可得知:

 接着我們查看第65個頁目錄項,第三個頁表項,查詢過程如下:

 注意後三位爲控制位,在查找的時候要分辨清楚。最後我們可以知道轉化成的物理地址是:0x00fa7004.

此時查看一下0x00fa7004的值,發現值確實爲test.c中i的初值,說明過程是正確的。

 最後我們通過直接修改內存來改變i的值,使用命令"setpmem 0x00fa7004 4 0"實現,表示從0x00fa7004地址開始的4個字節均設置爲0,然後再用c命令繼續bochs的運行,則此時while(i);就會退出循環,我們看到了進程test成功退出了,說明i的修改成功了,即說明我們的修改是成功的。

 內存的共享的實現

首先,在開始之前,我們先要明確我們的目的,即實現兩個系統調用,來完成內存的共享。這兩個系統調用分別是shmget()和shmat(),在Linux0.11中其本身是沒有這種系統調用的。

系統調用的實現

 我們需要實現的兩個調用爲shmget()和shmat(),兩個調用的原型爲:

int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);

shmget()的調用會新建或者打開一頁的物理內存作爲共享內存,並返回該頁共享內存的shmid,即共享內存在操作系統中的標識。生成的shmid唯一地與key相關,如果多個進程使用相同的key來調用shmget(),則會返回相同的shmid。同時如果size>MAX_SIZE,則返回-1,置errno爲EINVAL,若無空閒內存,也返回-1,置errno爲ENOMEM。

shmat()會將shmid指定的共享頁面映射到當前進程的虛擬地址空間中,並返回一個邏輯地址p,調用進程可以讀寫p來讀寫這一頁的共享內存,兩個進程都調用shmat可以關聯到同一頁內存上,形成共享內存頁結構,實現了基於共享內存的進程間通信。如果shmid非法,返回-1,置errno爲EINVAL。

我們先從添加系統調用號開始:

 

 同時在unistd中添加shm_ds的結構體聲明以及函數調用:

 

 然後我們在include/linux/sys.h中添加系統調用的定義:

 之後我們在kernel/system_call.s中增加系統調用數爲78

 然後我們就可以正式地來編寫shm.c了,此處我們選擇在linux-0.11/kernel/文件夾下編寫:

 

#define __LIBRARY__
#include <unistd.h> //引入調用 
#include <linux/kernel.h>
#include <errno.h> //引入錯誤提示 
#include <linux/sched.h> //引入結構 
#include <linux/mm.h>

static shm_ds shm_list[SHM_SIZE]; //嚴格的來說,其初始值應該默認是0? 

int sys_shmget(unsigned int key, size_t size)
{
    int i;
    unsigned long page;
    if (size > PAGE_SIZE)
    {
        printk("Shmget size overflow (%u > %ud). \n", size, PAGE_SIZE);
        return -ENOMEM; //error1
    }
    if (key == 0)
    {
        printk("Shmget key number cannot be 0.\n");
        return -EINVAL; //error2
    }
    for (i = 0; i < SHM_SIZE; i++) //先進行查詢 
        if (shm_list[i].key == key)
            return i; //查詢到,則(打開這個界面)返回 
            
    page = get_free_page(); //沒查詢到,新建立一個非空頁面 
    if (!page)
        return -ENOMEM;
        
    printk("Shmget get page address: 0x%08x\n", page);
    for (i = 0; i < SHM_SIZE; i++)
    {
        if (shm_list[i].key == 0)
        {
            shm_list[i].key = key; //賦值佔用 
            shm_list[i].size = size;
            shm_list[i].page = page;
            return i;
        }
    }
    return -1;
}

void *sys_shmat(int shmid)
{
    unsigned long data_base, brk;
    if (shmid < 0 || SHM_SIZE <= shmid || shm_list[shmid].page == 0 || shm_list[shmid].key <= 0) //如果此shmid對應地無效 
        return (void *)-EINVAL;
    data_base = get_base(current->ldt[2]);
    printk("Shmat data_base = 0x%08x, 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)) //如果put_page失敗 
        return (void *)-ENOMEM;
    return (void *)(current->brk - PAGE_SIZE); //返回p 
}

具體的內容此處都寫了註釋,關鍵的應用函數是get_free_page(),get_base(),put_page()。

 

 在shmat()函數中,我們利用了在進程的PCB中記錄了brk指針的邏輯地址的信息,通過進程開始的虛擬地址就可以得到brk指針的虛擬地址/線性地址(段基址+邏輯地址)。

 

 

 然後我們修改一下shm.c文件在其中的kernel/下的Makefile:

 

 然後我們在linux-0.11文件夾下make clean之後再make一下,出現sync則表示我們修改內核成功了:

producer&consumer的實現

 此時我們可以採取掛載一下sudo ./mount-hdc,實現較爲便捷的編程。

採用如下命令進入Linux-0.11的root文件夾下編程:

sudo ./mount-hdc
cd /oslab/hdc/usr/root
vim producer.c
vim consumer.c

記得在開始之前,我們要把linux-0.11/中的unistd.h文件拷貝到hdc/usr/root/中去:

(sudo ./mount-hdc) 
cp ./linux-0.11/include/unistd.h ./hdc/usr/include/

接下來我們將編寫producer.c和consumer.c:

producer.c:

#define   __LIBRARY__
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <linux/sem.h>

_syscall2(sem_t*,sem_open,const char *,name,unsigned int,value);
_syscall1(int,sem_wait,sem_t*,sem);
_syscall1(int,sem_post,sem_t*,sem);
_syscall1(int,sem_unlink,const char *,name);
_syscall1(void*,shmat,int,shmid);
_syscall2(int,shmget,int,key,int,size);

#define NUMBER 520
#define BUFSIZE 10

sem_t *empty, *full, *mutex;

int i,shmid;
int *p;
int buf_in = 0;
int main()
{
    if((mutex = sem_open("mutex",1)) == NULL)
    {
        perror("sem open error!\n");
        return -1;
    }
    if((empty = sem_open("empty",10)) == NULL)
    {
        perror("sem open error!\n");
        return -1;
    }
    if((full = sem_open("full",0)) == NULL)
    {
        perror("sem open error!\n");
        return -1;
    }
    shmid = shmget(1234, BUFSIZE);
    printf("Shmid %d\n",shmid);
    if(shmid == -1)
        return -1;
    p = (int*) shmat(shmid);
    
    printf("Producer start.\n");
    fflush(stdout);
    for( i = 0 ; i < NUMBER; i++)
    {
        sem_wait(empty);
        sem_wait(mutex);
        p[buf_in] = i; //輸入數據, produce 
        buf_in = ( buf_in + 1)% BUFSIZE;
        sem_post(mutex);
        sem_post(full);
    }
    printf("Producer end.\n");
    fflush(stdout);
    sem_unlink("full");
    sem_unlink("empty");
    sem_unlink("mutex");
    
    return 0;
}

consumer.c:

#define   __LIBRARY__
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <linux/sem.h>

_syscall2(sem_t*,sem_open,const char *,name,unsigned int,value);
_syscall1(int,sem_wait,sem_t*,sem);
_syscall1(int,sem_post,sem_t*,sem);
_syscall1(int,sem_unlink,const char *,name);
_syscall1(void*,shmat,int,shmid);
_syscall2(int,shmget,int,key,int,size);

#define NUMBER 520
#define BUFSIZE 10

sem_t  *empty, *full, *mutex;
int  i,shmid,data;
int *p;
int  buf_out = 0;
int main()
{
    
    if((mutex = sem_open("mutex",1)) == NULL)
    {
        perror("sem open error!\n");
        return -1;
    }
    if((empty = sem_open("empty",10)) == NULL)
    {
        perror("sem open error!\n");
        return -1;
    }
    if((full = sem_open("full",0)) == NULL)
    {
        perror("sem open error!\n");
        return -1;
    }
    printf("consumer start.\n");
    fflush(stdout);
    
    shmid = shmget(1234, BUFSIZE);
    printf("shmid:%d\n",shmid);
    if(shmid == -1)
        return -1;
    p = (int *)shmat(shmid);

    for (i = 0; i < NUMBER; i++ )
    {
        sem_wait(full);
        sem_wait(mutex);

        data = p[buf_out];
        buf_out = (buf_out + 1) % BUFSIZE;
        sem_post(mutex);
        sem_post(empty);
        
        printf("%d:  %d\n",getpid(),data); //consume
        fflush(stdout);
    }
    printf("consumer end.\n");
    fflush(stdout);
    sem_unlink("full");
    sem_unlink("empty");
    sem_unlink("mutex");

    return 0;
}

之後我們再編譯一下:

gcc -o producer producer.c
gcc -o consumer consumer.c
sudo umount hdc

最後我們在linux-0.11中運行以下命令:

./producer &
./consumer

運行測試:

 

 

 

以上的編寫方式會出現Kernel Panic。

所以我們可以採取以下的方案將Kernel Panic轉化爲Segmentation Fault,通過修改memory.c的內存管理方式,具體的代碼內容在這裏:(內核修改部分+測試程序)。

最終呈現的內容如下所示:

 

 完結撒花!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章