内核主函数中的这个神秘字符是啥?我怎样才能删除它?

Posted

技术标签:

【中文标题】内核主函数中的这个神秘字符是啥?我怎样才能删除它?【英文标题】:what is this mysterious character in kernel main function? How can i remove it?内核主函数中的这个神秘字符是什么?我怎样才能删除它? 【发布时间】:2020-10-22 13:37:41 【问题描述】:

我正在尝试通过链接 https://wiki.osdev.org/Bare_Bones 创建自己的内核 作者曾提到“后面跟着一些神秘人物。” 为什么会出现这个角色?如何删除它?

我使用的是 kernel.c 文件,只是将内核 main 更改为打印 ASCII 字符。

void kernel_main(void) 

    terminal_initialize();

    terminal_writestring(" ____                         ___  ____");
            terminal_putchar('\n');
    terminal_writestring("|  _ \\ _   _ _ __ ___   __ _ / _ \\/ ___|");
            terminal_putchar('\n');
    terminal_writestring("| |_) | | | | '_ ` _ \\ / _` | | | \\___ \\");
            terminal_putchar('\n');
    terminal_writestring("|  __/| |_| | | | | | | (_| | |_| |___) |");
            terminal_putchar('\n');
    terminal_writestring("|_|    \\__,_|_| |_| |_|\\__,_|\\___/|____/");
            terminal_putchar('\n');

这是我的结果

【问题讨论】:

请贴出terminal_writestring()的代码,但我认为问题在于它在行尾写了'\r\n'而不是'\n' 问题是奇数符号是与\n (LINEFEED) 关联的字符。字符集是CodePage 437。从输出中可以清楚地看出,您的代码将\n 视为\r\n(换行和回车),因为您将其转到下一行的第一列。如果这是您想要的行为,那么您所要做的就是更新光标位置,但丢弃 \n 而不是打印它。您的代码当前正在打印 \n 并将光标更新到下一行的开头。 其他不想直接打印到屏幕上的字符 - TAB(\t)、回车(\r)、换行(\n)、退格(\b)。所有这些都必须经过特殊处理。 你能给我们看一下 terminal_putchar 函数的代码吗? 【参考方案1】:

@Ken Y-N 是正确的,因为您应该编写 \r\n 而不是 \n(即 CRLF 而不是 LF)。

但是,更好的解决方案(在我看来)是:

a) 检查terminal_putchar 中的字符是否为\n,以及是否只是将terminal_column 设置为0 并将terminal_row 加1。:

void terminal_putchar(char c) 

    /* check that the previous character wasn't a '\r', if it was print CRLF as normal */
    if (c == '\n' && terminal_buffer[terminal_column * VGA_WIDTH + terminal_row] != '\r') 
        /* move cursor to start of next line (without printing a CRLF) */
        terminal_column = 0;
        terminal_row += 1;
        return;
    
    terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
    if (++terminal_column == VGA_WIDTH) 
        terminal_column = 0;
        if (++terminal_row == VGA_HEIGHT)
            terminal_row = 0;
    

甚至更好 b) 检查 terminal_putchar 中的字符是否为 \n,如果是则打印 CRLF:

void terminal_putchar(char c) 

    /* turn LF -> CRLF if missing CR */
    if (c == '\n' && terminal_buffer[terminal_column * VGA_WIDTH + terminal_row] != '\r') 
        terminal_putentryat('\r', terminal_color, terminal_column, terminal_row);
    
    terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
    if (++terminal_column == VGA_WIDTH) 
        terminal_column = 0;
        if (++terminal_row == VGA_HEIGHT)
            terminal_row = 0;
    

希望对您有所帮助。

编辑:对于一些额外的优化,您可以检测 \r 并设置一些布尔可用值(例如一个 1 或 0 的 int),然后将 terminal_buffer[terminal_column ... 替换为该布尔可用值的名称.这允许保证 4 字节对齐的访问,这总是更快(有关更多信息,请参阅here)。示例代码:

uint8_t was_cr = 0;

void terminal_putchar(char c) 

    /* turn LF -> CRLF if missing CR */
    if (c == '\n' && !was_cr) 
        terminal_putentryat('\r', terminal_color, terminal_column, terminal_row);
    

    /* at this point was_cr has already been read */
    /* considering that was_cr is read every time this function is called, */
    /* it is likely that the CPU will cache it's value, so read times will be short */
    /* even if it isn't cached, memory reads are always faster than memory writes */
    if (c == '\r' && !was_cr) was_cr = 1;
    else if (was_cr) was_cr = 0;

    terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
    if (++terminal_column == VGA_WIDTH) 
        terminal_column = 0;
        if (++terminal_row == VGA_HEIGHT)
            terminal_row = 0;
    

【讨论】:

我认为问题的真正关键是@MichaelPetch 在 cmets 中指出的:当您检测到换行符时,您必须将其复制到屏幕上,only 移动光标。您在答案的文本中没有提到这一点,只是一个代码块中的一个小注释。 IMO 如果您不打算在字符串中包含\n,您可以为终端换行提供一个单独的函数,无需检查任何内容,只需重置列和递增的行。如果你确实想在 writestring / putchar 中处理\n,那么在调用者中利用它。 @PeterCordes 嗨,谢谢我明白了,我实际上是在打印“\n”,我如何省略转义序列(\n、\r、\t 等)并将我的代码处理为通常?我使用字符串功能等吗? - 复制strlen -2(\n) 到缓冲区,然后打印缓冲区? @varun_koganti:显然最简单的方法是一次循环字符串 1 个字节并检查特殊情况,否则只能复制字符并更新列。例如让 writestring 分别为每个 char 调用 putchar 。这将处理在字符串的 middle 中有 \n\r\n 的情况。如果您想优化并且仍然能够一次复制多个字节,您可以使用 SIMD(MMX 或 SSE2 pcmpeqb / _mm_cmpeq_epi8())一次检查多个字节是否在可打印的 ASCII 范围内,如果您启用SSE 指令。 (通常内核不会)

以上是关于内核主函数中的这个神秘字符是啥?我怎样才能删除它?的主要内容,如果未能解决你的问题,请参考以下文章

我的挤压功能没有按预期工作,我该怎么做才能修复这个功能?

为啥应用 tranwrd 函数后最后一个字符被删除

kszzlog是啥东西?能删除吗

Haskell 中的“让”和“进入”是啥意思?

是啥导致我的堆栈中出现神秘的重复条目?

这些文本格式标记是啥,我怎样才能故意将它们插入另一个文件?