内核主函数中的这个神秘字符是啥?我怎样才能删除它?
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 指令。 (通常内核不会)以上是关于内核主函数中的这个神秘字符是啥?我怎样才能删除它?的主要内容,如果未能解决你的问题,请参考以下文章