#include "core/types.h" #include "core/defs.h" #include "core/param.h" #include "arch/mmu.h" #include "core/proc.h" #include "fs/fs.h" #include "core/spinlock.h" #include "core/sleeplock.h" #include "fs/file.h" #include "core/kmem.h" #include "uapi/posix.h" #include "uapi/errno.h" #define PIPESIZE 512 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 }; // A pipe is about 560 bytes and used to come from kalloc(), which is a whole // 4096-byte page -- so every `|` in a shell command burned three and a half // kilobytes it never touched. This is what kmem_cache exists for, and the // cache is created on first use rather than at boot so a kernel that never // opens a pipe holds no page for one. static struct kmem_cache *pipe_cache; int pipealloc(struct file **f0, struct file **f1) { struct pipe *p; p = 0; *f0 = *f1 = 0; if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0) goto bad; if(!pipe_cache && !(pipe_cache = kmem_cache_create("pipe", sizeof(*p)))) goto bad; if((p = kmem_cache_alloc(pipe_cache)) == 0) goto bad; p->readopen = 1; p->writeopen = 1; p->nwrite = 0; p->nread = 0; initlock(&p->lock, "pipe"); (*f0)->type = FD_PIPE; (*f0)->readable = 1; (*f0)->writable = 0; (*f0)->pipe = p; (*f1)->type = FD_PIPE; (*f1)->readable = 0; (*f1)->writable = 1; (*f1)->pipe = p; return 0; //PAGEBREAK: 20 bad: if(p) kmem_cache_free(pipe_cache, p); if(*f0) fileclose(*f0); if(*f1) fileclose(*f1); return -1; } void pipeclose(struct pipe *p, int writable) { acquire(&p->lock); if(writable){ p->writeopen = 0; wakeup(&p->nread); } else { p->readopen = 0; wakeup(&p->nwrite); } if(p->readopen == 0 && p->writeopen == 0){ release(&p->lock); kmem_cache_free(pipe_cache, p); } else release(&p->lock); } //PAGEBREAK: 40 int pipewrite(struct pipe *p, char *addr, int n) { int i; acquire(&p->lock); // Writing to a pipe nobody is reading is SIGPIPE and EPIPE, in that order. // // This returned a bare -1 with no signal and no errno, so `yes | head -2` // came back with whatever errno happened to be lying around -- "Invalid // argument" -- instead of quietly ending the way every shell pipeline is // built to. Checked up front as well as in the blocking loop below: the // common case is that head has already exited by the time the next write // arrives, and the pipe is nowhere near full. if(p->readopen == 0){ release(&p->lock); sigsend(myproc()->pid, SIGPIPE); return -EPIPE; } for(i = 0; i < n; i++){ while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full if(p->readopen == 0){ release(&p->lock); sigsend(myproc()->pid, SIGPIPE); return i > 0 ? i : -EPIPE; // a short write is still a write } if(myproc()->killed){ release(&p->lock); return -1; } wakeup(&p->nread); sleep(&p->nwrite, &p->lock); //DOC: pipewrite-sleep } p->data[p->nwrite++ % PIPESIZE] = addr[i]; } wakeup(&p->nread); //DOC: pipewrite-wakeup1 release(&p->lock); return n; } int piperead(struct pipe *p, char *addr, int n) { int i; acquire(&p->lock); while(p->nread == p->nwrite && p->writeopen){ //DOC: pipe-empty if(myproc()->killed){ release(&p->lock); return -1; } sleep(&p->nread, &p->lock); //DOC: piperead-sleep } for(i = 0; i < n; i++){ //DOC: piperead-copy if(p->nread == p->nwrite) break; addr[i] = p->data[p->nread++ % PIPESIZE]; } wakeup(&p->nwrite); //DOC: piperead-wakeup release(&p->lock); return i; } // Readiness for poll(2)/select(2). Reports state, doesn't block. int pipe_poll(struct pipe *p, int readable, int writable) { int rev = 0; acquire(&p->lock); if(readable){ if(p->nread != p->nwrite) rev |= POLLIN; if(p->writeopen == 0) rev |= POLLHUP; } if(writable){ if(p->readopen == 0) rev |= POLLERR; else if(p->nwrite < p->nread + PIPESIZE) rev |= POLLOUT; } release(&p->lock); return rev; }