gpt4 book ai didi

c - C 指针是否导致我的自制内核 VGA 部分出现三重错误?

转载 作者:太空宇宙 更新时间:2023-11-04 02:02:22 25 4
gpt4 key购买 nike

我正在尝试获得一个简单的 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 构建到其中。我宁愿学习另一种风格的 Assembly,也不愿重做工具链。 :-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) 您的 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) scroll() 函数内对 memcpy() 的调用具有未定义的行为,因为源和目标内存范围可以重叠:

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

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

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

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

关于c - C 指针是否导致我的自制内核 VGA 部分出现三重错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25656536/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com