基于xv6的类Uinx操作系统实现

01 低级

1.1 添加简单程序

app.c

#include "types.h"
#include "stat.h"
#include "user.h"

int main(int argc, char *argv[]) {
    printf(1, "Hello World\n");
    exit();
}

UPROGS 中添加

image-20221011192416325

make 后生成

image-20221011192506673

运行结果

image-20221011192624816

1.2 系统调用示例

查看pid

image-20221011193106471

1.3 系统调用getcpuid()

  • 编写 pcpuid.c 代码
  • 同样修改 Makefile,添加一行 pcpuid 即可, 系统启动以后直接输入 pcpuid 打印cpu号
  • 在 syscall.h 中添加一行系统调用号 22 号
  • 然后再增加用户态的进入接口,在 user.h 中添加一行 int getcpuid(void); (getcpuid函数在 pcpuid.c中调用了)
  • 在 usys.S 中加入一行 SYSCALL(getcpuid) 以定义用户态的入口。将系统调用号保存在eax寄存器中 触发软中断 int 0x40(int 64)进入内核的中断处理函数

=====================================================================================================================================================================内核与应用层分割线

  • 在 syscall.c 中的分发函数表中添加 getcpuid 函数所对应的表项 [SYS_getcpuid] sys_getcpuid (以eax及系统调用号 在 syscalls[]系统调用表中找到需要执行的代码)
  • 然后修改 syscall.c,添加一行 sys_getcpuid 函数的声明 extern int sys_getcpuid(void);
  • 然后开始实现 sys_getcpuid 函数。在 sysproc.c 中添加 sys_getcpuid 的实现
sys_getcpuid(void) {
  return getcpuid();
}
  • 在 proc.c 中实现内核态的 getcpuid 函数
int getcpuid() {
  return cpunum();
}
  • 最后在defs.h中加入 getcpuid 用作内核态代码调用 getcpuid()时的函数原型 (为了让sysproc.c中的sys_getcpuid()可以调用proc.c中的getcpuid())

运行结果

image-20221012211425369

1.4 观察调度过程

#include "types.h"
#include "stat.h"
#include "user.h"

int main(int argc, char *argv[]) {
    printf(1, "Hello World\n");
    int a;
    a = fork();
    a = fork();
    while(1) {
        a++;
    } 
    exit();
}

实验结果:

image-20221012213415509

可以看出每次在不同的进程运行,说明时间片在轮转

image-20221012213717132

02 中级

2.1 调整时间片长度

运行结果

cprintf(“slice left:%d ticks,%d %s %s”, p->slot, p->pid, state, p->name);

输出4个参数 时间片剩余数 进程号 进程状态 进程名字

image-20221013224300917

2.2 信号量

spinlock.c

int sys_sem_create(void) {
    int n_sem, i;
    if(argint(0, &n_sem) < 0 )
        return -1;
    for(i = 0; i < SEM_MAX_NUM; i++) {
        acquire(&sems[i].lock);
        if(sems[i].allocated == 0) {
            sems[i].allocated = 1;
            sems[i].resource_count = n_sem;
            cprintf("create %d sem\n",i);
            release(&sems[i].lock);
            return i;
        }
        release(&sems[i].lock);
    }
    return -1;
 }


int sys_sem_free(){
    int id;    
    if(argint(0,&id)<0)        
      return -1;
    acquire(&sems[id].lock);    
    if(sems[id].allocated == 1 && sems[id].resource_count > 0){        
        sems[id].allocated = 0;        
        cprintf("free %d sem\n", id);    
    }    
    release(&sems[id].lock);
    return 0;
}

int sys_sem_p()
{       int id;
    if(argint(0, &id) < 0)
      return -1;
    acquire(&sems[id].lock);
    sems[id]. resource_count--;
    if(sems[id].resource_count<0)           //首次进入、或被唤醒时,资源不足
      sleep(&sems[id],&sems[id].lock);        //睡眠(会释放sems[id].lock才阻塞)
    release(&sems[id].lock);                                //解锁(唤醒到此处时,重新持有sems[id].lock)
    return 0;                                               //此时获得信号量资源
}

int sys_sem_v()
{       int id;
    if(argint(0,&id)<0)
      return -1;
    acquire(&sems[id].lock);
    sems[id]. resource_count+=1;            //增1
    if(sems[id].resource_count<1)                   //有阻塞等待该资源的进程
      wakeup1p(&sems[id]);                    //唤醒等待该资源的1个进程
    release(&sems[id].lock);                                //释放锁
    return 0;
}

sh_rw_lock.c文件

#include "types.h"
#include "stat.h"
#include "user.h"

int main(){
	int id=sem_create(1);
	int pid = fork();
	int i;
	for(i=0;i<100000;i++){
		sem_p(id);
		sh_var_write(sh_var_read()+1);
		sem_v(id);
	}
	if(pid >0){
		wait();
		sem_free(id);
	}
	printf(1,"sum=%d\n",sh_var_read());
	exit();
}

运行结果

image-20221020193157565

2.3 进程间通信

2.3.1 共享内存

sharemem.c

#include "types.h"
#include "defs.h"
#include "param.h"
#include "mmu.h"
#include "proc.h"
#include "spinlock.h"
#include "memlayout.h"
#define MAX_SHM_PGNUM (4) 	//每个共享内存最带4页内存

struct sharemem
{
    int refcount;     		//当前共享内存引用数,当引用数为0时才会真正回收
    int pagenum;		//占用的页数(0-4)
    void* physaddr[MAX_SHM_PGNUM];   		//对应每页的物理地址
};
struct spinlock shmlock;    			//用于互斥访问的锁
struct sharemem shmtab[8];  			//整个系统最多8个共享内存

void
sharememinit()
{
    initlock(&shmlock,"shmlock");   //初始化锁
    for (int i = 0; i < 8; i++)     //初始化shmtab
    {
        shmtab[i].refcount = 0;
    }
    
    cprintf("shm init finished.\n");
}

int
shmkeyused(uint key, uint mask)
{
    if(key<0 || 8<=key){
        return 0;
    }
    return (mask >> key) & 0x1;  //这里判断对应的系统共享内存区是否已经启用
}


int
shmrm(int key)
{
    if(key<0||8<=key){
        return -1;
    }
    //cprintf("shmrm: key is %d\n",key);
    struct sharemem* shmem = &shmtab[key];
    for(int i=0;i<shmem->pagenum;i++){
        kfree((char*)P2V(shmem->physaddr[i]));   		//逐个页帧回收
    }
    shmem->refcount = 0;
    return 0;
}


int
shmadd(uint key, uint pagenum, void* physaddr[MAX_SHM_PGNUM])
{
    if(key<0 || 8<=key || pagenum<0 || MAX_SHM_PGNUM < pagenum){
        return -1;
    }
    shmtab[key].refcount = 1;
    shmtab[key].pagenum = pagenum;
    for(int i = 0;i<pagenum;++i){
        shmtab[key].physaddr[i] = physaddr[i];
    }
    return 0;
}

int
mapshm(pde_t *pgdir, uint oldshm, uint newshm, uint sz, void **physaddr)
{
    uint a;
    if(oldshm & 0xFFF || newshm & 0xFFF || oldshm > KERNBASE || newshm < sz)
        return 0;  												//验证参数
    a=newshm;
    for (int i = 0;a<oldshm;a+=PGSIZE, i++) 					//逐页映射
    {
        mappages(pgdir,(char*)a,PGSIZE,(uint)physaddr[i],PTE_W|PTE_U);
    }
    return newshm;
}

int
deallocshm(pde_t *pgdir, uint oldshm, uint newshm)
{
	pte_t *pte;
	uint a, pa;
	if(newshm <= oldshm)
		return oldshm;
	a = (uint)PGROUNDDOWN(newshm - PGSIZE);
	for (; oldshm <= a; a-=PGSIZE)
	{
		pte = walkpgdir(pgdir,(char*)a,0);
		if(pte && (*pte & PTE_P)!=0){
			pa = PTE_ADDR(*pte);
		if(pa == 0){
			panic("kfree");
		}
		*pte = 0;
		}
	}
	return newshm;
}

// 这个方法和allcouvm实现基本一样
int
allocshm(pde_t *pgdir, uint oldshm, uint newshm, uint sz,void *phyaddr[MAX_SHM_PGNUM])
{
    char *mem;
    uint a;
    
    if(oldshm & 0xFFF || newshm & 0xFFF || oldshm > KERNBASE || newshm < sz)
        return 0;
    a = newshm;
    for (int i = 0; a < oldshm; a+=PGSIZE, i++)
    {
        mem = kalloc(); 		//分配物理页帧
        if(mem == 0){
            // cprintf("allocshm out of memory\n");
            deallocshm(pgdir,newshm,oldshm);
            return 0;
        }
        memset(mem,0,PGSIZE);
        mappages(pgdir,(char*)a,PGSIZE,(uint)V2P(mem),PTE_W|PTE_U);	//页表映射
        phyaddr[i] = (void *)V2P(mem);
        // cprintf("allocshm : %x\n",a);
    }
    return newshm;
}


void*
shmgetat(uint key, uint num)
{
    pde_t *pgdir;
    void *phyaddr[MAX_SHM_PGNUM];
    uint shm =0;
    if(key<0||8<=key||num<0||MAX_SHM_PGNUM<num) 	//校验参数
        return (void*)-1;
    acquire(&shmlock);
    pgdir = proc->pgdir;
shm = proc->shm;

// 情况1.如果当前进程已经映射了该key的共享内存,直接返回地址
    if(proc->shmkeymask>>key & 1){ 
        release(&shmlock);
        return proc->shmva[key];
}

// 情况2.如果系统还未创建此key对应的共享内存,则分配内存并映射
    if(shmtab[key].refcount == 0){
        shm = allocshm(pgdir, shm, shm - num * PGSIZE, proc->sz, phyaddr); 
//新增的allocshm()分配内存并映射,其原理和allcouvm()相同
        if(shm == 0){
            release(&shmlock);
            return (void*)-1;
        }
        proc->shmva[key] = (void*)shm;
        shmadd(key, num, phyaddr);	//将新内存区信息填入shmtab[8]数组
    }else { 

//情况3.如果未持有且已经系统中分配此key对应的共享内存,则直接映射
        for(int i = 0;i<num;i++)
        {
            phyaddr[i] = shmtab[key].physaddr[i];
        }
        num = shmtab[key].pagenum;
		//mapshm方法新建映射
        if((shm = mapshm(pgdir,shm,shm-num*PGSIZE,proc->sz,phyaddr))==0){
            release(&shmlock);
            return (void*)-1;
        }
        proc->shmva[key] = (void*)shm;
        shmtab[key].refcount++;			//引用计数+1
    }
    proc->shm = shm;
    proc->shmkeymask |= 1<<key;
    release(&shmlock);
    return (void*)shm;
}

void
shmaddcount(uint mask)
{
	acquire(&shmlock);
	for (int key = 0; key < 8; key++)
	{
	if(shmkeyused(key,mask)){   //对目前进程所有引用的共享内存的引用数加1
		shmtab[key].refcount++;   
		}
	}
	release(&shmlock);
}

int
shmrelease(pde_t *pgdir, uint shm, uint keymask)
{
    //cprintf("shmrelease: shm is %x, keymask is %x.\n",shm, keymask);
    acquire(&shmlock);
    deallocshm(pgdir,shm,KERNBASE); 				//释放用户空间的内存
    for (int k = 0; k < 8; k++)
    {
        if(shmkeyused(k,keymask)){
            shmtab[k].refcount--;   				//引用数目减1
            if(shmtab[k].refcount==0){  			//若为0 ,即可以回收物理内存
                shmrm(k);    
            }
        }
    }
    release(&shmlock);
    return 0;
}

int
shmrefcount(uint key)
{
    acquire(&shmlock);
    int count;
    count = (key<0||8<=key)? -1:shmtab[key].refcount;
    release(&shmlock);
    return count;
}


test.c

#include "types.h"
#include "stat.h"
#include "user.h"
#include "fs.h"
int main(void)
{
  char *shm;
  int pid = fork();
  if(pid == 0){
    sleep(1);
    shm = (char*)shmgetat(1,3);//key为1,大小为3页的共享内存
    printf(1,"child  process pid:%d shm is %s refcount of 1 is:%d\n", getpid(), shm, shmrefcount(1));
    strcpy(shm, "hello_world!");
    printf(1, "child  process pid:%d write %s into the shm\n", getpid(), shm);
  } else if (pid > 0) {
    shm = (char*)shmgetat(1,3);
    printf(1,"parent process pid:%d before wait() shm is %s refcount of 1 is:%d\n", getpid(), shm, shmrefcount(1));
    strcpy(shm, "share_memory!");
    printf(1,"parent process pid:%d write  %s into the shm\n", getpid(), shm);
    wait();
    printf(1,"parent process pid:%d after wait() shm is %s refcount of 1 is:%d\n", getpid(), shm, shmrefcount(1));
  }
  exit();
}

image-20221020202119861

2.3.2 消息队列

messagequeue.c

#include "types.h"
#include "defs.h"
#include "param.h"
#include "mmu.h"
#include "proc.h"
#include "spinlock.h"

struct msg {
    struct msg *next;
    long type;
    char *dataaddr;
    int  datasize;
};

struct mq {
    int key;
    int status;
    struct msg *msgs;
    int maxbytes;
    int curbytes;
    int refcount;
};

struct spinlock mqlock;
struct mq mqs[MQMAX];
struct proc* wqueue[NPROC];
int wstart=0;

struct proc* rqueue[NPROC];
int rstart=0;

void
mqinit()
{
    cprintf("mqinit.\n");
    initlock(&mqlock,"mqlock");
    for(int i =0;i<MQMAX;++i){
        mqs[i].status = 0;
    }
}

int findkey(int key)
{
    int idx =-1;
    for(int i = 0;i<MQMAX;++i){
        if(mqs[i].status != 0 && mqs[i].key == key){
            idx = i;
            break;
        }
    }
    return idx;
}

int newmq(int key)
{
    int idx =-1;
    for(int i=0;i<MQMAX;++i){
        if(mqs[i].status == 0){
            idx = i;
            break;
        }
    }
    if(idx == -1){
        cprintf("newmq failed: can not get idx.\n");
        return -1;
    }
    mqs[idx].key = key;
    mqs[idx].status = 1;
    mqs[idx].msgs = (struct msg*)kalloc();
    if(mqs[idx].msgs == 0){
        cprintf("newmq failed: can not alloc page.\n");
        return -1;
    }
    memset(mqs[idx].msgs,0,PGSIZE);
    mqs[idx].msgs -> next = 0;
    mqs[idx].msgs -> datasize = 0;
    mqs[idx].maxbytes = PGSIZE;
    mqs[idx].curbytes = 16;
    mqs[idx].refcount = 1;
    proc->mqmask |= 1 << idx;
    return idx;

}

int
mqget(uint key)
{
    acquire(&mqlock);
    int idx = findkey(key);
    if(idx != -1){
        if(!(proc->mqmask >> idx & 1)){
            proc->mqmask |= 1 << idx;
            mqs[idx].refcount++;
        }
        release(&mqlock);
        return idx;
    }
    idx = newmq(key);
    release(&mqlock);
    return idx;
}
int
msgsnd(uint mqid, void* msg, int sz)
{
    if(mqid<0 || MQMAX<=mqid || mqs[mqid].status == 0){
        return -1;
    }

    char *data = (char *)(*((int *) (msg + 4)));
    int  *type = (int *)msg;

    if(mqs[mqid].msgs == 0){
        cprintf("msgsnd failed: msgs == 0.\n");
        return -1;
    }

    acquire(&mqlock);

    while(1){
        if(mqs[mqid].curbytes + sz + 16 <= mqs[mqid].maxbytes){
            struct msg *m = mqs[mqid].msgs;
            while(m->next != 0){
                m = m -> next;
            }
            m->next = (void *)m + m->datasize + 16;
            m = m -> next;
            m->type = *(type);
            m->next = 0;
            m->dataaddr = (void*)m + 16;
            m->datasize = sz;
            memmove(m->dataaddr, data, sz);
            mqs[mqid].curbytes += (sz+16);

            for(int i=0; i<rstart; i++)
            {
                wakeup(rqueue[i]);
            }
            rstart = 0;

            release(&mqlock);
            return 0;
        } else {
            cprintf("msgsnd: can not alloc: pthread: %d sleep.\n",proc->pid);
            wqueue[wstart++] = proc;

            sleep(proc,&mqlock);
        }
        
    }

    return -1;
}



int reloc(int mqid)
{
    struct msg *pages = mqs[mqid].msgs;
    struct msg *m  = pages;
    struct msg *t;
    struct msg *pre = pages;
    while (m != 0)
    {
        t = m->next;
        memmove(pages, m, m->datasize+16);
        pages->next = (struct msg *)((char *)pages + pages->datasize + 16);
        pages->dataaddr = ((char *)pages + 16);
        pre = pages;
        pages = pages->next;
        m = t;
    }
    pre->next = 0;
    return 0;
}



int
msgrcv(uint mqid, void* msg, int sz)
{
    if(mqid<0 || MQMAX<=mqid || mqs[mqid].status ==0){
        return -1;
    }
    int *type = msg;
    int *data = msg + 4;
    acquire(&mqlock);
    
    while(1){
        struct msg *m = mqs[mqid].msgs->next;
        struct msg *pre = mqs[mqid].msgs;
        while (m != 0)
        {
            if(m->type == *type){
                memmove((char *)*data, m->dataaddr, sz);
                pre->next = m->next;
                mqs[mqid].curbytes -= (m->datasize + 16);
                reloc(mqid);

                for(int i=0; i<wstart; i++)
                {
                    wakeup(wqueue[i]);
                }
                wstart = 0;

                release(&mqlock);
                return 0;
            }
            pre = m;
            m = m->next;
        }
        cprintf("msgrcv: can not read: pthread: %d sleep.\n",proc->pid);
        rqueue[rstart++] = proc;
        sleep(proc,&mqlock);
    }
    return -1;
}

void
rmmq(int mqid)
{
    kfree((char *)mqs[mqid].msgs);
    mqs[mqid].status = 0;
}

void
releasemq2(int mask)
{
    acquire(&mqlock);
    for(int id = 0;id<MQMAX;++id){
        if( mask >> id & 0x1){
            mqs[id].refcount--;
            if(mqs[id].refcount == 0){
                rmmq(id);
            }
        }
    }
    release(&mqlock);
}


void
releasemq(uint key)
{
    //cprintf("releasemq: %d.\n",key);
  int idx= findkey(key);
  if (idx!=-1){
      acquire(&mqlock);
          mqs[idx].refcount--;   //引用数目减1
            if(mqs[idx].refcount == 0)  //引用数目为0时候需要回收物理内存
                rmmq(idx);
       release(&mqlock);
     }
}



void
addmqcount(uint mask)
{
    acquire(&mqlock);
    for (int key = 0; key < MQMAX; key++)
    {
        if(mask >> key & 1){
            mqs[key].refcount++;
        }
    }
    release(&mqlock);
}

msg_test.c

#include "param.h"
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fs.h"
#include "fcntl.h"
#include "syscall.h"
#include "traps.h"
#include "memlayout.h"


struct msg{
  int type;
  char *dataaddr;
}s1,s2,g;

void msg_test()
{
  int mqid = mqget(123);
  // int msg_len = 48;
  // s1.dataaddr = "total number:47 : hello, this is child process.";
  int pid = fork();
  if(pid == 0){
    s1.type = 1;
    s1.dataaddr = "This is the first message!";
    msgsnd(mqid, &s1, 27);
    s1.type = 2;
    s1.dataaddr = "Hello, another message comes!";
    msgsnd(mqid, &s1, 30);
    s1.type = 3;
    s1.dataaddr = "This is the third message, and this message has great many characters!";
    msgsnd(mqid, &s1, 70);
    // sleep(10);
    // for(int i=0; i<70; i++)
    // {
    //   s1.type = i;
    //   msgsnd(mqid, &s1, msg_len);
    // }
    printf(1,"all messages have been sent.\n");
  } else if (pid >0)
  {
    // sleep(10);     // sleep保证子进程消息写入
    // g.dataaddr = malloc(msg_len);
    // for(int i=0; i<70; i++)
    // {
    //   g.type = i;
    //   msgrcv(mqid, &g, msg_len);
    //   printf(1, "读取第%d个消息: %s\n", i, g.dataaddr);

    // }
    sleep(10);      // sleep保证子进程消息写入
    g.dataaddr = malloc(70);
    g.type = 2;
    msgrcv(mqid,&g, 30);
    printf(1, "receive the %dth message: %s\n", 2, g.dataaddr);
    g.type = 1;
    msgrcv(mqid,&g, 27);
    printf(1, "receive the %dth message: %s\n", 1, g.dataaddr);
    g.type = 3;
    msgrcv(mqid,&g, 70);
    printf(1, "receive the %dth message: %s\n", 3, g.dataaddr);

    wait();

  }
  exit();
}


int
main(int argc, char *argv[])
{
  // printf(1, "消息队列测试\n");
  msg_test();
  exit();
}

运行结果

image-20221021200348259

2.4 内存管理

2.4.1 实现myfree()和myalloc()系统调用

myalloc.c

#include "types.h"
#include "stat.h"
#include "user.h"


int
main(int argc, char *argv[]) {
  // int pid = getpid();   
  // map(pid);
  
  char* m1 = (char*)myalloc(2 * 4096);
  char* m2 = (char*)myalloc(3 * 4096);
  char* m3 = (char*)myalloc(1 * 4096);
  char* m4 = (char*)myalloc(7 * 4096);
  char* m5 = (char*)myalloc(9 * 4096);

  m1[0] = 'h';
  m1[1] = '\0';


  printf(1,"m1:%s\n",m1);
  myfree(m2);

  //m2[1] = 'p';

  myfree(m4);
  
  // map(pid);
  sleep(5000);
  myfree(m1);
  myfree(m3);
  myfree(m5);
  // char *p=(char *)0x0000;
  // for(int i=0x0000;i<0x08;i++)
  //     *(p+i)='*';

  // printf(1,"This string shouldn't be modified!\n");
  // exit();


  exit();
}

运行结果:

image-20221021211837168

遇见问题:

image-20221021212021956

解决:

image-20221021212625546

03 高级

(待更新)

3.1 实现xv6内核线程

创建线程的开销比创建进程要小,线程可以共享进程的主要资源,例如内存映像、打开的文件等。在xv6上实现线程所涉及的主要工作如下:

  • 实现clone系统调用,用于创建一个内核线程
  • 实现join系统调用,用于回收一个内核线程
  • 实现用户线程库,封装对线程的管理,而用户只需要知道接口即可
  • 提供测试样例,包括共享进程空间、多线程并行

注意:建立在2.4.1基础上

3.2 文件系统

文件系统方面主要实现了三个功能

  • 为xv6文件系统增加文件读写权限控制
  • 实现恢复被删除的文件内容
  • 和设备有关的磁盘裸设备的读写

3.3 虚拟内存

实现的虚存交换机制比较简陋,换出的页帧内容保存到磁盘文件系统的普通文件数据区,而不是Linux那样使用独立的交换分区获得交换文件。换出的页帧所在盘块号直接保存在其pte的高位,其pte低12位仍用作标志用途。

演示了在一个进程启动后,分配和使用的内存总量超过系统剩余的总量时,呈现的进程内部的交换过程。

04 总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-特立独行的猪-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值