for(;;){ // this call to wait() returns if the shell exits, // or if a parentless process exits. wpid = wait((int *) 0); if(wpid == pid){ // the shell exited; restart it. break; } elseif(wpid < 0){ printf("init: wait returned an error\n"); exit(1); } else { // it was a parentless process; do nothing. } } } }
// kernel/console.c // user read()s from the console go here. // copy (up to) a whole input line to dst. // user_dist indicates whether dst is a user // or kernel address. // int consoleread(int user_dst, uint64 dst, int n) { uint target; int c; char cbuf;
target = n; acquire(&cons.lock); while(n > 0){ // wait until interrupt handler has put some // input into cons.buffer. while(cons.r == cons.w){ if(killed(myproc())){ release(&cons.lock); return-1; } sleep(&cons.r, &cons.lock); }
c = cons.buf[cons.r++ % INPUT_BUF_SIZE];
if(c == C('D')){ // end-of-file if(n < target){ // Save ^D for next time, to make sure // caller gets a 0-byte result. cons.r--; } break; }
// copy the input byte to the user-space buffer. cbuf = c; if(either_copyout(user_dst, dst, &cbuf, 1) == -1) break;
dst++; --n;
if(c == '\n'){ // a whole line has arrived, return to // the user-level read(). break; } } release(&cons.lock);
// kernel/uart.c // add a character to the output buffer and tell the // UART to start sending if it isn't already. // blocks if the output buffer is full. // because it may block, it can't be called // from interrupts; it's only suitable for use // by write(). void uartputc(int c) { acquire(&uart_tx_lock);
if(panicked){ for(;;) ; } while(uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE){ // buffer is full. // wait for uartstart() to open up space in the buffer. sleep(&uart_tx_r, &uart_tx_lock); } uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c; uart_tx_w += 1; uartstart(); release(&uart_tx_lock); }
// kernel/uart.c // if the UART is idle, and a character is waiting // in the transmit buffer, send it. // caller must hold uart_tx_lock. // called from both the top- and bottom-half. void uartstart() { while(1){ if(uart_tx_w == uart_tx_r){ // transmit buffer is empty. return; }
if((ReadReg(LSR) & LSR_TX_IDLE) == 0){ // the UART transmit holding register is full, // so we cannot give it another byte. // it will interrupt when it's ready for a new byte. return; }
int c = uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE]; uart_tx_r += 1;
// maybe uartputc() is waiting for space in the buffer. wakeup(&uart_tx_r);
WriteReg(THR, c); } }
字符的输入
当用户在键盘上输入字符时,UART 会产生一个中断,PLIC 接收到中断后会路由到特定的 CPU 核,如果该核心设置了 SIE 的 E 位(外部中断位),硬件执行以下操作:
// kernel/trap.c // check if it's an external interrupt or software interrupt, // and handle it. // returns 2 if timer interrupt, // 1 if other device, // 0 if not recognized. int devintr() { uint64 scause = r_scause();
if((scause & 0x8000000000000000L) && (scause & 0xff) == 9){ // this is a supervisor external interrupt, via PLIC.
// irq indicates which device interrupted. int irq = plic_claim();
// the PLIC allows each device to raise at most one // interrupt at a time; tell the PLIC the device is // now allowed to interrupt again. if(irq) plic_complete(irq);
return1; } elseif(scause == 0x8000000000000001L){ // software interrupt from a machine-mode timer interrupt, // forwarded by timervec in kernelvec.S.
if(cpuid() == 0){ clockintr(); }
// acknowledge the software interrupt by clearing // the SSIP bit in sip. w_sip(r_sip() & ~2);
// kernel/uart.c // handle a uart interrupt, raised because input has // arrived, or the uart is ready for more output, or // both. called from devintr(). void uartintr(void) { // read and process incoming characters. while(1){ int c = uartgetc(); if(c == -1) break; consoleintr(c); }
// kernel/console.c // the console input interrupt handler. // uartintr() calls this for input character. // do erase/kill processing, append to cons.buf, // wake up consoleread() if a whole line has arrived. // void consoleintr(int c) { acquire(&cons.lock);
// store for consumption by consoleread(). cons.buf[cons.e++ % INPUT_BUF_SIZE] = c;
if(c == '\n' || c == C('D') || cons.e-cons.r == INPUT_BUF_SIZE){ // wake up consoleread() if a whole line (or end-of-file) // has arrived. cons.w = cons.e; wakeup(&cons.r); } } break; }
// kernel/console.c // send one character to the uart. // called by printf(), and to echo input characters, // but not from write(). // void consputc(int c) { if(c == BACKSPACE){ // if the user typed backspace, overwrite with a space. uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b'); } else { uartputc_sync(c); } }
// kernel/uart.c // alternate version of uartputc() that doesn't // use interrupts, for use by kernel printf() and // to echo characters. it spins waiting for the uart's // output register to be empty. void uartputc_sync(int c) { push_off();
if(panicked){ for(;;) ; }
// wait for Transmit Holding Empty to be set in LSR. while((ReadReg(LSR) & LSR_TX_IDLE) == 0) ; WriteReg(THR, c);