模拟Linus Torvalds:从头开始创建自己的操作系统(VI)

好了,稍加括号后,我们将继续进行一系列教程。 如果返回上一代码,则必须将ISR除以零。 现在,我们必须填写发布了其消息的其余ISR(前32个)。 好了,现在我们将继续编程中断,我们将执行IRQ(也称为IRQ) 中断请求。 这些IRQ由硬件设备(例如键盘,鼠标,打印机等)生成。 最初,前8个IRQ会自动映射到IDT的8至15位置。由于我们将前32个IRQ用于异常,所以现在我们必须重新映射它们。 我们将IRQ从32设置为45。为此,我们必须首先重新映射IRQ:

void ND::IRQ::Remap(int pic1, int pic2)
{
#define PIC1 0x20
#define PIC2 0xA0
#define ICW1 0x11
#define ICW4 0x01
/* send ICW1 */
ND::Ports::OutputB(PIC1, ICW1);
ND::Ports::OutputB(PIC2, ICW1);
/* send ICW2 */
ND::Ports::OutputB(PIC1 + 1, pic1); /* remap */
ND::Ports::OutputB(PIC2 + 1, pic2); /* pics */
/* send ICW3 */
ND::Ports::OutputB(PIC1 + 1, 4); /* IRQ2 -> connection to slave */
ND::Ports::OutputB(PIC2 + 1, 2);
/* send ICW4 */
ND::Ports::OutputB(PIC1 + 1, ICW4);
ND::Ports::OutputB(PIC2 + 1, ICW4);
/* disable all IRQs */
ND::Ports::OutputB(PIC1 + 1, 0xFF);
}

现在,我们创建一个函数来安装IRQ:

void ND::IRQ::Install()
{
ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_BLACK);
ND::Screen::PutString("\nInstalling IRQ...");
ND::IRQ::Remap(0x20,0x28);
ND::IDT::SetGate(32,(unsigned)ND::IRQ::IRQ1,0x08,0x8E);
ND::IDT::SetGate(33,(unsigned)ND::IRQ::IRQ2,0x08,0x8E);
ND::IDT::SetGate(34,(unsigned)ND::IRQ::IRQ3,0x08,0x8E);
ND::IDT::SetGate(35,(unsigned)ND::IRQ::IRQ4,0x08,0x8E);
ND::IDT::SetGate(36,(unsigned)ND::IRQ::IRQ5,0x08,0x8E);
ND::IDT::SetGate(37,(unsigned)ND::IRQ::IRQ6,0x08,0x8E);
ND::IDT::SetGate(38,(unsigned)ND::IRQ::IRQ7,0x08,0x8E);
ND::IDT::SetGate(39,(unsigned)ND::IRQ::IRQ8,0x08,0x8E);
ND::IDT::SetGate(40,(unsigned)ND::IRQ::IRQ9,0x08,0x8E);
ND::IDT::SetGate(41,(unsigned)ND::IRQ::IRQ10,0x08,0x8E);
ND::IDT::SetGate(42,(unsigned)ND::IRQ::IRQ11,0x08,0x8E);
ND::IDT::SetGate(43,(unsigned)ND::IRQ::IRQ12,0x08,0x8E);
ND::IDT::SetGate(44,(unsigned)ND::IRQ::IRQ13,0x08,0x8E);
ND::IDT::SetGate(45,(unsigned)ND::IRQ::IRQ14,0x08,0x8E);
ND::IDT::SetGate(46,(unsigned)ND::IRQ::IRQ15,0x08,0x8E);
ND::IDT::SetGate(47,(unsigned)ND::IRQ::IRQ16,0x08,0x8E);
ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_GREEN);
ND::Screen::PutString("done");
asm volatile("sti");
}

asm的句子 STI 我们激活IRQ。 好吧,现在我们去做类似ISR的事情。 基本IRQ的功能:

void ND::IRQ::IRQ1()
{
asm volatile(
"cli \n"
"pushl 0\n"
"pushl 32\n"
"jmp ND_IRQ_Common"
);
}

通用部分(与ISR相同):

extern "C"
void ND_IRQ_Common()
{
asm volatile(
"pusha \n"
"push %ds\n"
"push %es\n"
"push %fs\n"
"push %gs\n"
"movw $0x10, %ax \n"
"movw %ax, %ds \n"
"movw %ax, %es \n"
"movw %ax, %fs \n"
"movw %ax, %gs \n"
"movl %esp, %eax \n"
"push %eax \n"
"movl $ND_IRQ_Handler, %eax \n"
"call *%eax \n"
"popl %eax \n"
"popl %ds \n"
"popl %es \n"
"popl %fs \n"
"popl %gs \n"
"popa \n"
"addl 8, %esp \n"
"iret \n"
);
}

和一个基本的处理程序:

extern "C"
void ND_IRQ_Handler(struct regs* r)
{
void (*handler)(struct regs *r);
if(r->int_no >= 40)
{
ND::Ports::OutputB(0xA0,0x20);
}
ND::Ports::OutputB(0x20,0x20);
}

这样,即使他们仍然不执行任何操作,我们也应该已经激活了IRQ。 在下一章中,我们将看到如何从这些IRQ(例如时钟或键盘)中获取数据。

NextDivel-IRQ


至此,今天的帖子结束了。 如您现在所见,由于其他问题,我的写作时间有所减少。 即使如此,我将继续直到拥有更完整的操作系统


发表您的评论

您的电子邮件地址将不会被发表。 必填字段标有 *

*

*

  1. 负责数据:MiguelÁngelGatón
  2. 数据用途:控制垃圾邮件,注释管理。
  3. 合法性:您的同意
  4. 数据通讯:除非有法律义务,否则不会将数据传达给第三方。
  5. 数据存储:Occentus Networks(EU)托管的数据库
  6. 权利:您可以随时限制,恢复和删除您的信息。

  1.   aitor_cz

    非常感谢Adrian,我一有时间(现在我很忙,尤其是在使用操作系统时),我将开始逐步测试本教程。

  2.   红宝石

    非常感谢您的发言。

  3.   佐助

    我正在做一个基于这些编程语言的项目的问题
    * HTML5
    * CSS3
    * Java
    我的问题是我希望该项目可执行,以便用户在Linux和Windows等操作系统中使用它,能否告诉我我该怎么做?

    1.    阿德里安·阿罗约街

      与此无关,但我还是要回答你。 我认为它将是HTML5,CSS3和JavaScript而不是Java,因为HTML5和Java会致命。 使用HTML5,您可以像以前一样创建网站,然后可以从Internet访问它们。 如果要使其本地化,可以将其打包为Firefox OS和Chrome OS应用程序。 如果您想要的是每个操作系统中都有一个可执行文件,请看一下XUL Runner,它是使用Gecko引擎执行XUL(因此在元素内的HTML5)的工具。

    2.    索德·佩雷斯

      Java框架或面板是一个很好的选择,我已经使用Java框架类作为Web浏览器在窗口中创建了一些可执行应用程序,但是没有在任何页面中使用它,而是在代码中使用了直接路径,并使用php执行了Java语句通过一个负责钢铁的.jar。 尽管我建议使用HTML5,CSS3和JavaScript,因为正如Adrian Java所说,但Html5却需要致命的功能,这让我很头疼

  4.   鲁班

    关于如何制作自己的编程语言的教程会很不错

  5.   伊万

    很好的这一系列文章介绍了如何构建操作系统,您学到了很多东西。 我期待下一个输入,我已经想要在操作系统中使用键盘。 我一直在搞乱git代码,但无法使其与端口0x60和0x64一起使用。 即使我认为对于键盘来说,还是有一个中断让您按下了键。

    1.    阿德里安·阿罗约街

      您实际上可以无中断地获得键盘输入,但是必须在端口0x60上使用ND :: Ports :: InputB进行阅读。 但是,执行此操作的理想方法是使用IRQ中断。 我目前正在尝试这样做,因此需要花费更长的时间才能继续。

      1.    Carlosorta

        嗨,阿德里安(Adrian),我一直在检查代码,它的功能以及它如何帮助我理解一些东西,给我留下了深刻的印象。

        我对它的工作方式有几个小问题,来源是我从您的GIT那里得到的:

        1.-在IRQ部分中,您提到IDT的从0到32的位置用于异常,IRQ从32(0x20)到45(0x2D)的位置使用,但是IRQ总共为16,重映射是不是从0x20到0x30?

        2.-在IRQ部分中,请注意将设置门发送到IDT部分; 当您将它们分开时,请注意,它不再产生除以0的异常,因此有必要为所做的每个修改添加IDT Flush()。 为什么设置定时器时IDT会发生变化,并且0之间的分频停止工作?

        3.-我试图通过一些打印来跟踪代码,以作为执行代码的指示(以免逐步调试),但我意识到没有IRQ在起作用,我是否必须注册其他代码生成IRQ中断?

        1.    Carlosorta

          我忘了提到我找到了以下带有信息的链接:
          http://arstechnica.com/civis/viewtopic.php?f=20&t=899001
          http://www.superfrink.net/athenaeum/OS-FAQ/os-faq-pics.html
          http://orga2.exp.dc.uba.ar/data/downloads/clasespracticas/interrupciones2_clase_17.pdf
          http://www.intel-assembler.it/PORTALE/4/231468_8259A_PIC.pdf

          显然对于处理IRQ,有必要考虑PIC,APIC,IOAPIC使用哪种类型的处理程序。 。 。等等。 有没有办法使IRQ管理动态化,或者您需要尝试运气吗?

  6.   Carlosorta

    下午好,阿德里安。

    我看到我的IRQ出现了问题,这就是为什么无法进行代码改进的原因,我制作了一个项目的副本并开始对其进行分析。 我在中断时向屏幕打印添加了一个功能,以打印struct reg的reg记录; 我发现了几件事,其中有一个注册表正在运行,但我仍然找不到原因。 更改计时器中断以测试键盘中断并执行应做的事情,但我找不到问题,您能帮我继续此好帖子吗? 😀

    我离开了链接(由于我使用Mageia和Grub2,所以我进行了一些修改,我正在使用VirtualBox进行测试)
    https://www.mediafire.com/?93psrgaoozatse8

    等待您的细心回答,如果您有任何疑问或需要什么,我想帮助🙂

    1.    Carlosorta

      我忘了提一下,我还检查了KernelPanic,因为ISR无法正常工作,并且在堆栈顶部存在相同的问题,正在过滤一个值,并且我不知道它是我的编译器还是存在问题,我在Mageia 4.8.2中使用GCC 4

    2.    阿德里安·阿罗约街

      我真的很喜欢您在该项目中对我的帮助。 我真的被计时器困住了,我也不明白为什么它不起作用。 我做了测试,修改了很多东西,但是没有成功。 目前,我无法编辑代码(我正在度假),但我会尽快对其进行仔细的研究。 我给您一个链接,其中包含有关此问题的信息,这似乎很常见: http://wiki.osdev.org/I_Cant_Get_Interrupts_Working

      关于例外,我想我记得您必须调用ASM中的“ sti”来激活它们,尽管很明显某个地方有问题。

      1.    Carlosorta

        感谢您的回答,是的,的确如此。 中断失败,但这是在堆栈中插入代码和强制转换问题,我将检查链接并进行测试。 如果我解决了问题,我会通知您,否则,我会通知您进度。 节日快乐🙂

      2.    Carlosorta

        没有办法查看汇编代码吗?发生奇怪的事情了,我找不到什么吗?看一下这个屏幕(我把链接放在最后),这很奇怪,因为在IRQ 2功能(键盘)中),它会向堆栈输入值0和0x20(32,因此适合测试),然后先推入(32位GPR寄存器),再依次输入分段寄存器和栈顶,然后调用IRQ处理程序。 我开始看到每个堆栈,显然它们是按顺序排列的,但是如果您可以看到VM的输出,则可以看到它又堆栈了一个元素,我找不到位置,我只知道它是0x10,并且结构混乱。 这是记录结构。

        struct regs {
        uint32_t gs,fs,es,ds; / *持续秒数* /
        uint32_t edi,esi,ebp,esp,ebx,edx,ecx,eax; / *由“ pushal”推动* /
        uint32_t int_no,err_code; /* 错误代码 */
        uint32_t eip,cs,eflags,useresp,ss; / *休息时堆叠* /
        };

        它给了我一个可以在顶部放置一个元素的罐,如果显然所有内容都应该堆叠,我就找不到它的堆叠位置。 你有什么想法吗?

        http://www.subeimagenes.com/img/sin-titulo-1036729.jpg

  7.   Carlosorta

    使键盘中断工作,但不使定时器中断工作; 错误在于它的编译方式,我使用objdump查看了最终对象,结果发现,即使使用“ asm volatile”输入的每个函数也都带有pushl ebp,mov ebp,esp。 然后只需向其添加popl ebp以恢复初始堆栈,它将毫无疑问地传递参数。 在这里,我附上了要检查它的代码,如果您能找出为什么计时器不会打扰我,我很想知道,并在他们谈论多任务的地方附上了链接。 http://www.jamesmolloy.co.uk/tutorial_html/9.-Multitasking.html
    下一个Divel
    https://www.mediafire.com/?kmo83dxyzc7c3cz

    1.    阿德里安·阿罗约街

      我知道了。 这是另一个错误,它影响了Timer PIC的激活,特别是在IRQ重映射中,我必须修改两行。 多亏了我可以在网站上看到一个与当时有所不同的代码,然后单击! 更改遵循以下原则:
      ND ::端口:: OutputB(PIC1 + 1,0xFF);
      ND ::端口:: OutputB(PIC2 + 1,0xFF);

      尽管我不确定,但您必须将0xFF(我说它表示禁用)的值更改为0x00(我说它表示启用),但是它可以工作。 我已经在GitHub上更新了代码。 非常感谢您为我的项目提供了帮助,由于这个问题,我对此有些遗弃。 H

      1.    Carlosorta

        不客气,我期待该主题的下一个更新,您可以依靠我me(Y)

      2.    Carlosorta

        更改键盘字符串捕获例程; 因为释放键时它正在读取,并且它在缓冲区中装载了0,这在读取时产生了问题,最后,'\ n'将其更改为»,以便可以正确打印

  8.   索德·佩雷斯

    您好,我阅读了您的整篇文章,尽管实际上它不超过2篇文章,它确实非常好,可以保存所有内容,但是我真的需要学习c ++和posix来理解它,因为我了解“ c” (我着迷于该编程语言)但是尽管c ++是c OO,但我从未真正从事过它,但请向我阅读google的一些教程,然后返回这个非常有趣的问题:窗口启动是否类似于linux?

    1.    阿德里安·阿罗约街

      从某种意义上说,Windows引导是类似的,因为这是使用x86处理器引导系统的方式,而基于xXNUMX处理器构建的操作系统的影响很小。 我们实际上并没有在启动自己,而是在为我们启动GRUB。 设计用于引导Linux的GRUB可以引导Windows,在这种情况下也可以引导NextDivel。

      1.    大豆

        好的,谢谢,这意味着我想做的是可能的,我已经在研究c ++,并且我已经创建了一些应用程序并将您的系统安装在pendrive上,并且我正在更详细地研究它,这是一篇非常好的文章