HomePostAbout
thumbnail
XV6 운영체제 개선해 보기 <Virtual memory, mmap() 구현하기>
OS
2023.09.05.

해당 포스트는 MIT의 교육용 운영체제 XV6를 활용하며, 코드 저작권은 MIT에 있음을 밝힙니다.

목표

Virtual memory에 해당하는 mmap() 함수 구현하기

과정

0. usys.S 에 시스템 콜 추가

...생략
SYSCALL(ps)
SYSCALL(mmap)
SYSCALL(munmap)
SYSCALL(freemem)

1.user.h에 함수 추가

...생략
uint mmap(uint, int, int, int, int, int);
int munmap(uint);
int freemem(void);

2.syscall.h 에 코드 추가

...생략
#define SYS_mmap  25
#define SYS_munmap  26
#define SYS_freemem  27

3.syscall.c에 코드 추가

...생략
extern int sys_mmap(void);
extern int sys_munmap(void);
extern int sys_freemem(void);

4. sysproc.c에 함수 추가

...생략
int 
sys_mmap(void) 
{
  //uint mmap(uint addr, int length, int prot, int flags, int fd, int offset)
  int addr;
  int length;
  int prot;
  int flags;
  int fd;
  int offset;

  if ( (argint(0, &addr) < 0) || (argint(1, &length) < 0) || (argint(2, &prot) < 0) || (argint(3, &flags) < 0) || (argint(4, &fd) < 0) || (argint(5, &offset) < 0) ){
    return 0;
  }
  
  return mmap(addr, length, prot, flags, fd, offset);
}

int
sys_munmap(void)
{
  int addr;
  if(argint(0, &addr) < 0) {
    return -1;
  }
  return munmap(addr);
}

int
sys_freemem(void){
  return freemem();
}

각 전달되는 인자에 대해 argint()가 음수가 반환 될 시 오류값을 반환 하도록하였다.

5. param.h에 코드 추가

...생략
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define MAP_ANONYMOUS 0x1
#define MAP_POPULATE 0x2

6. vm.c 변경

pte_t *
walkpgdir(pde_t *pgdir, const void *va, int alloc)
{}
...생략
int
mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm)
{}
...생략

walkpgdir()과 mappages()를 사용해야 하기에, 기존의 static을 지워서 다른 곳에서도 쓸 수 있도록 하였다.

7. defs.h 추가

...생략
int             freemc(void);
...생략
uint            mmap(uint, int, int, int, int, int);
int             pfh(uint, uint);
int             munmap(uint);
int             freemem(void);
...생략
int             mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm);
pte_t *        walkpgdir(pde_t *pgdir, const void *va, int alloc);

새롭게 사용하게 되는 함수, 기존의 static을 바꾼 함수 등을 추가해 주었다.

8. types.h 수정

...생략
typedef uint pte_t;

proc.c에서 사용하게될 타입 pte_t를 추가해 준다.

9. proc.h

...생략
struct mmap_area {
  struct file *f;
  uint addr;
  int length;
  int offset;
  int prot;
  int flags;
  struct proc *p;
  int is_used;
};

mmap_area를 추가해 준다. 여기서 is_used 는 해당 mmap_area가 유효한지를 알려주는 변수이다.

10. proc.c

10.1. 초기 선언

...생략
#include "sleeplock.h"
#include "fs.h"
#include "file.h"

#define MMAPBASE 0x40000000
struct mmap_area ma[64];

추가적으로 필요한 h파일들과 MMAPBASE, struct mmap_area ma[64]; 등을 추가해 준다. 여기서 ma는 자동으로 초기화된다. 64는 “Maximum number of mmap_area array is 64.”를 참고하였다.

10.2. mmap 함수 추가

uint 
mmap(uint addr, int length, int prot, int flags, int fd, int offset)
{
  // cprintf("addr: %d, length: %d, prot: %d, flags: %d, fd: %d, offset: %d\n", addr, length, prot, flags, fd, offset);
  
  struct proc *p = myproc();
  uint start_addr = addr + MMAPBASE;
  
  // cprintf("start_addr : %d\n",start_addr);

  struct file* f = 0;
  if (fd != -1){
    f = p->ofile[fd];
  }


  int anony = 0;
  int populate = 0;
  int prot_read = 0;
  int prot_write = 0;
  char* mem = 0;


  if (flags & MAP_ANONYMOUS) anony = 1;
  if (flags & MAP_POPULATE) populate = 1;
  if (prot & PROT_READ) prot_read = 1;
  if (prot & PROT_WRITE) prot_write = 1; 

  // cprintf("prot & PROT_READ: %d prot & PROT_WRITE: %d\n", prot & PROT_READ, prot & PROT_WRITE);

  if ((!anony) && (fd==-1)) {
    // cprintf("error It's not anonymous, but when the fd is -1\n");
    return 0;
  }
  if (f != 0){
    if (!(f->readable) && prot_read) {
      // cprintf("error protection of the file and the prot of the parameter are different\n");
      return 0;
    }
    if (!(f->writable) && prot_write) {
      // cprintf("error protection of the file and the prot of the parameter are different\n");
      return 0;
    }
  }


  int i = 0;
  while(ma[i].is_used != 0){
    i++;
  }


  if (f){
    f = filedup(f);
  }

  ma[i].f = f;
  ma[i].addr = start_addr;
  ma[i].length = length;
  ma[i].offset = offset;
  ma[i].prot = prot;
  ma[i].flags = flags;
  ma[i].p = p;
  ma[i].is_used = 1;
  
  if ((!anony) && (!populate)) {
    // cprintf("it's not ANONY, not POPULATE\n");
    return start_addr;
    //to page fault, late phy mem alloc
  }
  
  if ((anony) && (!populate)){
    // cprintf("it's ANONY, not POPULATE\n");
    return start_addr;
    //to page fault, late phy mem alloc
  }
  
  if ((!anony) && (populate)){
    // cprintf("it's not ANONY, POPULATE\n");
    f->off = offset;
    uint ptr = 0;

    for(ptr=start_addr; ptr<start_addr+length; ptr+=PGSIZE){
      mem = kalloc();
      if(!mem) return 0;
      memset(mem, 0, PGSIZE);
      fileread(f, mem, PGSIZE);
      int perm = prot|PTE_U;
      int ret = mappages(p->pgdir, (void*)(ptr), PGSIZE, V2P(mem), perm);
      if (ret == -1) return 0;
    }
    return start_addr;
  }

  if ((anony) && (populate)){
    // cprintf("it's ANONY, POPULATE\n");
    uint ptr = 0;
    for(ptr=start_addr; ptr<start_addr+length; ptr+=PGSIZE){
      mem = kalloc();
      if(!mem) return 0;
      memset(mem, 0, PGSIZE);
      int perm = prot|PTE_U;
      int ret = mappages(p->pgdir, (void*)(ptr), PGSIZE, V2P(mem), perm);
      if (ret == -1) return 0;
    }
    return start_addr;
  }

  return start_addr;
}

start_addr = addr + MMAPBASE;를 해준다. 그리고 fd != -1 인경우 파일을 f = p->ofile[fd]; 로 불러온다. 다음으로 오류 반환값을 처리하는데, 다음과 같이 구현하였다.

  • Failed: return 0

  • It’s not anonymous, but when the fd is -1 if ((!anony) && (fd==-1)) { return 0; }

  • The protection of the file and the prot of the parameter are different if (!(f->readable) && prot_read) { return 0; } if (!(f->writable) && prot_write) { return 0; }

  • if(!mem) return 0; kalloc()이 실패할 경우

  • int ret = mappages(p->pgdir, (void*)(ptr), PGSIZE, V2P(mem), perm); if (ret == -1) return 0; mappages가 실패할 경우

해당 과정이 모두 끝나면 filedup()을 진행하고 mmap_area를 할당해 준다. 다음으로 flag에 대해서 다음과 같이 처리하였다. POPULATE가 아닌 경우 따로 물리 메모리를 설정하지 않으며, mmap_area만 채워주고 start_addr 만 바로 반환한다. POPULATE인 경우, kalloc()으로 물리 페이지 mem을 할당한 다음 0으로 채운다. 이때 ANONYMOUS가 아닌 경우 fileread()를 통해 실제 파일 값을 페이지 단위로 읽어 들여서 mem에 저장하고 mappages로 페이지 테이블에 등록한다. 유저가 사용하므로 prot|PTE_U를 추가하였다. ANONYMOUS인 경우, 0으로 초기화만 하고 fileread()를 제외한 나머지는 동일하게 수행한다. 최종적으로는 start_addr를 리턴한다.

10.3. Page Fault Handler 구현

10.3.1 trap.c

...생략
case T_PGFLT:
    if(pfh(rcr2(), tf->err) != -1){
      break;
    }

interrupt 14, T_PGFLT에 따라 trap.c에서 해당 케이스를 추가해 주었다. 해당 케이스에 해당하면 Page Fault Handler 함수를 호출하고, 만약 실패할 경우 -1을 리턴한다. 인자로는 rcr2()와 tf->err를 준다.

10.3.2 pfh(Page Fault Handler)함수 구현

int 
pfh(uint addr, uint err){
  struct proc *p = myproc();
  int find_idx = -1;


  for (int t=0;t<64;t++){
    if (((ma[t].addr + ma[t].length) > addr) && (addr >= ma[t].addr)){
      if ((ma[t].p == p) && (ma[t].is_used == 1)){
        find_idx = t;
        break;
      }
    }
  }

  if(find_idx == -1){
    //cprintf("pfh error: no such address!\n");
    return -1;
  }


  int anony = 0;
  int prot_write = 0;
  char* mem = 0;


  if (ma[find_idx].flags & MAP_ANONYMOUS) anony = 1;
  if (ma[find_idx].prot & PROT_WRITE) prot_write = 1; 

  // can't write(it is read) but try to write
  if ((prot_write == 0) && ((err & 2) != 0)){
    //cprintf("error: can't write but try to write\n");
    return -1;
  }


  cprintf("\npage fault ... %x\n", addr);

  uint start_addr = ma[find_idx].addr;
  uint length = ma[find_idx].length;
  uint ptr = 0;

  if(!anony) {
    // cprintf("\tit's not ANONY, POPULATE\n");
    struct file* f = ma[find_idx].f;
    f->off = ma[find_idx].offset;
    for(ptr=start_addr; ptr<start_addr+length; ptr+=PGSIZE){
      if ((ptr <= addr) && (addr < ptr + PGSIZE)){       
        mem = kalloc();
        if(!mem) return 0;
        memset(mem, 0, PGSIZE);
        fileread(f, mem, PGSIZE);
        int perm = ma[find_idx].prot | PTE_U;
        if (prot_write) perm = perm | PTE_W;
        int ret = mappages(p->pgdir, (void*)ptr, PGSIZE, V2P(mem), perm);
        if (ret == -1) return 0;
      }
      f->off+=PGSIZE;
    }
  }
  else{
    // cprintf("it's ANONY, POPULATE\n");
    for(ptr=start_addr; ptr<start_addr+length; ptr+=PGSIZE){
      if ((ptr <= addr) && (addr < ptr + PGSIZE)){  
        mem = kalloc();
        if(!mem) return 0;
        memset(mem, 0, PGSIZE);
        int perm = ma[find_idx].prot | PTE_U;
        if (prot_write) perm = perm | PTE_W;
        int ret = mappages(p->pgdir, (void*)ptr, PGSIZE, V2P(mem), perm);
        if (ret == -1) return 0;
      }
    }
  }


  return 0;
}

반복문을 돌며 mmap_area 중 addr의 범위에 맞으며 proc이 자신것과 동일하고, 사용중인 것을 찾는다. 만약 없을 경우 return -1 를 수행한다. (prot_write == 0) 로 쓰기가 금지되어 있는데 (err & 2) != 0 로 쓰기를 시도한 경우(읽기가 아닌 경우) -1을 반환한다. ANONYMOUS의 여부에 따라 페이지 할당을 시작한다. 여기서, 받은 addr이 해당하는 페이지를 찾고, if ((ptr <= addr) && (addr < ptr + PGSIZE)), 해당 페이지를 kalloc() , memset(), fileread(f, mem, PGSIZE), mappages() 처리를 해준다. 이때, if (prot_write) perm = perm | PTE_W; 를 수행하였다.

10.4. munmap() 구현

int 
munmap(uint addr)
{
  struct proc *p = myproc();
  int find_idx = -1;

  for (int t=0;t<64;t++){
    if (addr == ma[t].addr){
      if ((ma[t].p == p) && (ma[t].is_used == 1)){
        find_idx = t;
        break;
      }
    }
  }

  if(find_idx == -1){
    //cprintf("error: unmap no such address!\n");
    return -1;
  }

  uint ptr = 0;
  pte_t* pte;

  for(ptr = addr; ptr < addr+ma[find_idx].length; ptr += PGSIZE){
    pte = walkpgdir(p->pgdir, (char*)(ptr), 0);
    if(!pte) continue; // page fault has not been occurred on that address, just remove mmap_area structure.
    if(!(*pte & PTE_P)) continue;
    uint paddr = PTE_ADDR(*pte);
    char *v = P2V(paddr);
    kfree(v);
    *pte = 0;
  }
  ma[find_idx].f = 0;
  ma[find_idx].addr = 0;
  ma[find_idx].length = 0;
  ma[find_idx].offset = 0;
  ma[find_idx].prot = 0;
  ma[find_idx].flags = 0;
  ma[find_idx].p = 0;
  ma[find_idx].is_used = 0;
  return 1;
}

구현시 참고한 함수

int
deallocuvm(pde_t *pgdir, uint oldsz, uint newsz)
{
  pte_t *pte;
  uint a, pa;

  if(newsz >= oldsz)
    return oldsz;

  a = PGROUNDUP(newsz);
  for(; a  < oldsz; a += PGSIZE){
    pte = walkpgdir(pgdir, (char*)a, 0);
    if(!pte)
      a = PGADDR(PDX(a) + 1, 0, 0) - PGSIZE;
    else if((*pte & PTE_P) != 0){
      pa = PTE_ADDR(*pte);
      if(pa == 0)
        panic("kfree");
      char *v = P2V(pa);
      kfree(v);
      *pte = 0;
    }
  }
  return newsz;
}

munmap() 구현은 deallocuvm()를 많이 참고하였다. 우선 if (addr == ma[t].addr) 을 만족하고 자신의 프로세스가 맞으며, ma[t].is_used = 1 인 mmap_area를 찾는다. 만약 이것이 없으면 return -1 를 수행한다. 이어서 free를 진행하는데, 우선 walkpgdir로 페이지 테이블을 순회하며 각 페이지가 있는지 확인한다. 만약 특정 페이지가 없으면, if(!pte) continue;를 통해 그냥 지나치고 마지막 mmap_area 초기화로 간다. 또한 pte & PTE_P 도 확인한다. 만약 특정 페이지가 있으면, PTE_ADDR(pte)를 통해 주소를 얻고 P2V()를 통해 가상 주소로 바꾼다음 kfree()로 해제한다. pte의 경우 *pte = 0;로 해제한다. 최종적으로는 mmap_area의 값을 모두 0으로 초기화 해준다.

10.5. freemem() 구현

int freemem()
{
  return freememCount();
}

freemem()의 경우, kalloc()에서 직접 계산한 freememC값을 전달받아 반환하도록 하였다.


uint freememC;

void
kfree(char *v)
{
  struct run *r;

  if((uint)v % PGSIZE || v < end || V2P(v) >= PHYSTOP)
    panic("kfree");

  // Fill with junk to catch dangling refs.
  memset(v, 1, PGSIZE);
  freememC++;
  
  if(kmem.use_lock)
    acquire(&kmem.lock);
  r = (struct run*)v;
  r->next = kmem.freelist;
  kmem.freelist = r;
  if(kmem.use_lock)
    release(&kmem.lock);
}

char*
kalloc(void)
{
  struct run *r;
  freememC--;

  if(kmem.use_lock)
    acquire(&kmem.lock);
  r = kmem.freelist;
  if(r)
    kmem.freelist = r->next;
  if(kmem.use_lock)
    release(&kmem.lock);
  return (char*)r;
}

int freememCount(void)
{
  return freememC;
}

kfree()가 호출될 시 freememC++;를 진행하고, kalloc()시 freememC--;를 진행했다.

10.6. fork 수정

int
fork(void)
{
  int i, pid;
  struct proc *np;
  struct proc *curproc = myproc();

  // Allocate process.
  if((np = allocproc()) == 0){
    return -1;
  }

  // Copy process state from proc.
  if((np->pgdir = copyuvm(curproc->pgdir, curproc->sz)) == 0){
    kfree(np->kstack);
    np->kstack = 0;
    np->state = UNUSED;
    return -1;
  }
  np->vruntime = curproc->vruntime; //==========================================
  np->sz = curproc->sz;
  np->parent = curproc;
  *np->tf = *curproc->tf;

  // Clear %eax so that fork returns 0 in the child.
  np->tf->eax = 0;

  for(i = 0; i < NOFILE; i++)
    if(curproc->ofile[i])
      np->ofile[i] = filedup(curproc->ofile[i]);
  np->cwd = idup(curproc->cwd);

  safestrcpy(np->name, curproc->name, sizeof(curproc->name));

  for (int i = 0; i<64; i++){
    if ((ma[i].is_used == 1) && (ma[i].p == curproc)){
      for(int t = 0;t<64;t++){
        if (ma[t].is_used == 0){
          ma[t].f = ma[i].f;
          ma[t].addr = ma[i].addr;
          ma[t].length = ma[i].length;
          ma[t].offset = ma[i].offset;
          ma[t].prot = ma[i].prot;
          ma[t].flags = ma[i].flags;
          ma[t].p = np;
          ma[t].is_used = ma[i].is_used ;

          uint ptr = 0;
          uint addr = ma[i].addr;
          pte_t* pte;
          int prot_write = 0;

          char* mem = 0;
          
          for(ptr = addr; ptr < addr+ma[i].length; ptr += PGSIZE){
            pte = walkpgdir(curproc->pgdir, (char*)(ptr), 0);
            if(!pte) continue; // not in pte pass
            if(!(*pte & PTE_P)) continue;
            mem = kalloc();
            if(!mem) return 0;
            memset(mem, 0, PGSIZE);
            memmove(mem, (void*)ptr, PGSIZE);
            int perm = ma[i].prot | PTE_U;
            if (prot_write) perm = perm | PTE_W;
            int ret = mappages(np->pgdir, (void*)ptr, PGSIZE, V2P(mem), perm);
            if (ret == -1) return 0;
          }
          break;
        }
      }
    }
  }

  pid = np->pid;

  acquire(&ptable.lock);

  np->state = RUNNABLE;

  release(&ptable.lock);

  return pid;
}

우선, 중첩 반복문 중 첫번째 for문에서 mmap_area 중 부모(curproc)와 관련된 모든 것들을 방문한다. 두번째 for문에서는 mmap_area중 비어있는 것을 하나 찾는다. 찾았을 때, 우선 비어있는 mmap_area에 자식을 할당하기 위해 부모의 mmap_area를 복제하되, ma[t].p = np; 만큼은 자식 프로세스(np)로 넣는다. 이후에는 부모와 동일하게 자식의 매핑을 진행한다. 여기서는 munmap과 비슷하게 walkpgdir()로 부모에 할당되어 있는 모든 페이지들을 확인하고, 만약 페이지가 존재할경우, kalloc();을 통해 새 물리 페이지를 할당하고 memset(mem, 0, PGSIZE);을 통해 초기화한 다음 memmove(mem, (void*)ptr, PGSIZE);로 부모의 것을 복제한다. 마지막으로는 mappages()를 진행한다. 일련의 과정이 끝나면 두번째 포문을 바로 break하고(부모것을 하나 완료했으므로) 다시 첫번째 포문으로 돌아가 다음 부모의 mmap_area을 찾아서 위의 과정을 반복한다.

11. 테스트 코드

임시적으로 README는 1과 2로 이루어진 파일로 변경하였다.

코드

int size = 4096;
char* text = (char*)mmap(0, size, PROT_READ, MAP_POPULATE, -1, 0);
printf(1, "mmap return is: %d\n", text);

결과

mmap return is: 0

It’s not anonymous, but when the fd is -1 을 잘 처리한다.

코드

int size = 4096;
int fd = open("README", O_RDONLY);
char* text = (char*)mmap(0, size, PROT_READ|PROT_WRITE, MAP_POPULATE, fd, 0);
printf(1, "mmap return is: %d\n", text);

결과

mmap return is: 0

The protection of the file and the prot of the parameter are different 을 잘 처리한다

코드

int size = 8192;
int fd = open("README", O_RDONLY);
char* text = (char*)mmap(0, size, PROT_READ, MAP_POPULATE, fd, 0);
printf(1, "mmap return is: %d\n", text);
printf(1, "text[0] is: %c\n", text[0]);

결과

mmap return is: 1073741824
text[0] is: 1

MAP_POPULATE 인 경우 파일을 잘 불러온다.

코드

int size = 8192;
int fd = open("README", O_RDONLY);
char* text = (char*)mmap(0, size, PROT_READ, MAP_ANONYMOUS|MAP_POPULATE, fd, 0);
printf(1, "mmap return is: %d\n", text);
printf(1, "text[0] is: %d\n", text[0]);

결과

mmap return is: 1073741824
text[0] is: 0

0으로 잘 초기화 된다.

코드

int size = 8192;
int fd = open("README", O_RDONLY);
printf(1,"freemem now is %d\n",freemem());
char* text = (char*)mmap(0, size, PROT_READ, 0, fd, 0);
printf(1, "mmap return is: %d\n", text);
printf(1,"freemem now is %d\n",freemem());
printf(1, "text[4100] is: %c\n", text[4100]);
printf(1,"freemem now is %d\n",freemem());
// for(int i=4096; i<size; i++) printf(1, "%c", *(text+i));
printf(1, "text[300] is: %c\n", text[300]);
printf(1,"freemem now is %d\n",freemem());
printf(1,"\n");

결과

freemem now is 56788
mmap return is: 1073741824
freemem now is 56788
text[4100] is: 2
freemem now is 56786
text[300] is: 1
freemem now is 56785

mmap 시에는 페이지가 늘어나지 않고, 4100을 접근하자 페이지 테이블용 1개, 파일 매핑용 1개 총 2개의 페이지가 늘어났다. 300 접근시 마지막 1개가 생기는 모습을 볼 수 있다.

코드

int size = 8192;
int fd = open("README", O_RDONLY);
char* text = (char*)mmap(0, size, PROT_READ, 0, fd, 0);
printf(1, "mmap return is: %d\n", text);
printf(1, "text[9000] is: %c\n", text[9000]);
printf(1,"\n");

결과

mmap return is: 1073741824
pid 3 mytest: trap 14 err 4 on cpu 0 eip 0x45 addr 0x40002328--kill proc

If faulted address has no corresponding mmap_area, return -1에 따라 프로세스가 잘 종료된다.

코드

int size = 8192;
int fd = open("README", O_RDONLY);
char* text = (char*)mmap(0, size, PROT_READ, 0, fd, 0);
printf(1, "mmap return is: %d\n", text);
text[5000] = '5';
printf(1,"\n");

결과

mmap return is: 1073741824
pid 3 mytest: trap 14 err 6 on cpu 0 eip 0x45 addr 0x40001388--kill proc

프로세스가 잘 종료된다.

코드

int size = 8192;
int fd = open("README", O_RDONLY);
printf(1,"freemem now is %d\n",freemem());
char* text = (char*)mmap(0, size, PROT_READ, MAP_POPULATE, fd, 0);
printf(1, "mmap return is: %d\n", text);
printf(1,"freemem now is %d\n",freemem());
int ret = munmap(0 + MMAPBASE);
printf(1, "munmap return is: %d\n", ret);
printf(1,"freemem now is %d\n",freemem());
printf(1,"\n");

결과

freemem now is 56788
mmap return is: 1073741824
freemem now is 56785
munmap return is: 1
freemem now is 56787

첫 할당시 테이블을 포함한 3개의 페이지가 줄어들며, 언맵이 성공적으로 진행된 후에는 테이블을 제외한 2개의 페이지가 다시 돌아오는 걸 볼 수 있다.

코드

int size = 8192;
int fd = open("README", O_RDONLY);
printf(1,"freemem now is %d\n",freemem());
char* text = (char*)mmap(size, size, PROT_READ, MAP_POPULATE, fd, 0);
printf(1, "mmap return is: %d\n", text);
printf(1,"freemem now is %d\n",freemem());
int ret = munmap(0 + MMAPBASE);
printf(1, "munmap return is: %d\n", ret);
printf(1,"freemem now is %d\n",freemem());
printf(1,"\n");

결과

freemem now is 56788
mmap return is: 1073750016
freemem now is 56785
munmap return is: -1
freemem now is 56785

없는 주소를 munmap하자 -1을 잘 리턴하고 freemem도 변하지 않았다.

코드

int size = 8192;
int fd = open("README", O_RDWR);
printf(1,"freemem now is %d\n",freemem());
char* text = (char*)mmap(0, size, PROT_READ|PROT_WRITE, MAP_POPULATE, fd, 0);
printf(1, "mmap return is: %d\n", text);
printf(1,"freemem now is %d\n",freemem());
printf(1, "parent text[110] is: %c\n", text[110]);
text[110] = '9';
printf(1, "parent text[110] is: %c\n", text[110]);

int fo;
if((fo=fork())==0){
  printf(1,"fork! child freemem now is %d\n",freemem());
    printf(1, "child text[110] is: %c\n", text[110]);
  text[110] = '7';
  printf(1, "child text[110] is: %c\n", text[110]);
}
else{
  wait();
  printf(1,"parent freemem now is %d\n",freemem());
  printf(1, "parent text[110] is: %c\n", text[110]);
}

결과

freemem now is 56788
mmap return is: 1073741824
freemem now is 56785
parent text[110] is: 1
parent text[110] is: 9
fork! child freemem now is 56712
child text[110] is: 9
child text[110] is: 7
parent freemem now is 56785
parent text[110] is: 9

fork시 부모의 mmap_area와 물리 메모리 매핑을 잘 복제하는 모습을 보여준다.

Source

Just an developer with drinks
2022 HyeonDong, Powered By Gatsby.