2022-2-24 MIT 6.268 lab:Booting PC Part 3: The Kernel —— exercise7-8

Using virtual memory to work around position dependence
操作系统会被映射到高地址处,较低的虚拟地址会交给用户程序使用。
实验中先映射 4MB 的物理内存,这足以保证启动。使用 kern/entrypgdir.c 中的页目录来完成这一操作。
使用 kern/entry.S 来设立 CR0_PG 标志位,这个标志位一旦设立,便会从物理地址转换到线性地址(物理地址)。

先查看 boot.out (计算机启动的可执行文件)发现其虚拟地址和物理地址是一致的。
在这里插入图片描述
但是查看 kernel 发现其物理地址和虚拟地址相差比较大。
在这里插入图片描述
Exercise 7. Use QEMU and GDB to trace into the JOS kernel and stop at the movl %eax, %cr0. Examine memory at 0x00100000 and at 0xf0100000. Now, single step over that instruction using the stepi GDB command. Again, examine memory at 0x00100000 and at 0xf0100000. Make sure you understand what just happened.

What is the first instruction after the new mapping is established that would fail to work properly if the mapping weren’t in place? Comment out the movl %eax, %cr0 in kern/entry.S, trace into it, and see if you were right.

为什么我第一次查看是这个结果
在这里插入图片描述
直接在 0x10000c (这个地址依然是物理地址)处打断点,这个地址就是 kernel 的入口地址,之后再借助 si 一步一步的走下去。
在这里插入图片描述
查看 0x100000 处的内容
在这里插入图片描述
查看 0xf010_0000 处的内容。分别是执行前和执行后的不同结果。
发现和 0x100000 处的一致。
在这里插入图片描述
为什么会是这种结果?
因为这句实现了虚拟地址的启用。

movl %eax, %cr0

使用 info registers 来查看寄存器里面的值,可知,通过 %eax 寄存器将 0x80010011 里的值传入给 %cr0 。由 cr0 所对应的标志位可知 ,这个数字 设置了 PG 标志位,开启了分页的机制,故可以通过查看到地址为 0xf010_0000 的内存当中的内容。
在这里插入图片描述

补充1:
gdb 的 x 代码:用 gdb 来查看 < addr > 处的 n 个存储单元,以 f 格式。
用gdb查看内存

格式: x /nfu

说明
x 是 examine 的缩写

n表示要显示的内存单元的个数

f表示显示方式, 可取如下值
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。

u表示一个地址单元的长度
b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节

————————————————
版权声明:本文为CSDN博主「公众号:程序芯世界」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/baidu_24256693/article/details/47298513
补充2 :
CR0 寄存器的标志位
在这里插入图片描述
Formatted Printing to the Console

printf.c

// Simple implementation of cprintf console output for the kernel,
// based on printfmt() and the kernel console's cputchar().

#include <inc/types.h>
#include <inc/stdio.h>
#include <inc/stdarg.h>


static void
putch(int ch, int *cnt)
{
        cputchar(ch);
        *cnt++;
}

int
vcprintf(const char *fmt, va_list ap)
{
        int cnt = 0;

        vprintfmt((void*)putch, &cnt, fmt, ap);
        return cnt;
}

int
cprintf(const char *fmt, ...)
{
        va_list ap;
        int cnt;

        va_start(ap, fmt);
        cnt = vcprintf(fmt, ap);
        va_end(ap);
        return cnt;
}
       

printfmt.c

// Stripped-down primitive printf-style formatting routines,
// used in common by printf, sprintf, fprintf, etc.
// This code is also used by both the kernel and user programs.

#include <inc/types.h>
#include <inc/stdio.h>
#include <inc/string.h>
#include <inc/stdarg.h>
#include <inc/error.h>

/*
 * Space or zero padding and a field width are supported for the numeric
 * formats only.
 *
 * The special format %e takes an integer error code
 * and prints a string describing the error.
 * The integer may be positive or negative,
 * so that -E_NO_MEM and E_NO_MEM are equivalent.
 */
 
 //根据这个可以得知,具体的错误描述放置在 error_string 的数组里面,其中错误号 errno  用来查找具体的 error_string 是什么。

static const char * const error_string[MAXERROR] =
{
        [E_UNSPECIFIED] = "unspecified error",
        [E_BAD_ENV]     = "bad environment",
        [E_INVAL]       = "invalid parameter",
        [E_NO_MEM]      = "out of memory",
        [E_NO_FREE_ENV] = "out of environments",
        [E_FAULT]       = "segmentation fault",
};

/*
 * Print a number (base <= 16) in reverse order,
 * using specified putch function and associated pointer putdat.
 */
static void
printnum(void (*putch)(int, void*), void *putdat,
         unsigned long long num, unsigned base, int width, int padc)
{
        // first recursively print all preceding (more significant) digits
        if (num >= base) {
                printnum(putch, putdat, num / base, base, width - 1, padc);
        } else {
                // print any needed pad characters before first digit
                while (--width > 0)
                        putch(padc, putdat);
        }

        // then print this (the least significant) digit
        putch("0123456789abcdef"[num % base], putdat);
}

// Get an unsigned int of various possible sizes from a varargs list,
// depending on the lflag parameter.
static unsigned long long
getuint(va_list *ap, int lflag)
{
        if (lflag >= 2)
                return va_arg(*ap, unsigned long long);
        else if (lflag)
                return va_arg(*ap, unsigned long);
        else
                return va_arg(*ap, unsigned int);
}

// Same as getuint but signed - can't use getuint
// because of sign extension
static long long
getint(va_list *ap, int lflag)
{
        if (lflag >= 2)
                return va_arg(*ap, long long);
        else if (lflag)
                return va_arg(*ap, long);
        else
                return va_arg(*ap, int);
}


// Main function to format and print a string.
void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...);

void
vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
{
        register const char *p;
        register int ch, err;
        unsigned long long num;
        int base, lflag, width, precision, altflag;
        char padc;

        while (1) {
                while ((ch = *(unsigned char *) fmt++) != '%') {
                        if (ch == '\0')
                                return;
                        putch(ch, putdat);
                }

                // Process a %-escape sequence
                padc = ' ';
                width = -1;
                precision = -1;
                lflag = 0;
                altflag = 0;
        reswitch:
                switch (ch = *(unsigned char *) fmt++) {

                // flag to pad on the right
                case '-':
                        padc = '-';
                        goto reswitch;

                // flag to pad with 0's instead of spaces
                case '0':
                        padc = '0';
                        goto reswitch;

                // width field
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                        for (precision = 0; ; ++fmt) {
                                           precision = precision * 10 + ch - '0';
                                ch = *fmt;
                                if (ch < '0' || ch > '9')
                                        break;
                        }
                        goto process_precision;

                case '*':
                        precision = va_arg(ap, int);
                        goto process_precision;

                case '.':
                        if (width < 0)
                                width = 0;
                        goto reswitch;

                case '#':
                        altflag = 1;
                        goto reswitch;

                process_precision:
                        if (width < 0)
                                width = precision, precision = -1;
                        goto reswitch;

                // long flag (doubled for long long)
                case 'l':
                        lflag++;
                        goto reswitch;

                // character
      case 'c':
                        putch(va_arg(ap, int), putdat);
                        break;

                // error message
                case 'e':
                        err = va_arg(ap, int);
                        if (err < 0)
                                err = -err;
                        if (err >= MAXERROR || (p = error_string[err]) == NULL)
                                printfmt(putch, putdat, "error %d", err);
                        else
                                printfmt(putch, putdat, "%s", p);
                        break;

                // string
                case 's':
                        if ((p = va_arg(ap, char *)) == NULL)
                                p = "(null)";
                        if (width > 0 && padc != '-')
                                for (width -= strnlen(p, precision); width > 0; width--)
                                        putch(padc, putdat);
                        for (; (ch = *p++) != '\0' && (precision < 0 || --precision >= 0); width--)
                                if (altflag && (ch < ' ' || ch > '~'))
                                        putch('?', putdat);
                                else
                                        putch(ch, putdat);
                        for (; width > 0; width--)
                                putch(' ', putdat);
                        break;
                           // (signed) decimal
                case 'd':
                        num = getint(&ap, lflag);
                        if ((long long) num < 0) {
                                putch('-', putdat);
                                num = -(long long) num;
                        }
                        base = 10;
                        goto number;

                // unsigned decimal
                case 'u':
                        num = getuint(&ap, lflag);
                        base = 10;
                        goto number;

                // (unsigned) octal
                case 'o':
                        // Replace this with your code.
                        putch('X', putdat);
                        putch('X', putdat);
                        putch('X', putdat);
                        break;

                // pointer
                case 'p':
                        putch('0', putdat);
                        putch('x', putdat);
                        num = (unsigned long long)
                                (uintptr_t) va_arg(ap, void *);
                        base = 16;
                        goto number;
                         // (unsigned) hexadecimal
                case 'x':
                        num = getuint(&ap, lflag);
                        base = 16;
                number:
                        printnum(putch, putdat, num, base, width, padc);
                        break;

                // escaped '%' character
                case '%':
                        putch(ch, putdat);
                        break;

                // unrecognized escape sequence - just print it literally
                default:
                        putch('%', putdat);
                        for (fmt--; fmt[-1] != '%'; fmt--)
                                /* do nothing */;
                        break;
                }
        }
}

void
printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...)
{
        va_list ap;
       va_start(ap, fmt);
        vprintfmt(putch, putdat, fmt, ap);
        va_end(ap);
}

struct sprintbuf {
        char *buf;
        char *ebuf;
        int cnt;
};

static void
sprintputch(int ch, struct sprintbuf *b)
{
        b->cnt++;
        if (b->buf < b->ebuf)
                *b->buf++ = ch;
}

int
vsnprintf(char *buf, int n, const char *fmt, va_list ap)
{
        struct sprintbuf b = {buf, buf+n-1, 0};

        if (buf == NULL || n < 1)
                return -E_INVAL;

        // print the string to the buffer
        vprintfmt((void*)sprintputch, &b, fmt, ap);

        // null terminate the buffer
        *b.buf = '\0';
        return b.cnt;
}

int
snprintf(char *buf, int n, const char *fmt, ...)
{
        va_list ap;
        int rc;

        va_start(ap, fmt);
        rc = vsnprintf(buf, n, fmt, ap);
        va_end(ap);

        return rc;
}                                                                                                                                        

console.c

/* See COPYRIGHT for copyright information. */
  
#include <inc/x86.h>
#include <inc/memlayout.h>
#include <inc/kbdreg.h>
#include <inc/string.h>
#include <inc/assert.h>

#include <kern/console.h>

static void cons_intr(int (*proc)(void));
static void cons_putc(int c);

// Stupid I/O delay routine necessitated by historical PC design flaws
static void
delay(void)
{
        inb(0x84);
        inb(0x84);
        inb(0x84);
        inb(0x84);
}

/***** Serial I/O code *****/

#define COM1            0x3F8

#define COM_RX          0       // In:  Receive buffer (DLAB=0)
#define COM_TX          0       // Out: Transmit buffer (DLAB=0)
#define COM_DLL         0       // Out: Divisor Latch Low (DLAB=1)
#define COM_DLM         1       // Out: Divisor Latch High (DLAB=1)
#define COM_IER         1       // Out: Interrupt Enable Register
#define   COM_IER_RDI   0x01    //   Enable receiver data interrupt
#define COM_IIR         2       // In:  Interrupt ID Register
#define COM_FCR         2       // Out: FIFO Control Register
#define COM_LCR         3       // Out: Line Control Register
#define   COM_LCR_DLAB  0x80    //   Divisor latch access bit
#define   COM_LCR_WLEN8 0x03    //   Wordlength: 8 bits
#define COM_MCR         4       // Out: Modem Control Register
#define   COM_MCR_RTS   0x02    // RTS complement
#define   COM_MCR_DTR   0x01    // DTR complement
#define   COM_MCR_OUT2  0x08    // Out2 complement
#define COM_LSR         5       // In:  Line Status Register
#define   COM_LSR_DATA  0x01    //   Data available
#define   COM_LSR_TXRDY 0x20    //   Transmit buffer avail
#define   COM_LSR_TSRE  0x40    //   Transmitter off

static bool serial_exists;

static int
serial_proc_data(void)
{
        if (!(inb(COM1+COM_LSR) & COM_LSR_DATA))
                return -1;
        return inb(COM1+COM_RX);
}

void
serial_intr(void)
{
        if (serial_exists)
                cons_intr(serial_proc_data);
}
static void
serial_putc(int c)
{
        int i;

        for (i = 0;
             !(inb(COM1 + COM_LSR) & COM_LSR_TXRDY) && i < 12800;
             i++)
                delay();

        outb(COM1 + COM_TX, c);
}

static void
serial_init(void)
{
        // Turn off the FIFO
        outb(COM1+COM_FCR, 0);

        // Set speed; requires DLAB latch
        outb(COM1+COM_LCR, COM_LCR_DLAB);
        outb(COM1+COM_DLL, (uint8_t) (115200 / 9600));
        outb(COM1+COM_DLM, 0);

        // 8 data bits, 1 stop bit, parity off; turn off DLAB latch
        outb(COM1+COM_LCR, COM_LCR_WLEN8 & ~COM_LCR_DLAB);

        // No modem controls
        outb(COM1+COM_MCR, 0);
        // Enable rcv interrupts
        outb(COM1+COM_IER, COM_IER_RDI);
   // Clear any preexisting overrun indications and interrupts
        // Serial port doesn't exist if COM_LSR returns 0xFF
        serial_exists = (inb(COM1+COM_LSR) != 0xFF);
        (void) inb(COM1+COM_IIR);
        (void) inb(COM1+COM_RX);

}



/***** Parallel port output code *****/
// For information on PC parallel port programming, see the class References
// page.

static void
lpt_putc(int c)
{
        int i;

        for (i = 0; !(inb(0x378+1) & 0x80) && i < 12800; i++)
                delay();
        outb(0x378+0, c);
        outb(0x378+2, 0x08|0x04|0x01);
        outb(0x378+2, 0x08);
}




/***** Text-mode CGA/VGA display output *****/

static unsigned addr_6845;
static uint16_t *crt_buf;
static uint16_t crt_pos;

static void
cga_init(void)
{
        volatile uint16_t *cp;
        uint16_t was;
        unsigned pos;

        cp = (uint16_t*) (KERNBASE + CGA_BUF);
        was = *cp;
        *cp = (uint16_t) 0xA55A;
        if (*cp != 0xA55A) {
                cp = (uint16_t*) (KERNBASE + MONO_BUF);
                addr_6845 = MONO_BASE;
        } else {
                *cp = was;
                addr_6845 = CGA_BASE;
        }

        /* Extract cursor location */
        outb(addr_6845, 14);
        pos = inb(addr_6845 + 1) << 8;
        outb(addr_6845, 15);
        pos |= inb(addr_6845 + 1);

        crt_buf = (uint16_t*) cp;
        crt_pos = pos;
}
static void
cga_putc(int c)
{
        // if no attribute given, then use black on white
        if (!(c & ~0xFF))
                c |= 0x0700;

        switch (c & 0xff) {
        case '\b':
                if (crt_pos > 0) {
                        crt_pos--;
                        crt_buf[crt_pos] = (c & ~0xff) | ' ';
                }
                break;
        case '\n':
                crt_pos += CRT_COLS;
                /* fallthru */
        case '\r':
                crt_pos -= (crt_pos % CRT_COLS);
                break;
        case '\t':
                cons_putc(' ');
                cons_putc(' ');
                cons_putc(' ');
                cons_putc(' ');
                cons_putc(' ');
                break;
        default:
                crt_buf[crt_pos++] = c;         /* write the character */
                break;
        }
      // What is the purpose of this?
        if (crt_pos >= CRT_SIZE) {
                int i;

                memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
                for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
                        crt_buf[i] = 0x0700 | ' ';
                crt_pos -= CRT_COLS;
        }

        /* move that little blinky thing */
        outb(addr_6845, 14);
        outb(addr_6845 + 1, crt_pos >> 8);
        outb(addr_6845, 15);
        outb(addr_6845 + 1, crt_pos);
}


/***** Keyboard input code *****/

#define NO              0

#define SHIFT           (1<<0)
#define CTL             (1<<1)
#define ALT             (1<<2)

#define CAPSLOCK        (1<<3)
#define NUMLOCK         (1<<4)
#define SCROLLLOCK      (1<<5)

#define E0ESC           (1<<6)
static uint8_t shiftcode[256] =
{
        [0x1D] = CTL,
        [0x2A] = SHIFT,
        [0x36] = SHIFT,
        [0x38] = ALT,
        [0x9D] = CTL,
        [0xB8] = ALT
};

static uint8_t togglecode[256] =
{
        [0x3A] = CAPSLOCK,
        [0x45] = NUMLOCK,
        [0x46] = SCROLLLOCK
};

static uint8_t normalmap[256] =
{
        NO,   0x1B, '1',  '2',  '3',  '4',  '5',  '6',  // 0x00
        '7',  '8',  '9',  '0',  '-',  '=',  '\b', '\t',
        'q',  'w',  'e',  'r',  't',  'y',  'u',  'i',  // 0x10
        'o',  'p',  '[',  ']',  '\n', NO,   'a',  's',
        'd',  'f',  'g',  'h',  'j',  'k',  'l',  ';',  // 0x20
        '\'', '`',  NO,   '\\', 'z',  'x',  'c',  'v',
        'b',  'n',  'm',  ',',  '.',  '/',  NO,   '*',  // 0x30
        NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
        NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
        '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
        '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
        [0xC7] = KEY_HOME,            [0x9C] = '\n' /*KP_Enter*/,
        [0xB5] = '/' /*KP_Div*/,      [0xC8] = KEY_UP,
      [0xC9] = KEY_PGUP,            [0xCB] = KEY_LF,
        [0xCD] = KEY_RT,              [0xCF] = KEY_END,
        [0xD0] = KEY_DN,              [0xD1] = KEY_PGDN,
        [0xD2] = KEY_INS,             [0xD3] = KEY_DEL
};

static uint8_t shiftmap[256] =
{
        NO,   033,  '!',  '@',  '#',  '$',  '%',  '^',  // 0x00
        '&',  '*',  '(',  ')',  '_',  '+',  '\b', '\t',
        'Q',  'W',  'E',  'R',  'T',  'Y',  'U',  'I',  // 0x10
        'O',  'P',  '{',  '}',  '\n', NO,   'A',  'S',
        'D',  'F',  'G',  'H',  'J',  'K',  'L',  ':',  // 0x20
        '"',  '~',  NO,   '|',  'Z',  'X',  'C',  'V',
        'B',  'N',  'M',  '<',  '>',  '?',  NO,   '*',  // 0x30
        NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
        NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
        '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
        '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
        [0xC7] = KEY_HOME,            [0x9C] = '\n' /*KP_Enter*/,
        [0xB5] = '/' /*KP_Div*/,      [0xC8] = KEY_UP,
        [0xC9] = KEY_PGUP,            [0xCB] = KEY_LF,
        [0xCD] = KEY_RT,              [0xCF] = KEY_END,
        [0xD0] = KEY_DN,              [0xD1] = KEY_PGDN,
        [0xD2] = KEY_INS,             [0xD3] = KEY_DEL
};

#define C(x) (x - '@')

static uint8_t ctlmap[256] =
{
        NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO,
        NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO,
        C('Q'),  C('W'),  C('E'),  C('R'),  C('T'),  C('Y'),  C('U'),  C('I'),
        C('O'),  C('P'),  NO,      NO,      '\r',    NO,      C('A'),  C('S'),
        C('D'),  C('F'),  C('G'),  C('H'),  C('J'),  C('K'),  C('L'),  NO,
        NO,      NO,      NO,      C('\\'), C('Z'),  C('X'),  C('C'),  C('V'),
        C('B'),  C('N'),  C('M'),  NO,      NO,      C('/'),  NO,      NO,
        [0x97] = KEY_HOME,
        [0xB5] = C('/'),                [0xC8] = KEY_UP,
        [0xC9] = KEY_PGUP,              [0xCB] = KEY_LF,
        [0xCD] = KEY_RT,                [0xCF] = KEY_END,
        [0xD0] = KEY_DN,                [0xD1] = KEY_PGDN,
        [0xD2] = KEY_INS,               [0xD3] = KEY_DEL
};

static uint8_t *charcode[4] = {
        normalmap,
        shiftmap,
        ctlmap,
        ctlmap
};

/*
 * Get data from the keyboard.  If we finish a character, return it.  Else 0.
 * Return -1 if no data.
 */
static int
                                                                                                                                      
                                                             
kbd_proc_data(void)
{
        int c;
        uint8_t stat, data;
        static uint32_t shift;

        stat = inb(KBSTATP);
        if ((stat & KBS_DIB) == 0)
                return -1;
        // Ignore data from mouse.
        if (stat & KBS_TERR)
                return -1;

        data = inb(KBDATAP);

        if (data == 0xE0) {
                // E0 escape character
                shift |= E0ESC;
                return 0;
        } else if (data & 0x80) {
                // Key released
                data = (shift & E0ESC ? data : data & 0x7F);
                shift &= ~(shiftcode[data] | E0ESC);
                return 0;
        } else if (shift & E0ESC) {
                // Last character was an E0 escape; or with 0x80
                data |= 0x80;
                shift &= ~E0ESC;
        }

        shift |= shiftcode[data];
        shift ^= togglecode[data];

      c = charcode[shift & (CTL | SHIFT)][data];
        if (shift & CAPSLOCK) {
                if ('a' <= c && c <= 'z')
                        c += 'A' - 'a';
                else if ('A' <= c && c <= 'Z')
                        c += 'a' - 'A';
        }

        // Process special keys
        // Ctrl-Alt-Del: reboot
        if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
                cprintf("Rebooting!\n");
                outb(0x92, 0x3); // courtesy of Chris Frost
        }

        return c;
}

void
kbd_intr(void)
{
        cons_intr(kbd_proc_data);
}

static void
kbd_init(void)
{
}



/***** General device-independent console code *****/
// Here we manage the console input buffer,
// where we stash characters received from the keyboard or serial port
// whenever the corresponding interrupt occurs.

#define CONSBUFSIZE 512

static struct {
        uint8_t buf[CONSBUFSIZE];
        uint32_t rpos;
        uint32_t wpos;
} cons;

// called by device interrupt routines to feed input characters
// into the circular console input buffer.
static void
cons_intr(int (*proc)(void))
{
        int c;

        while ((c = (*proc)()) != -1) {
                if (c == 0)
                        continue;
                cons.buf[cons.wpos++] = c;
                if (cons.wpos == CONSBUFSIZE)
                        cons.wpos = 0;
        }
}

// return the next input character from the console, or 0 if none waiting
int
cons_getc(void)
{
        int c;
    // poll for any pending input characters,
        // so that this function works even when interrupts are disabled
        // (e.g., when called from the kernel monitor).
        serial_intr();
        kbd_intr();

        // grab the next character from the input buffer.
        if (cons.rpos != cons.wpos) {
                c = cons.buf[cons.rpos++];
                if (cons.rpos == CONSBUFSIZE)
                        cons.rpos = 0;
                return c;
        }
        return 0;
}

// output a character to the console
static void
cons_putc(int c)
{
        serial_putc(c);
        lpt_putc(c);
        cga_putc(c);
}

// initialize the console devices
void
cons_init(void)
{
        cga_init();
        kbd_init();
        serial_init();

        if (!serial_exists)
                cprintf("Serial port does not exist!\n");
}


// `High'-level console I/O.  Used by readline and cprintf.

void
cputchar(int c)
{
        cons_putc(c);
}

int
getchar(void)
{
        int c;

        while ((c = cons_getc()) == 0)
                /* do nothing */;
        return c;
}

int
iscons(int fdnum)
{
        // used by readline
        return 1;
}
                                                                                                                       
                                                                                                      
                                                                         

printf.c 提供接口的包装,printfmt.c 是具体的函数实现,console.c 是更加具体的函数的实现。

Exercise 8. We have omitted a small fragment of code - the code necessary to print octal numbers using patterns of the form “%o”. Find and fill in this code fragment.

 // (unsigned) octal
                case 'o':
                        // Replace this with your code.
                        putch('X', putdat);
                        putch('X', putdat);
                        putch('X', putdat);
                        break;

//my code
case 'o':
putch('0', putdat);//为什么要加上这一句
//猜测这句话可以在输出的数字前面加上 0 ,符合八进制的格式
//putch 函数将字符输出到屏幕后,会在地址上自增一个数字 putdat
num = getuint(&ap, lflag);
                        base = 8;
                        goto number;



//完全参考上面的代码:
 // unsigned decimal
                case 'u':
                        num = getuint(&ap, lflag);
                        base = 10;
                        goto number;

Be able to answer the following questions:

Explain the interface between printf.c and console.c. Specifically, what function does console.c export? How is this function used by printf.c?

基础的理解:printf.c 提供统一的接口,console.c 提供底层的实现。
具体的解释:
printf.c 调用了 printfmt.c 中的 vprintfmt 函数,printf.c 调用了console.c 的 cputchar 函数。总体来说 printfmt.c 和 console.c提供给了 printf.c 可调用的函数,console.c 直接与底层打交道。

console.c 当中定义了几个字符集
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Explain the following from console.c:

//显示超过一屏可以显示的数目,,
//突然意识到,是不是移动一次屏幕,之后屏幕上所有的内容都需要重新刷新一遍。是的,就算不移动,屏幕也是每秒刷新的。
//CRT_ROWS,CRT_COLS:CRT显示器行列最大值, 此处是25x80
1      if (crt_pos >= CRT_SIZE) {
2              int i;
//清楚 buf 当中的第一行的数据
//crt_buf 指向的是显示器 I/O 的地址
//将第 1~(n-1)行的数据移动到 0~(n-2)行

3              memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
4              for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
5                      crt_buf[i] = 0x0700 | ' ';
//空出来的那一行用空格填充。
6              crt_pos -= CRT_COLS;
//pos 移动到写入字符串的末尾。
7      }

For the following questions you might wish to consult the notes for Lecture 2. These notes cover GCC’s calling convention on the x86.
Trace the execution of the following code step-by-step:

int x = 1, y = 3, z = 4;
cprintf("x %d, y %x, z %d\n", x, y, z);

In the call to cprintf(), to what does fmt point? To what does ap point?
List (in order of execution) each call to cons_putc, va_arg, and vcprintf. For cons_putc, list its argument as well. For va_arg, list what ap points to before and after the call. For vcprintf list the values of its two arguments.

在 kern/init.c 下的 i386_init() 加入要测试的代码。

// lab1 Exercise_8
{
    cprintf("Lab1_Exercise_8:\n");
    int x = 1, y = 3, z = 4;
    // 
    Lab1_exercise8_3:
    cprintf("x %d, y %x, z %d\n", x, y, z);

    unsigned int i = 0x00646c72;
    cprintf("H%x Wo%s", 57616, &i);
}

使用 disassemble + 函数名 来查看反汇编的结果。
在这里插入图片描述
使用 disassemble + /m + 函数名 来查看汇编代码 和 源代码 对应的字符。
在这里插入图片描述
第一段代码和汇编解释,但是好像都不是应该要的
在这里插入图片描述
好像看到了 fmt
具体方法:
1、在 cprintf () 处打断点,之后使用 si 一路向下。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
cprint ()函数好像是一个字符一个字符打印的
在这里插入图片描述

调试过程中可以看到 fmt = 0xf0101b23 指向字符串,ap = 0xf010ffc4 指向栈顶
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上网搜索了一些资料
C语言参数的入栈顺序是从右至左,这个是 C语言支持可变长参数的原因。这种入栈方式保证最左边的参数在栈顶。
如果采用从左至右的入栈方式,最左边的参数会被压在栈底,除非知道参数的个数,否则无法找到最左边的参数。
疑问:真的不需要统计参数的个数吗?

Run the following code.

unsigned int i = 0x00646c72;
cprintf("H%x Wo%s", 57616, &i);
//传递 i 地址,对 i 上的内容一个字节一个字节的读取。

What is the output? Explain how this output is arrived at in the step-by-step manner of the previous exercise. Here’s an ASCII table that maps bytes to characters.
The output depends on that fact that the x86 is little-endian. If the x86 were instead big-endian what would you set i to in order to yield the same output? Would you need to change 57616 to a different value?
在这里插入图片描述
在这里插入图片描述
57616 的十六进制形式为 E110。
由于是 little-endian , 0x00646c72 在内存中的位置如下图。

0x00 - '\0'
0x72 - 'r'
0x6c - 'l'
0x64 - 'd'

在这里插入图片描述

In the following code, what is going to be printed after ‘y=’? (note: the answer is not a specific value.) Why does this happen?

 cprintf("x=%d y=%d", 3);

y 会是一个随机的整型值。因为可变参数是从右向左压入栈中,栈顶为最左边的参数,读完之后,在 3 所在的位置上加一,得到的就是右边的参数。由于没有,将会是一个随机值。

Let’s say that GCC changed its calling convention so that it pushed arguments on the stack in declaration order, so that the last argument is pushed last. How would you have to change cprintf or its interface so that it would still be possible to pass it a variable number of arguments?
那就不支持可变参数了,需要传入参数的个数,并且指明每个参数在内存中的大小。

Challenge Enhance the console to allow text to be printed in different colors. The traditional way to do this is to make it interpret ANSI escape sequences embedded in the text strings printed to the console, but you may use any mechanism you like. There is plenty of information on the 6.828 reference page and elsewhere on the web on programming the VGA display hardware. If you’re feeling really adventurous, you could try switching the VGA hardware into a graphics mode and making the console draw text onto the graphical frame buffer.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值