> 文章列表 > 那些突然想到的问题---操作系统的中断程序到底长什么样

那些突然想到的问题---操作系统的中断程序到底长什么样

那些突然想到的问题---操作系统的中断程序到底长什么样

Linux 0.11是一个非常早期的Linux内核版本,一些程序简单,所以以此为例

Linux0.11的中断程序是由汇编语言编写的,主要包括以下几个部分:

1. 中断处理函数:当硬件设备向CPU发送中断请求时,中断处理函数会被调用。它会保存当前CPU的状态并处理中断请求,包括读取中断向量表、确定中断类型、执行相应的中断处理程序等。

Linux0.11的中断处理函数代码如下:

```assembly
/* 中断处理函数 */
void do_IRQ(int irq, int error_code, struct pt_regs *regs)
{unsigned char x;// 根据IRQ号读取中断类型x = inb(0x21);// IRQ号小于8,直接处理if (irq < 8) {outb(x & ~(1 << irq), 0x21);}// IRQ号在8~15之间,需要先处理从8259A-2传递过来的IRQ2else if (irq != 2) {outb(x & ~(1 << (irq - 8)), 0xA1);outb(x & ~(1 << 2), 0x21);}// 处理具体的中断类型if (irq_table[irq].handler) {irq_table[irq].handler(irq, irq_table[irq].dev_id, regs);}// 处理完毕后发送EOI信号if (irq >= 8) {outb(0x20, 0xA0);}outb(0x20, 0x20);
}
```该函数的主要功能是处理中断请求并调用相应的中断处理程序。
它首先根据IRQ号读取中断类型,然后根据中断类型进行相应的处理。
如果IRQ号小于8,则直接处理,否则需要先处理从8259A-2传递过来的IRQ2。
处理完毕后,发送EOI信号以通知8259A中断控制器,该中断请求已经被处理完毕,
可以接收下一个中断请求。

2. 中断向量表:中断向量表是一个数组,包含了所有中断类型对应的中断处理程序的入口地址。当中断处理函数被调用时,它会根据中断号从中断向量表中读取对应的中断处理程序的入口地址,并跳转到该地址执行中断处理程序。

Linux0.11的中断向量表是由汇编语言编写的,代码如下:


```assembly
/* 中断向量表 */
.align 4
set_intr_gate(0,&divide_error);
set_intr_gate(1,&debug);
set_intr_gate(2,&nmi);
set_intr_gate(3,&int3);
set_intr_gate(4,&overflow);
set_intr_gate(5,&bounds);
set_intr_gate(6,&invalid_op);
set_intr_gate(7,&device_not_available);set_intr_gate(8,&double_fault);
set_intr_gate(9,&coprocessor_segment_overrun);
set_intr_gate(10,&invalid_TSS);
set_intr_gate(11,&segment_not_present);
set_intr_gate(12,&stack_segment);
set_intr_gate(13,&general_protection);
set_intr_gate(14,&page_fault);
set_intr_gate(15,&reserved);set_intr_gate(16,&coprocessor_error);
set_intr_gate(17,&alignment_check);
set_intr_gate(18,&machine_check);
set_intr_gate(19,&simd_coprocessor_error);/* 20~31保留 */
.align 4
set_intr_gate(32,&irq0);
set_intr_gate(33,&irq1);
set_intr_gate(34,&irq2);
set_intr_gate(35,&irq3);
set_intr_gate(36,&irq4);
set_intr_gate(37,&irq5);
set_intr_gate(38,&irq6);
set_intr_gate(39,&irq7);
set_intr_gate(40,&irq8);
set_intr_gate(41,&irq9);
set_intr_gate(42,&irq10);
set_intr_gate(43,&irq11);
set_intr_gate(44,&irq12);
set_intr_gate(45,&irq13);
set_intr_gate(46,&irq14);
set_intr_gate(47,&irq15);
```该代码定义了一个中断向量表,包含了所有中断类型对应的中断处理程序的入口地址。
每个中断类型都通过set_intr_gate宏定义了一个中断门,
其中第一个参数是中断号,第二个参数是中断处理程序的入口地址。
通过这种方式,将中断号与中断处理程序的入口地址对应起来,
方便中断处理函数调用对应的中断处理程序。

3. 中断处理程序:中断处理程序是由C语言编写的,用于处理特定的中断类型。它会根据具体的中断类型执行相应的操作,如读取硬件设备数据、发送数据等。

而Linux0.11中断服务程序的源码如下所示:

```c
void do_timer(long cpl)
{/* 更新系统时间 */if (current->counter > 0)current->counter--;if (counter > 0)counter--;if (buffered_disk < 0)current->utime++;elsecurrent->stime++;/* 进行进程调度 */if (!cpl) {/* 禁止中断 */cli();schedule();/* 恢复中断 */sti();}
}void do_keyboard(long cpl)
{unsigned char code;/* 从键盘缓冲区中读取按键码 */code = inb(0x60);/* 处理按键事件 */if (code & 0x80) {code &= 0x7f;key_stat[code] = 0;} else {key_stat[code] = 1;if (code == 0x1c)wake_up(&wait_for_char);}
}
```

其中,`do_timer()`函数用于处理时钟中断,它会更新系统时间并进行进程调度。如果当前进程的时间片已经用完,`schedule()`函数会进行进程调度,选择一个新的进程执行。

`do_keyboard()`函数用于处理键盘中断,它会从键盘缓冲区中读取按键码,并根据按键码来处理按键事件。如果按下了回车键(对应的按键码是0x1c),则会唤醒等待该按键的进程。

4. 中断控制器:中断控制器是硬件设备中的一个模块,用于管理和控制中断请求。它会将中断请求发送给CPU,并根据CPU的响应进行相应的操作,如向设备发送数据、接收数据等。

Linux0.11中使用的是8259A中断控制器,其代码如下:

```c
/* 初始化8259A中断控制器 */
void init_IRQ(void)
{int i;// 初始化主8259Aoutb_p(0x11, 0x20); // ICW1:边沿触发,级联8259A,需要ICW4outb_p(0x20, 0x21); // ICW2:IRQ0~7映射到中断向量表的20~27outb_p(0x04, 0x21); // ICW3:连接从8259Aoutb_p(0x01, 0x21); // ICW4:8086模式,正常EOI// 初始化从8259Aoutb_p(0x11, 0xA0); // ICW1:边沿触发,级联8259A,需要ICW4outb_p(0x28, 0xA1); // ICW2:IRQ8~15映射到中断向量表的28~2Foutb_p(0x02, 0xA1); // ICW3:连接到主8259A的IRQ2outb_p(0x01, 0xA1); // ICW4:8086模式,正常EOI// 打开所有中断outb(0x0, 0x21);outb(0x0, 0xA1);// 初始化中断处理程序for (i = 0; i < NR_IRQS; i++) {irq_table[i].handler = NULL;}
}
```该函数的主要功能是初始化8259A中断控制器,
并打开所有中断。在初始化过程中,
需要设置ICW1~ICW4寄存器,以配置中断控制器的工作模式和中断向量表。
其中,主8259A和从8259A需要分别进行初始化。
初始化完成后,打开所有中断,并将中断处理程序初始化为空。

Linux0.11的中断程序是由汇编语言和C语言编写的,通过上面步骤协同工作,实现了对硬件设备中断请求的处理和管理。

需要注意的是,这些代码是非常早期的Linux内核版本,其代码结构和实现方式可能与现代内核有所不同。此外,该版本内核的代码非常简单,只支持单CPU、单任务,并且没有多线程、虚拟内存等现代操作系统的特性