From c41f1de5d41a527a3fa2d1e94215766130eac456 Mon Sep 17 00:00:00 2001 From: rtm Date: Tue, 27 Jun 2006 14:35:53 +0000 Subject: [PATCH] file descriptors pipes --- Makefile | 18 +++++++--- Notes | 8 +++++ defs.h | 16 +++++++++ fd.c | 80 +++++++++++++++++++++++++++++++++++++++++ fd.h | 9 +++++ main.c | 8 ++--- param.h | 2 ++ pipe.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++ proc.c | 9 +++++ proc.h | 1 + syscall.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++- syscall.h | 3 ++ trap.c | 4 +-- ulib.c | 44 +++++++++++++++++++++++ user1.c | 38 ++++++-------------- usertests.c | 30 ++++++++++++++++ 16 files changed, 424 insertions(+), 39 deletions(-) create mode 100644 fd.c create mode 100644 fd.h create mode 100644 pipe.c create mode 100644 ulib.c create mode 100644 usertests.c diff --git a/Makefile b/Makefile index 9cac647..0b716b4 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ OBJS = main.o console.o string.o kalloc.o proc.o trapasm.o trap.o vectors.o \ - syscall.o ide.o picirq.o mp.o spinlock.o + syscall.o ide.o picirq.o mp.o spinlock.o fd.o pipe.o CC = i386-jos-elf-gcc LD = i386-jos-elf-ld @@ -20,22 +20,30 @@ bootblock : bootasm.S bootmain.c $(OBJCOPY) -S -O binary bootblock.o bootblock ./sign.pl bootblock -kernel : $(OBJS) bootother.S user1 +kernel : $(OBJS) bootother.S user1 usertests $(CC) -nostdinc -I. -c bootother.S $(LD) -N -e start -Ttext 0x7000 -o bootother.out bootother.o $(OBJCOPY) -S -O binary bootother.out bootother $(OBJDUMP) -S bootother.o > bootother.asm - $(LD) -Ttext 0x100000 -e main -o kernel $(OBJS) -b binary bootother user1 + $(LD) -Ttext 0x100000 -e main -o kernel $(OBJS) -b binary bootother user1 usertests $(OBJDUMP) -S kernel > kernel.asm vectors.S : vectors.pl perl vectors.pl > vectors.S -user1 : user1.c +user1 : user1.c ulib.o $(CC) -nostdinc -I. -c user1.c - $(LD) -N -e main -Ttext 0 -o user1 user1.o + $(LD) -N -e main -Ttext 0 -o user1 user1.o ulib.o $(OBJDUMP) -S user1 > user1.asm +usertests : usertests.c ulib.o + $(CC) -nostdinc -I. -c usertests.c + $(LD) -N -e main -Ttext 0 -o usertests usertests.o ulib.o + $(OBJDUMP) -S usertests > usertests.asm + +ulib.o : ulib.c + $(CC) -nostdinc -I. -c ulib.c + -include *.d clean : diff --git a/Notes b/Notes index 88b725c..350d929 100644 --- a/Notes +++ b/Notes @@ -83,3 +83,11 @@ it's IRQ 0, but it comes at a weird and changing vector (e.g. 119) if you don't initialize the PIC. why doesn't jos see this? if i initialize the PIC with IRQ_OFFSET 32, the interrupt arrives at vector 32. + +test out-of-fd cases for creating pipe. +test pipe circular buffer +test pipe writer or reader closes while other active or waiting +test exit vs fd reference counts +test write of more than PIPESIZE +test reader goes first vs writer goes first +test streaming of a lot of data diff --git a/defs.h b/defs.h index f10859e..d7cc09b 100644 --- a/defs.h +++ b/defs.h @@ -37,6 +37,7 @@ void pic_init(void); void mp_init(void); int cpu(void); int mp_isbcpu(void); +void lapic_init(int c); // spinlock.c extern uint32_t kernel_lock; @@ -46,3 +47,18 @@ void release_grant_spinlock(uint32_t* lock, int cpu); // main.c void load_icode(struct proc *p, uint8_t *binary, unsigned size); + +// pipe.c +struct pipe; +struct fd; +int pipe_alloc(struct fd **fd1, struct fd **fd2); +void pipe_close(struct pipe *p, int writeable); +int pipe_write(struct pipe *p, char *addr, int n); +int pipe_read(struct pipe *p, char *addr, int n); + +// fd.c +int fd_ualloc(); +struct fd * fd_alloc(); +void fd_close(struct fd *); +int fd_read(struct fd *fd, char *addr, int n); +int fd_write(struct fd *fd, char *addr, int n); diff --git a/fd.c b/fd.c new file mode 100644 index 0000000..0546cef --- /dev/null +++ b/fd.c @@ -0,0 +1,80 @@ +#include "types.h" +#include "param.h" +#include "x86.h" +#include "mmu.h" +#include "proc.h" +#include "defs.h" +#include "fd.h" + +struct fd fds[NFD]; + +/* + * allocate a file descriptor number for curproc. + */ +int +fd_ualloc() +{ + int fd; + struct proc *p = curproc[cpu()]; + for(fd = 0; fd < NOFILE; fd++) + if(p->fds[fd] == 0) + return fd; + return -1; +} + +struct fd * +fd_alloc() +{ + int i; + + for(i = 0; i < NFD; i++){ + if(fds[i].type == FD_CLOSED){ + fds[i].type = FD_NONE; + fds[i].count = 1; + return fds + i; + } + } + return 0; +} + +void +fd_close(struct fd *fd) +{ + if(fd->type == FD_CLOSED || fd->count <= 0) + panic("fd_close"); + fd->count -= 1; + if(fd->count == 0){ + if(fd->type == FD_PIPE) + pipe_close(fd->pipe, fd->writeable); + fd->type = FD_CLOSED; + } +} + +/* + * addr is a kernel address, pointing into some process's p->mem. + */ +int +fd_write(struct fd *fd, char *addr, int n) +{ + if(fd->writeable == 0) + return -1; + if(fd->type == FD_PIPE){ + return pipe_write(fd->pipe, addr, n); + } else { + panic("fd_write"); + return -1; + } +} + +int +fd_read(struct fd *fd, char *addr, int n) +{ + if(fd->readable == 0) + return -1; + if(fd->type == FD_PIPE){ + return pipe_read(fd->pipe, addr, n); + } else { + panic("fd_read"); + return -1; + } +} diff --git a/fd.h b/fd.h new file mode 100644 index 0000000..c8d0c34 --- /dev/null +++ b/fd.h @@ -0,0 +1,9 @@ +struct fd { + enum { FD_CLOSED, FD_NONE, FD_PIPE } type; + int count; // reference count + char readable; + char writeable; + struct pipe *pipe; +}; + +extern struct fd fds[NFD]; diff --git a/main.c b/main.c index 1226a8f..92b8f24 100644 --- a/main.c +++ b/main.c @@ -11,8 +11,8 @@ extern char edata[], end[]; extern int acpu; -extern char _binary_user1_start[]; -extern char _binary_user1_size[]; +extern char _binary_user1_start[], _binary_user1_size[]; +extern char _binary_usertests_start[], _binary_usertests_size[]; char buf[512]; @@ -25,7 +25,7 @@ main() cprintf("an application processor\n"); release_spinlock(&kernel_lock); acquire_spinlock(&kernel_lock); - idtinit(); + idtinit(); // CPU's idt lapic_init(cpu()); curproc[cpu()] = &proc[0]; // XXX swtch(); @@ -70,7 +70,7 @@ main() #if 1 p = newproc(); - load_icode(p, _binary_user1_start, (unsigned) _binary_user1_size); + load_icode(p, _binary_usertests_start, (unsigned) _binary_usertests_size); #endif #if 0 diff --git a/param.h b/param.h index 0a931d9..dac7e10 100644 --- a/param.h +++ b/param.h @@ -2,3 +2,5 @@ #define PAGE 4096 #define KSTACKSIZE PAGE #define NCPU 8 +#define NOFILE 16 // file descriptors per process +#define NFD 100 // file descriptors per system diff --git a/pipe.c b/pipe.c new file mode 100644 index 0000000..9a81e0b --- /dev/null +++ b/pipe.c @@ -0,0 +1,100 @@ +#include "types.h" +#include "param.h" +#include "x86.h" +#include "mmu.h" +#include "proc.h" +#include "defs.h" +#include "fd.h" + +#define PIPESIZE 512 + +struct pipe { + int readopen; // read fd is still open + int writeopen; // write fd is still open + int writep; // next index to write + int readp; // next index to read + char data[PIPESIZE]; +}; + +int +pipe_alloc(struct fd **fd1, struct fd **fd2) +{ + *fd1 = *fd2 = 0; + struct pipe *p = 0; + + if((*fd1 = fd_alloc()) == 0) + goto oops; + if((*fd2 = fd_alloc()) == 0) + goto oops; + if((p = (struct pipe *) kalloc(PAGE)) == 0) + goto oops; + (*fd1)->type = FD_PIPE; + (*fd1)->readable = 1; + (*fd1)->writeable = 0; + (*fd1)->pipe = p; + (*fd2)->type = FD_PIPE; + (*fd2)->readable = 0; + (*fd2)->writeable = 1; + (*fd2)->pipe = p; + return 0; + oops: + if(p) + kfree((char *) p, PAGE); + if(*fd1){ + (*fd1)->type = FD_NONE; + fd_close(*fd1); + } + if(*fd2){ + (*fd2)->type = FD_NONE; + fd_close(*fd2); + } + return -1; +} + +void +pipe_close(struct pipe *p, int writeable) +{ + if(writeable) + p->writeopen = 0; + else + p->readopen = 0; + if(p->readopen == 0 && p->writeopen == 0) + kfree((char *) p, PAGE); +} + +int +pipe_write(struct pipe *p, char *addr, int n) +{ + int i; + + for(i = 0; i < n; i++){ + while(((p->writep + 1) % PIPESIZE) == p->readp){ + if(p->readopen == 0) + return -1; + sleep(&p->writep); + } + p->data[p->writep] = addr[i]; + p->writep = (p->writep + 1) % PIPESIZE; + } + return i; +} + +int +pipe_read(struct pipe *p, char *addr, int n) +{ + int i; + + while(p->readp == p->writep){ + if(p->writeopen == 0) + return 0; + sleep(&p->readp); + } + + for(i = 0; i < n; i++){ + if(p->readp == p->writep) + break; + addr[i] = p->data[p->readp]; + p->readp = (p->readp + 1) % PIPESIZE; + } + return i; +} diff --git a/proc.c b/proc.c index e8a911d..97e84e3 100644 --- a/proc.c +++ b/proc.c @@ -2,6 +2,7 @@ #include "mmu.h" #include "x86.h" #include "param.h" +#include "fd.h" #include "proc.h" #include "defs.h" @@ -49,6 +50,7 @@ newproc() struct proc *np; struct proc *op = curproc[cpu()]; unsigned *sp; + int fd; for(np = &proc[1]; np < &proc[NPROC]; np++) if(np->state == UNUSED) @@ -80,6 +82,13 @@ newproc() np->esp = (unsigned) sp; np->ebp = (unsigned) sp; + // copy file descriptors + for(fd = 0; fd < NOFILE; fd++){ + np->fds[fd] = op->fds[fd]; + if(np->fds[fd]) + np->fds[fd]->count += 1; + } + np->state = RUNNABLE; cprintf("newproc %x\n", np); diff --git a/proc.h b/proc.h index 36e608e..14164db 100644 --- a/proc.h +++ b/proc.h @@ -24,6 +24,7 @@ struct proc{ int pid; int ppid; void *chan; // sleep + struct fd *fds[NOFILE]; struct Taskstate ts; // only to give cpu address of kernel stack struct Segdesc gdt[NSEGS]; diff --git a/syscall.c b/syscall.c index 1d2eb54..c27c226 100644 --- a/syscall.c +++ b/syscall.c @@ -42,6 +42,88 @@ fetcharg(int argno, int *ip) return fetchint(curproc[cpu()], esp + 8 + 4*argno, ip); } +int +putint(struct proc *p, unsigned addr, int ip) +{ + if(addr > p->sz - 4) + return 0; + memcpy(p->mem + addr, &ip, 4); + return 1; +} + +int +sys_pipe() +{ + struct fd *rfd = 0, *wfd = 0; + int f1 = -1, f2 = -1; + struct proc *p = curproc[cpu()]; + unsigned fdp; + + if(pipe_alloc(&rfd, &wfd) < 0) + goto oops; + if((f1 = fd_ualloc()) < 0) + goto oops; + p->fds[f1] = rfd; + if((f2 = fd_ualloc()) < 0) + goto oops; + p->fds[f2] = wfd; + if(fetcharg(0, &fdp) < 0) + goto oops; + if(putint(p, fdp, f1) < 0) + goto oops; + if(putint(p, fdp+4, f2) < 0) + goto oops; + return 0; + + oops: + cprintf("sys_pipe failed\n"); + if(rfd) + fd_close(rfd); + if(wfd) + fd_close(wfd); + if(f1 >= 0) + p->fds[f1] = 0; + if(f2 >= 0) + p->fds[f2] = 0; + return -1; +} + +int +sys_write() +{ + int fd, n; + unsigned addr; + struct proc *p = curproc[cpu()]; + + if(fetcharg(0, &fd) < 0 || fetcharg(1, &addr) < 0 || fetcharg(2, &n) < 0) + return -1; + if(fd < 0 || fd >= NOFILE) + return -1; + if(p->fds[fd] == 0) + return -1; + if(addr + n > p->sz) + return -1; + return fd_write(p->fds[fd], p->mem + addr, n); +} + +int +sys_read() +{ + int fd, n; + unsigned addr; + struct proc *p = curproc[cpu()]; + + if(fetcharg(0, &fd) < 0 || fetcharg(1, &addr) < 0 || fetcharg(2, &n) < 0) + return -1; + if(fd < 0 || fd >= NOFILE) + return -1; + if(p->fds[fd] == 0) + return -1; + if(addr + n > p->sz) + return -1; + return fd_read(p->fds[fd], p->mem + addr, n); +} + int sys_fork() { @@ -122,7 +204,7 @@ syscall() int num = cp->tf->tf_regs.reg_eax; int ret = -1; - cprintf("%x sys %d\n", cp, num); + //cprintf("%x sys %d\n", cp, num); switch(num){ case SYS_fork: ret = sys_fork(); @@ -136,6 +218,15 @@ syscall() case SYS_cons_putc: ret = sys_cons_putc(); break; + case SYS_pipe: + ret = sys_pipe(); + break; + case SYS_write: + ret = sys_write(); + break; + case SYS_read: + ret = sys_read(); + break; default: cprintf("unknown sys call %d\n", num); // XXX fault diff --git a/syscall.h b/syscall.h index f054414..65d5ced 100644 --- a/syscall.h +++ b/syscall.h @@ -2,3 +2,6 @@ #define SYS_exit 2 #define SYS_wait 3 #define SYS_cons_putc 4 +#define SYS_pipe 5 +#define SYS_write 6 +#define SYS_read 7 diff --git a/trap.c b/trap.c index 0cd4958..cfa8a57 100644 --- a/trap.c +++ b/trap.c @@ -37,14 +37,14 @@ trap(struct Trapframe *tf) acquire_spinlock(&kernel_lock); // released in trapret in trapasm.S - cprintf("trap %d eip %x:%x\n", tf->tf_trapno, tf->tf_cs, tf->tf_eip); - if(v == T_SYSCALL){ curproc[cpu()]->tf = tf; syscall(); return; } + cprintf("trap %d eip %x:%x\n", tf->tf_trapno, tf->tf_cs, tf->tf_eip); + if(v == 32){ // probably clock return; diff --git a/ulib.c b/ulib.c new file mode 100644 index 0000000..694501e --- /dev/null +++ b/ulib.c @@ -0,0 +1,44 @@ +int +fork() +{ + asm("mov $1, %eax"); + asm("int $48"); +} + +void +cons_putc(int c) +{ + asm("mov $4, %eax"); + asm("int $48"); +} + +int +puts(char *s) +{ + int i; + + for(i = 0; s[i]; i++) + cons_putc(s[i]); + return i; +} + +int +pipe(int fds[]) +{ + asm("mov $5, %eax"); + asm("int $48"); +} + +int +read(int fd, char *buf, int n) +{ + asm("mov $7, %eax"); + asm("int $48"); +} + +int +write(int fd, char *buf, int n) +{ + asm("mov $6, %eax"); + asm("int $48"); +} diff --git a/user1.c b/user1.c index 9e0c731..d414699 100644 --- a/user1.c +++ b/user1.c @@ -1,35 +1,19 @@ -int -fork() -{ - asm("mov $1, %eax"); - asm("int $48"); -} - -void -cons_putc(int c) -{ - asm("mov $4, %eax"); - asm("int $48"); -} - -int -puts(char *s) -{ - int i; - - for(i = 0; s[i]; i++) - cons_putc(s[i]); - return i; -} +char buf[32]; main() { - int pid; + int pid, fds[2], n; + + pipe(fds); pid = fork(); - if(pid == 0){ - cons_putc('C'); + if(pid > 0){ + write(fds[1], "xyz", 4); + puts("w"); } else { - cons_putc('P'); + n = read(fds[0], buf, sizeof(buf)); + puts("r: "); + puts(buf); + puts("\n"); } while(1) ; diff --git a/usertests.c b/usertests.c new file mode 100644 index 0000000..62eefda --- /dev/null +++ b/usertests.c @@ -0,0 +1,30 @@ +// simple fork and pipe read/write + +char buf[32]; + +void +pipe1() +{ + int fds[2], pid; + + pipe(fds); + pid = pipe(); + if(pid == 0){ + write(fds[1], "xyz", 4); + } else { + read(fds[0], buf, sizeof(buf)); + if(buf[0] != 'x' || buf[1] != 'y'){ + puts("pipe1 oops\n"); + return; + } + } + puts("pipe1 ok\n"); +} + +main() +{ + pipe1(); + + while(1) + ; +}