본문 바로가기

xv6:2024/Chapter 1

Exercise 1-3. primes

Write a concurrent prime sieve program for xv6 using pipes and the design illustrated in the picture halfway down this page and the surrounding text. This idea is due to Doug McIlroy, inventor of Unix pipes. Your solution should be in the file user/primes.c.

Your goal is to use pipe and fork to set up the pipeline. The first process feeds the numbers 2 through 280 into the pipeline. For each prime number, you will arrange to create one process that reads from its left neighbor over a pipe and writes to its right neighbor over another pipe. Since xv6 has limited number of file descriptors and processes, the first process can stop at 280.

 

 

 

1. kernel/pipe.c에서 pipe buffer의 길이를 512 -> 0x800(2048)로 수정했다.

2~280까지의 int를 pipe에 적으려면 총 4*279 bytes(==1116 bytes)가 필요하다. 이렇게 편법을 쓰는 건 좋지 않지만, 왜 메모리가 넉넉한 2024년도에 pipe buffer의 크기가 512인 채로 해당 과제를 수행해야 하는지 납득이 가지 않았다.

 

따라서 아래의 kernel/pipe.c 코드에서 #define PIPESIZE의 크기를 2048 bytes로 수정하였다.

// kernel/pipe.c

#include "types.h"
#include "riscv.h"
#include "defs.h"
#include "param.h"
#include "spinlock.h"
#include "proc.h"
#include "fs.h"
#include "sleeplock.h"
#include "file.h"

#define PIPESIZE 0x800

struct pipe {
  struct spinlock lock;
  char data[PIPESIZE];
  uint nread;     // number of bytes read
  uint nwrite;    // number of bytes written
  int readopen;   // read fd is still open
  int writeopen;  // write fd is still open
};

int
pipealloc(struct file **f0, struct file **f1)
{
  struct pipe *pi;

  pi = 0;
  *f0 = *f1 = 0;
  if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0)
    goto bad;
  if((pi = (struct pipe*)kalloc()) == 0)
    goto bad;
  pi->readopen = 1;
  pi->writeopen = 1;
  pi->nwrite = 0;
  pi->nread = 0;
  initlock(&pi->lock, "pipe");
  (*f0)->type = FD_PIPE;
  (*f0)->readable = 1;
  (*f0)->writable = 0;
  (*f0)->pipe = pi;
  (*f1)->type = FD_PIPE;
  (*f1)->readable = 0;
  (*f1)->writable = 1;
  (*f1)->pipe = pi;
  return 0;

 bad:
  if(pi)
    kfree((char*)pi);
  if(*f0)
    fileclose(*f0);
  if(*f1)
    fileclose(*f1);
  return -1;
}

void
pipeclose(struct pipe *pi, int writable)
{
  acquire(&pi->lock);
  if(writable){
    pi->writeopen = 0;
    wakeup(&pi->nread);
  } else {
    pi->readopen = 0;
    wakeup(&pi->nwrite);
  }
  if(pi->readopen == 0 && pi->writeopen == 0){
    release(&pi->lock);
    kfree((char*)pi);
  } else
    release(&pi->lock);
}

int
pipewrite(struct pipe *pi, uint64 addr, int n)
{
  int i;
  char ch;
  struct proc *pr = myproc();

  acquire(&pi->lock);
  for(i = 0; i < n; i++){
    while(pi->nwrite == pi->nread + PIPESIZE){  //DOC: pipewrite-full
      if(pi->readopen == 0 || pr->killed){
        release(&pi->lock);
        return -1;
      }
      wakeup(&pi->nread);
      sleep(&pi->nwrite, &pi->lock);
    }
    if(copyin(pr->pagetable, &ch, addr + i, 1) == -1)
      break;
    pi->data[pi->nwrite++ % PIPESIZE] = ch;
  }
  wakeup(&pi->nread);
  release(&pi->lock);
  return i;
}

int
piperead(struct pipe *pi, uint64 addr, int n)
{
  int i;
  struct proc *pr = myproc();
  char ch;

  acquire(&pi->lock);
  while(pi->nread == pi->nwrite && pi->writeopen){  //DOC: pipe-empty
    if(pr->killed){
      release(&pi->lock);
      return -1;
    }
    sleep(&pi->nread, &pi->lock); //DOC: piperead-sleep
  }
  for(i = 0; i < n; i++){  //DOC: piperead-copy
    if(pi->nread == pi->nwrite)
      break;
    ch = pi->data[pi->nread++ % PIPESIZE];
    if(copyout(pr->pagetable, addr + i, &ch, 1) == -1)
      break;
  }
  wakeup(&pi->nwrite);  //DOC: piperead-wakeup
  release(&pi->lock);
  return i;
}

 

 

2. primes.c

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

__attribute__((noreturn)) void send_sieved(int* p, int limit){
    int pp[2];
    int prime;
    int n;

    if(!read(p[0], &prime, sizeof(int))){
        exit(0);
    }

    printf("prime %d\n", prime);
    if(prime > limit/2){
        while(read(p[0], &prime, sizeof(int))){
            printf("prime %d\n", prime);
        }
        exit(0);
    }

    pipe(pp);


    while(read(p[0], &n, sizeof(int))){
        if(n % prime != 0){
            write(pp[1], &n, sizeof(int));
        }
    }

    close(p[0]);
    close(pp[1]);

    if(!fork()){ // child
        send_sieved(pp, limit);
    }
    else{ // parent
        wait(0);
        exit(0);
    }
}

void prime(int a, int b){
    int p[2];

    pipe(p);
    for(uint i = a; i <= b; i++){
        write(p[1], &i, sizeof(i));
    }

    close(p[1]);

    if(!fork()){ // child
        send_sieved(p, b);
    }
    else{ // parent
        wait(0);
        exit(0);
    }
}

int main(){
    prime(2, 280);

    exit(0);
}

 

 

'xv6:2024 > Chapter 1' 카테고리의 다른 글

Exercise 1-5. xargs  (1) 2024.11.19
Exercise 1-4. find  (0) 2024.11.18
Exercise 1-2. pingpong  (0) 2024.11.16
Exercise 1-1. sleep  (0) 2024.11.16
xv6's every system call && read() from the pipe  (1) 2024.11.16