教員佈置的操作系統的第一個大作業,其中有一些知識相對而言比較重要,包括一些操作過程都具有重複實驗的價值;所以寫一個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; }
之後還要修改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的內存管理方式,具體的代碼內容在這裏:(內核修改部分+測試程序)。
最終呈現的內容如下所示:
完結撒花!