xv6, line by line
kernel/pipe.c

kernel/pipe.c

C · 143 lines · annotated 100% · kernel · upstream

About this file

The kernel’s implementation of pipes: a small in-kernel buffer that one process writes bytes into and another reads them out of, in order. Pipes are how the shell connects commands: in ls | wc, ls writes into a pipe and wc reads from the other end (user/sh.c).

A pipe is a pipe structure holding a 512-byte circular buffer, two byte counters, two “is this end still open” flags, and a spinlock protecting all of them. Each end is an ordinary open file (struct file) of type FD_PIPE pointing at the structure, so the rest of the kernel treats pipes like any other file: fileread, filewrite and fileclose in kernel/file.c call piperead, pipewrite and pipeclose.

The interesting part is waiting. A reader with nothing to read, or a writer facing a full buffer, must sleep until the other side acts. The file shows the full sleep and wakeup pattern of this xv6 version (sleep_prepare, sleep, wakeup) with two channels, one for readers and one for writers.

Read before: kernel/file.c, and kernel/proc.c for sleep and wakeup. Read next: sys_pipe in kernel/sysfile.c, which creates pipes for user programs.

1#include "types.h"
2#include "riscv.h"
3#include "defs.h"
4#include "param.h"
5#include "spinlock.h"
6#include "proc.h"
7#include "fs.h"
9#include "file.h"
11#define PIPESIZE 512
13struct pipe {
14 struct spinlock lock;
16 uint nread; // number of bytes read
17 uint nwrite; // number of bytes written
18 int readopen; // read fd is still open
19 int writeopen; // write fd is still open
20};
22int
23pipealloc(struct file **f0, struct file **f1)
25 struct pipe *pi;
27 pi = 0;
28 *f0 = *f1 = 0;
29 if ((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0)
30 goto bad;
31 if ((pi = (struct pipe *)kalloc()) == 0)
32 goto bad;
33 pi->readopen = 1;
35 pi->nwrite = 0;
36 pi->nread = 0;
37 initlock(&pi->lock, "pipe");
38 (*f0)->type = FD_PIPE;
39 (*f0)->readable = 1;
40 (*f0)->writable = 0;
41 (*f0)->pipe = pi;
42 (*f1)->type = FD_PIPE;
43 (*f1)->readable = 0;
44 (*f1)->writable = 1;
45 (*f1)->pipe = pi;
46 return 0;
49 if (pi)
50 kfree((char *)pi);
51 if (*f0)
53 if (*f1)
55 return -1;
58void
59pipeclose(struct pipe *pi, int writable)
62 if (writable) {
65 } else {
66 pi->readopen = 0;
68 }
69 if (pi->readopen == 0 && pi->writeopen == 0) {
71 kfree((char *)pi);
72 } else
76int
77pipewrite(struct pipe *pi, uint64 addr, int n)
79 int i = 0;
80 struct proc *pr = myproc();
83 while (i < n) {
84 if (pi->readopen == 0 || killed(pr)) {
86 return -1;
87 }
88 if (pi->nwrite == pi->nread + PIPESIZE) { //DOC: pipewrite-full
94 } else {
95 char ch;
96 if (copyin(pr->pagetable, pr->sz, &ch, addr + i, 1) == -1) {
97 if (i == 0)
98 i = -1;
99 break;
100 }
102 i++;
103 }
104 }
108 return i;
111int
112piperead(struct pipe *pi, uint64 addr, int n)
114 int i;
115 struct proc *pr = myproc();
116 char ch;
119 while (pi->nread == pi->nwrite && pi->writeopen) { //DOC: pipe-empty
120 if (killed(pr)) {
122 return -1;
123 }
124 sleep_prepare(&pi->nread); //DOC: piperead-sleep
128 }
129 for (i = 0; i < n; i++) { //DOC: piperead-copy
130 if (pi->nread == pi->nwrite)
131 break;
133 if (copyout(pr->pagetable, pr->sz, addr + i, &ch, 1) == -1) {
134 if (i == 0)
135 i = -1;
136 break;
137 }
139 }
140 wakeup(&pi->nwrite); //DOC: piperead-wakeup
142 return i;