os-xv6分析-设备管理-console.c 代码分析

os-xv6分析-设备管理-console.c 代码分析
Console.c 描述了控制台的基本功能

//Console.c 描述了控制台的基本功能
// Console input and output.
// Input is from the keyboard or serial port.
// Output is written to the screen and serial port.
//控制台输入和输出。
//输入来自键盘或串行端口。
//输出被写入屏幕和串行端口。
// 导包
#include "types.h"   
#include "defs.h"
#include "param.h"
#include "traps.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "file.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"
// 声明显示输出函数
static void consputc(int);
// 静态变量 panicked
static int panicked = 0;
// 定义cons结构体,描述控制台
static struct {
  struct spinlock lock; 
  int locking;
} cons;
// 定义函数printint,打印base进制字符 base<= 16
static void
printint(int xx, int base, int sign)
{
  static char digits[] = "0123456789abcdef";// 定义十六进制字符
  char buf[16]; 
  int i;
  uint x;

  if(sign && (sign = xx < 0)) // 根据sign和xx值判断x为多少
    x = -xx;
  else
    x = xx;

  i = 0;
  do{
    buf[i++] = digits[x % base]; // x%base余数
  }while((x /= base) != 0); // 进制转换

  if(sign) // 判断是否是负数
    buf[i++] = '-';

  while(--i >= 0)  // 倒序调用显示函数,输出xx的base进制表示
    consputc(buf[i]);
}
//PAGEBREAK: 50

// Print to the console. only understands %d, %x, %p, %s.
// 打印到控制台。仅理解%d、%x、%p、%s。
// 定义打印函数,理解printf中的%d,%x,%p和%s,并输出对应值
void
cprintf(char *fmt, ...)
{
  int i, c, locking;
  uint *argp;
  char *s;

  locking = cons.locking; // 获取cons的锁标记
  if(locking) //判断是否被锁,若被锁申请锁
    acquire(&cons.lock);

  if (fmt == 0)
    panic("null fmt"); //无须打印

  argp = (uint*)(void*)(&fmt + 1); //定义函数
  for(i = 0; (c = fmt[i] & 0xff) != 0; i++){
    if(c != '%'){
      consputc(c); //调用显示函数,显示字符
      continue;
    }
    c = fmt[++i] & 0xff;//取下一个字符
    if(c == 0)
      break;
    switch(c){ //按照条件打印c
    case 'd': //数字
      printint(*argp++, 10, 1); //调用printint打印十进制数字
      break;
    case 'x': //十六进制
    case 'p': // 输出内存地址
      printint(*argp++, 16, 0);
      break;
    case 's': //输出字符穿
      if((s = (char*)*argp++) == 0)
        s = "(null)"; //s赋值为null
      for(; *s; s++) //循环调用输出s
        consputc(*s); 
      break;
    case '%':
      consputc('%'); //打印%
      break;
    default:
      // Print unknown % sequence to draw attention.
      consputc('%'); //打印%
      consputc(c); //打印c
      break;
    }
  }

  if(locking)// 若被锁
    release(&cons.lock); //释放锁
}

void
panic(char *s) //
{
  int i;
  uint pcs[10];

  cli(); //调用命令行界面函数
  cons.locking = 0; //控制台locking置0
  // use lapiccpunum so that we can call panic from mycpu()
//使用lapiccpunum,以便我们可以从mycpu()调用panic
  cprintf("lapicid %d: panic: ", lapicid());// 调用cprintf,输出lapicid //lapicid(): panic:
  cprintf(s); // 打印字符串s
  cprintf("\n"); //换行
  getcallerpcs(&s, pcs); // 获取调用信息
  for(i=0; i<10; i++) //输出前10个
    cprintf(" %p", pcs[i]);
  panicked = 1; // freeze other CPU //冻结其他CPU
  for(;;) // 死循环
    ;
}

//PAGEBREAK: 50
// 声明变量
#define BACKSPACE 0x100 //退格键
#define CRTPORT 0x3d4
static ushort *crt = (ushort*)P2V(0xb8000);  // CGA memory //CGA存储器
// 对应CGA设备使用的0xb8000地址物理区,通过P2V转换成虚地址
static void
cgaputc(int c)  //将显示字符送往CRT显示器
{
  int pos; //光标变量函数

  // Cursor position: col + 80*row.  //光标位置:列+80*行。
  outb(CRTPORT, 14);  //指出十四号寄存器要被操作
  pos = inb(CRTPORT+1) << 8; //读14号寄存器,并左移8位
  outb(CRTPORT, 15); //指出十五号寄存器要被操作
  pos |= inb(CRTPORT+1); //读15号寄存器,并添加到pos后八位

  if(c == '\n')  //若字符为换行符
    pos += 80 - pos%80;  //光标位置跳到下一行行首
  else if(c == BACKSPACE){ //若为退格键
    if(pos > 0) --pos; //如果光标不在首位,则位置回退1
  } else
    crt[pos++] = (c&0xff) | 0x0700;  // black on white //显示字符

  if(pos < 0 || pos > 25*80) //如果光标位置出错,则输出pos under/overflow
    panic("pos under/overflow");

  if((pos/80) >= 24){  // Scroll up. //换行显示
    memmove(crt, crt+80, sizeof(crt[0])*23*80);
    pos -= 80;
    memset(crt+pos, 0, sizeof(crt[0])*(24*80 - pos));
  }

  outb(CRTPORT, 14); //指出要操作的字节
  outb(CRTPORT+1, pos>>8);
  outb(CRTPORT, 15);
  outb(CRTPORT+1, pos);
  crt[pos] = ' ' | 0x0700; //显示空
}

void
consputc(int c) //显示输出函数
{
  if(panicked){ //是否出错,若出错,则停止运行,进入死循环
    cli();
    for(;;)
      ;
  }

  if(c == BACKSPACE){ //判断是否是回退键
    uartputc('\b'); uartputc(' '); uartputc('\b'); //是通过uarputc函数将空送往串口
  } else 
    uartputc(c); //否通过uarputc函数将字符送往串口显示
  cgaputc(c);  //CRT显示器显示
}

//定义输入缓冲区
#define INPUT_BUF 128
struct {
  char buf[INPUT_BUF];
  uint r;  // Read index  //读指针
  uint w;  // Write index  //写指针
  uint e;  // Edit index  //编辑指针
} input;

#define C(x)  ((x)-'@')  // Control-x //ctrl 键与其他按键的组合

void
consoleintr(int (*getc)(void))  //控制器中断 串口中断传入uartgec函数指针
// 键盘中断传入kbdgetc函数指针
{
  int c, doprocdump = 0;

  acquire(&cons.lock); //获得锁
  while((c = getc()) >= 0){  //读入输入字符
    switch(c){  //分情况判断
    case C('P'):  // Process listing. //ctrl+P 组合键
      // procdump() locks cons.lock indirectly; invoke later
      // procdump()间接锁定cons.lock;稍后调用
      doprocdump = 1; 
      break;
case C('U'):  // Kill line. //ctrl+U 组合键
  // 循环回退撤销指令执行
      while(input.e != input.w &&
            input.buf[(input.e-1) % INPUT_BUF] != '\n'){
        input.e--;
        consputc(BACKSPACE); //显示回退键
      }
      break;
case C('H'): case '\x7f':  // Backspace  //ctrl+H组合键
 //撤回操作
      if(input.e != input.w){
        input.e--;
        consputc(BACKSPACE); //显示回退键
      }
      break;
    default: //写缓存
      if(c != 0 && input.e-input.r < INPUT_BUF){
        c = (c == '\r') ? '\n' : c;
        input.buf[input.e++ % INPUT_BUF] = c; //字符存入循坏缓存区
        consputc(c);  //显示字符
        // 换行||C(‘D’)|| 编辑指针位置==输入指针+缓冲区大小
        if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){
          input.w = input.e; // 写指针赋值为编辑指针值
          wakeup(&input.r); //唤醒
        }
      }
      break;
    }
  }
  release(&cons.lock); //释放锁
  if(doprocdump) {
    procdump();  // now call procdump() wo. cons.lock held现在调用//procdump()wo。消费者锁定
  }
}

int
consoleread(struct inode *ip, char *dst, int n)
 //从输入缓冲区input.buf[]读入数据,输入缓冲区的唯一“消费者”,通过//cons.lock 自旋锁与此缓冲区的“生产者”(键盘中断,串口代码中断)实现//互斥
{
  uint target;
  int c;

  iunlock(ip);   //释放指定inode锁
  target = n;
  acquire(&cons.lock);  //获得锁
  while(n > 0){
    while(input.r == input.w){  //互斥判断
      if(myproc()->killed){
        release(&cons.lock);  //释放锁
        ilock(ip);
        return -1;
      }
      sleep(&input.r, &cons.lock);  //睡眠
    }
    c = input.buf[input.r++ % INPUT_BUF];  //读指定大小的数据
    if(c == C('D')){  // EOF  //结束
      if(n < target){
        // Save ^D for next time, to make sure
        // caller gets a 0-byte result. 
       //保存以备下次使用,以确保调用者得到0字节的结果。
        input.r--;
      }
      break;
    }
    *dst++ = c; //写入磁盘
    --n;
    if(c == '\n')  //换行
      break;
  }
  release(&cons.lock); //释放锁
  ilock(ip); //获取指定inode锁

  return target - n; //返回读入数据数量
}

int
consolewrite(struct inode *ip, char *buf, int n)
//向控制台设备(文件类型3)文件进行写操作,可以实现输出显示。此文件操//作实际指向consloewrite()函数,通过uartputc函数和cgaputc将输出缓//冲区内容逐个字节输出到CGA显卡和串口终端上。
{
  int i;

  iunlock(ip);  // 释放指定inode锁
  acquire(&cons.lock); //获得锁
  for(i = 0; i < n; i++) // 循坏在控制台显示
    consputc(buf[i] & 0xff); 
  release(&cons.lock); //释放锁
  ilock(ip); //获得指定inode锁

  return n;
}

void
consoleinit(void)
{
  initlock(&cons.lock, "console"); // 初始化自旋锁
  // 建立文件系统于控制台设备的联系
  devsw[CONSOLE].write = consolewrite; //写
  devsw[CONSOLE].read = consoleread; // 读
  cons.locking = 1; //锁置1

  ioapicenable(IRQ_KBD, 0); //使能键盘中断
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值