You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自制OS内核中无法将char*字符串输出至VGA内存的问题

自制C++ OS内核打印字符串时固定输出乱码的问题

我用C++编写自制OS内核,正在开发打印功能。单个字符的print_char函数可以正常将字符输出到VGA内存(0xB8000),但调用print_str(const char* str)函数时,无论输入的字符串内容和长度如何,屏幕上都会固定输出三个乱码符号:

  • 无法描述的随机符号
  • 平方根符号
  • 笑脸符号

相关代码

void print_char(const char character) {
    Char* buffer = reinterpret_cast<Char*>(VGA_ADDRESS);
    
    // 处理换行符
    if(character == '\n') {
        print_newline();
        return;
    }

    if(col >= NUM_COLS) {
        print_newline();
    }

    buffer[col + NUM_COLS * row] = {static_cast<uint8_t>(character), color};
    col++; // 增加当前行的字符计数
}


// 打印字符串到屏幕
void print_str(const char* str) {
    // 按字符串长度调用print_char
    for(size_t i = 0; str[i] != '\0'; i++) {
        print_char(str[i]);
    }
}

链接脚本

OUTPUT_FORMAT("binary")
OUTPUT_ARCH(i386)

ENTRY(_start)

SECTIONS
{
    /* Bootloader段在0x7C00(BIOS将引导加载程序加载到此处) */
    . = 0x7C00;

    .boot : {
        *(.boot)      /* 包含所有.boot段 */
        *(.text)      /* 包含引导加载程序代码的所有.text段 */
    }

    /* 对齐到1 MiB(内核起始位置) */
    . = 0x100000;

    /* 内核段 */
    .text : ALIGN(4096)
    {
        __kernel_start = .;  /* 这是内核的起始地址 */
        *(.text)
    }

    .rodata : ALIGN(4096)
    {
        *(.rodata)
    }

    .data : ALIGN(4096)
    {
        *(.data)
    }

    .bss : ALIGN(4096)
    {
        *(COMMON)
        *(.bss)
    }

    __kernel_end = .;  /* 这是内核的结束地址 */
}

问题排查与解决思路

1. 全局光标变量未初始化

print_char依赖全局的col(列)和row(行)变量定位VGA缓冲区的写入位置,但如果这两个变量没有被显式初始化,内核启动后它们的值是内存中的随机垃圾数据。第一次调用print_str时,col可能是一个超出范围的数值,触发换行逻辑,但row的随机值会导致写入位置完全错误,从而输出乱码。
解决方法:在全局作用域初始化col = 0、row = 0,或者在内核启动入口函数中显式设置光标初始位置。

2. VGA字符结构体对齐错误

VGA缓冲区的每个字符占2字节(1字节ASCII码 + 1字节颜色属性)。如果你的Char结构体因为C++的默认对齐规则被填充为4字节,那么写入缓冲区时会跳过字节,导致字符和属性错位,出现乱码。
解决方法:给Char结构体添加强制对齐属性,比如:

struct __attribute__((packed)) Char {
    uint8_t c;
    uint8_t color;
};

或者使用编译指令强制1字节对齐:

#pragma pack(push, 1)
struct Char {
    uint8_t c;
    uint8_t color;
};
#pragma pack(pop)

3. 字符串缺少空终止符

如果传入print_str的字符串没有以\0结尾,循环会持续读取内存中的垃圾数据,直到碰到\0,导致输出多余的乱码。
解决方法:用双引号定义字符串(编译器会自动添加\0);如果是手动构造的字符串,要手动添加\0终止符。

4. 内核加载地址不匹配

链接脚本将内核放在0x100000(1MiB)地址,但如果bootloader没有将内核正确加载到这个地址,全局变量(比如col/row)的实际内存位置会和预期不符,导致读取错误的值。
解决方法:检查bootloader的内核加载代码,确保内核被加载到0x100000地址;同时确认链接脚本中__kernel_start和__kernel_end的定义正确,bootloader加载时覆盖了正确的内存范围。

内容的提问来源于stack exchange,提问作者Ioane Baidoshvili

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 02:33:20