xv6, line by line
kernel/console.c

kernel/console.c

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

About this file

The console: the device that user programs read from and write to when they use file descriptors 0, 1 and 2. It sits between the file layer above and the UART driver in kernel/uart.c below.

Output is the simple direction. consolewrite copies the program’s bytes into the kernel and passes them to uartwrite; kernel messages go through consputc instead.

Input is where the work is. Typed characters arrive one at a time from the UART interrupt handler and go to consoleintr, which provides line editing (cooked input): it echoes each character to the screen, lets backspace and control-U erase what has not been submitted, and stores the line in a circular buffer, cons. Only when you press Enter (or control-D) does the line become visible to consoleread, which a read() system call reaches. Until then the reader sleeps. Control-P prints a process list, for debugging.

consoleinit connects the two functions to the file system’s device table, so that read and write on the file /console end up here.

Read before: kernel/uart.c. Read next: kernel/printk.c (kernel output) and kernel/file.c (how read and write reach a device).

1//
2// Console input and output, to the uart.
3// Reads are line at a time.
4// Implements special input characters:
5// newline -- end of line
6// control-h -- backspace
7// control-u -- kill line
8// control-d -- end of file
9// control-p -- print process list
10//
12#include <stdarg.h>
14#include "types.h"
15#include "param.h"
18#include "fs.h"
19#include "file.h"
21#include "riscv.h"
22#include "defs.h"
23#include "proc.h"
25#define BACKSPACE 0x100 // erase the last output character
26#define C(x) ((x) - '@') // Control-x
28//
29// send one character to the uart, but don't use
30// interrupts or sleep(). safe to be called from
31// interrupts, e.g. by printk and to echo input
32// characters.
33//
34void
37 if (c == BACKSPACE) {
38 // if the user typed backspace, overwrite with a space.
42 } else {
44 }
47struct {
48 struct spinlock lock;
50 // input circular buffer
51#define INPUT_BUF_SIZE 128
53 uint r; // Read index
54 uint w; // Write index
55 uint e; // Edit index
58//
59// user write() system calls to the console go here.
60// uses sleep() and UART interrupts.
61//
62int
65 char buf[32]; // move batches from user space to uart.
66 int i = 0;
68 while (i < n) {
69 int nn = sizeof(buf);
70 if (nn > n - i)
71 nn = n - i;
72 if (either_copyin(buf, user_src, src + i, nn) == -1)
73 break;
75 i += nn;
76 }
78 return i;
81//
82// user read()s from the console go here.
83// copy (up to) a whole input line to dst.
84// user_dst indicates whether dst is a user
85// or kernel address.
86//
87int
91 int c;
92 char cbuf;
96 while (n > 0) {
97 // wait until interrupt handler has put some
98 // input into cons.buffer.
99 while (cons.r == cons.w) {
100 if (killed(myproc())) {
102 return -1;
103 }
108 }
112 if (c == C('D')) { // end-of-file
113 if (n < target) {
114 // Save ^D for next time, to make sure
115 // caller gets a 0-byte result.
117 }
118 break;
119 }
121 // copy the input byte to the user-space buffer.
123 if (either_copyout(user_dst, dst, &cbuf, 1) == -1)
124 break;
126 dst++;
127 --n;
129 if (c == '\n') {
130 // a whole line has arrived, return to
131 // the user-level read().
132 break;
133 }
134 }
137 return target - n;
140//
141// the console input interrupt handler.
142// uartintr() calls this for each input character.
143// do erase/kill processing, append to cons.buf,
144// wake up consoleread() if a whole line has arrived.
145//
146void
151 switch (c) {
152 case C('P'): // Print process list.
154 break;
155 case C('U'): // Kill line.
156 while (cons.e != cons.w &&
157 cons.buf[(cons.e - 1) % INPUT_BUF_SIZE] != '\n') {
160 }
161 break;
162 case C('H'): // Backspace
163 case '\x7f': // Delete key
164 if (cons.e != cons.w) {
167 }
168 break;
169 default:
170 if (c != 0 && cons.e - cons.r < INPUT_BUF_SIZE) {
171 c = (c == '\r') ? '\n' : c;
173 // echo back to the user.
176 // store for consumption by consoleread().
179 if (c == '\n' || c == C('D') || cons.e - cons.r == INPUT_BUF_SIZE) {
180 // wake up consoleread() if a whole line (or end-of-file)
181 // has arrived.
184 }
185 }
186 break;
187 }
192void
195 initlock(&cons.lock, "cons");
199 // connect read and write system calls
200 // to consoleread and consolewrite.