C指针是否会导致我的家庭内核VGA部分出现三重故障?

时间:2014-09-04 02:43:32

标签: c pointers operating-system kernel

我想要一个简单的Hello World!内核启动。每当我打开我的虚拟机时,我都会看到GRUB加载,选择我的操作系统,然后在屏幕上获得一个随机字符,并在三重故障时崩溃。我相信问题在于我的VGA部分。我怀疑问题出在我使用C指针的某个地方。 C和指针都不是强项。具体来说,我怀疑涉及put_char()函数。有一些涉及指针的编译器问题。我究竟做错了什么?我该如何解决这个问题?

感谢您的帮助!

以下是我的VGA部分的代码:

/*
    This file will handle dealing with VGA stuff so I can print to the screen.
*/

#include <system.h>

/* this pointer will be set to the VGA access memory address */
unsigned short *text_ptr;
int attrib = 0x0F; // foreground and background color gets set in here.
int csr_x = 0; // cursor x position
int csr_y = 0; // cursor y position
int screen_width = 80; // width of the screen in columns
int screen_height = 25; // height of the screen in rows

void vga_initialize()
{
    // set the text pointer to the proper location in memory
    text_ptr = (unsigned short *) 0x0B8000;
    clear_screen();
}

void set_text_color(unsigned char foreground_color, unsigned char background_color)
{
    attrib = (background_color << 4) | (foreground_color & 0x0F);
}

void put_str(char *text)
{
    // figure out the length of the string we'll be printing
    char *my_text = text;    // make a local copy of the pointer so the orinal's not overwritten.
    int str_length;
    for (str_length = 0; *my_text != '\0'; my_text++)    // set the string lenght to 0; if the character at the pointer isn't zero; increment the pointer
    {
        str_length++;    // increment the count
    }
    // for each character in the string
    for (int i = 0; i < str_length; i++)
    {
        put_char(my_text[i]); // put the character on the screen
    }
}

void put_char(char c)
{
    unsigned short *index_ptr;
    unsigned int my_attrib = attrib << 8;

    if (c == 0x08) // backspace
    {
        if (csr_x != 0)
        {
            csr_x--;
        }
    }
    else if (c == 0x09) // tab
    {
        csr_x = (csr_x + 8) & ~(8 - 1); // increment x but only to a point that will make it divisible by 8 (i dunno, lawl)
    }
    else if (c == '\r') // carriage return, move cursor to beginning of row
    {
        csr_x = 0;
    }
    else if (c == '\n') // newline
    {
        csr_x = 0;
        csr_y++;
    }
    else if (c >= ' ') // any character greater than or equal to space is a printable character
    {
        // figure out where to put the character
        index_ptr = text_ptr + (csr_y * screen_width + csr_x);
        // put the character and it's attribute into memory, which will put it on the screen when it refreshes.
        *index_ptr = c | my_attrib;
    }

    scroll();
    move_csr();
}



void scroll()
{
    unsigned short space_char = 0x20 | (attrib << 8);
    // used to calculate the offsets needed when moving data around in the vga buffer memory
    int temp_offset;
    // screen's full, we need to scroll
    if (csr_y >= screen_height)
    {
        // move everything up
        temp_offset = csr_y - screen_height + 1; // offset for calculating the addresses to use in the vga memory
        memcpy(text_ptr, text_ptr + temp_offset * screen_width, (screen_height - temp_offset) * screen_width * 2);
        // set the chunk of memory that occupies the last line of text to spaces
        memsetw(text_ptr + (screen_height - temp_offset) * screen_width, space_char, screen_width);
        // adjust the position of the cursor
        csr_y = screen_height - 1;
    }
}

void move_csr()
{
    unsigned char curr_pos = csr_y * screen_width + csr_x;
    outportb(0x3D4, 14);
    outportb(0x3D5, curr_pos >> 8);
    outportb(0x3D4, 15);
    outportb(0x3D5, curr_pos);
}

void clear_screen()
{
    unsigned short space_char;

    // i need to generate the ascii code? that represents a space of the proper background color
    space_char = 0x20 | (attrib << 8);

    // write spaces to the entire screen
    for (int i = 0; i < screen_height; i++)
    {
        for (int j = 0; j < screen_width; j++)
        {
            memsetw(text_ptr + i * screen_width, space_char, 1);
        }
    }

    // reset the cursors x and y position
    csr_x = 0;
    csr_y = 0;
    move_csr(); // and reset the cursor in the hardware
}

我一直在关注各种教程,试图创建自己的操作系统。到目前为止,我一直在使用描述here的GNU工具链。我正在使用带有工具链的GNU汇编程序,因为我遵循了指示,并没有考虑将NASM构建到其中。我宁愿学习另一种装配,而不是重做工具链。 :-P我一直在模拟我对Bran's Kernel Development tutorial.的尝试我试图将C代码和GAS代码保存在单独的文件中,因为我不喜欢这些教程如何将所有程序集合到引导加载程序中。我是在Windows 7机器上制作的,但是使用Debian进行编码。我在Windows上安装了Oracle VirtualBox,并使用它来模拟Debian。然后我使用我的开发工具设置Debian,并使用文本编辑器进行编码。

下面是我正在使用的所有代码的链接,崩溃的VirtualBox日志,我在Debian终端中用来构建我的代码的命令的日志文件,目标文件,二进制文件和我制作的iso文件。

http://wikisend.com/download/243118/POS_C3.zip

如果您想自己查看错误,那么您应该能够将pos.iso挂载到虚拟机上并运行它。它应该像宣传的那样崩溃。这时我已经为引导加载程序,内核主函数,GDT和VGA代码制作了代码。我在自己的C文件中有内存和I / O端口函数,GDT的GAS也在它自己的文件中。

1 个答案:

答案 0 :(得分:3)

我看到两个问题:

1)您的put_str()函数访问text字符串末尾之外的无效内存,这是未定义的行为:

void put_str(char *text)
{
    char *my_text = text; // (1)
    int str_length;
    for (str_length = 0; *my_text != '\0'; my_text++) // (2)
    {
        str_length++;    // increment the count
    }
    for (int i = 0; i < str_length; i++)
    {
        put_char(my_text[i]); // (3)
    }
}

在这种情况下,您将my_text分配给(1)处字符串的开头,然后将其前进到(2)处字符串的末尾。然后,您将从(3)处的字符串末尾开始索引它,这将读取内存超出范围。您可能打算在(3)处写text[i]而不是my_text[i],或者您可能忘记在该循环之前重置my_text = text;(要么产生相同的结果)。您也可以通过调用strlen()来替换整个第一个循环,但不是那个更简单的解决方案:

void put_str(char *text)
{
    while (*text != 0)
    {
        put_char(*text);
        text++;
    }
}

这会遍历字符串,寻找NUL终结符,并在每个字符输出时输出。不需要先计算字符串长度,然后再重复字符串。

2)memcpy()函数内对scroll()的调用具有未定义的行为,因为源和目标内存范围可能重叠:

memcpy(text_ptr, text_ptr + temp_offset * screen_width, (screen_height - temp_offset) * screen_width * 2);

这可能不会导致您看到的三重错误异常,但它可能导致数据以奇怪的方式被错误地复制,具体取决于标准库中memcpy()的实现方式。但由于它是未定义的行为,你必须为任何事情做好准备,包括从鼻子里冒出来的恶魔。

简单易用的解决方案是用memmove()调用替换它,即使源和目标内存范围重叠,它也定义了行为:

memmove(text_ptr, text_ptr + temp_offset * screen_width, (screen_height - temp_offset) * screen_width * 2);