码桶

发现社区成员的开源项目

朹方 /

moeos

公开
main
moeos/notes/自制操作系统(30):信号.md
自制操作系统(30):信号.md3.9 KB
## 自制操作系统(30):信号

```
这篇文章并不完整...正在建设中。
```

用户态程序卡住不动我们的操作系统就得重启了,这谁能忍?我们赶紧来给我们的操作系统实现信号吧!

信号其实就是一个标志位,如果中断返回时进程带了这个标志,那就进入这个标志的handler。

#### 只有系统handler的信号实现

```cpp
    uint32_t signal;
```

PCB加一个字段。

```
common_interrupt_handler:
    pusha
    # 保存段寄存器
    pushl %ds
    pushl %es
    pushl %fs
    pushl %gs
    pushl %esp
    
    call inner_interrupt_handler
    addl $4, %esp
    popl %gs
    popl %fs
    popl %es
    popl %ds
    popa
    addl $8, %esp
    iret
```



#### 从轮询模型到中断驱动

我们现在的getline是不断去问键盘驱动有没有字符,没有就pause,我们现在要做出改变,我们把原来的缓冲区移到tty侧,然后用terminal_input去输送字符。

```cpp
void keyboard_interrupt_handler(registers* /* regs */) {
    uint8_t scancode = hal_inb(0x60);
    if (scancode & KEY_RELEASED_MASK) {
        if ((scancode ^ KEY_RELEASED_MASK) == 0x2A || (scancode ^ KEY_RELEASED_MASK) == 0x36) {
            is_shift_pressed = false;
        }
        return;
    }
    if (scancode == 0x2A || scancode == 0x36) {
        is_shift_pressed = true;
        return;
    }
    terminal_input(scancode_to_ascii_table[scancode][is_shift_pressed]);
    return;
}
```

terminal_input会根据我们的输入去走不同分支,比如说,发中断:

```cpp
void terminal_input(char c) {
    if (c == 0x03) {
        send_signal(foreground_pid, SIGNAL::SIGINT);
        terminal_write("^C\n", 3);
        return;
    }

    rb_write(c);

    {
        SpinlockGuard guard(process_list_lock);
        PCB* cur;
        while(cur = tty_wait_queue) {
            remove_from_process_queue(tty_wait_queue, cur->pid);
            cur->state = process_state::READY;
            insert_into_scheduling_queue(cur->pid);
        }
    }
}
```

留意到我加了两个新玩意,一个是foreground_pid,表示正在执行我们的终端的前台pid(键盘中断触发时,调度的可以是任何进程,但是前台的进程只能有一个);第二个是一个等待序列,这个后面会提到。

```cpp

bool is_shift_pressed = false;
bool is_ctrl_pressed = false;

void keyboard_interrupt_handler(registers* /* regs */) {
    uint8_t scancode = hal_inb(0x60);
    if (scancode & KEY_RELEASED_MASK) {
        uint8_t released = scancode ^ KEY_RELEASED_MASK;
        if (released == 0x2A || released == 0x36)
            is_shift_pressed = false;
        if (released == 0x1D)
            is_ctrl_pressed = false;
        return;
    }

    if (scancode == 0x2A || scancode == 0x36) {
        is_shift_pressed = true;
        return;
    }
    if (scancode == 0x1D) {
        is_ctrl_pressed = true;
        return;
    }

    char c = scancode_to_ascii_table[scancode][is_shift_pressed];
    if (c && is_ctrl_pressed) {
        c = c & 0x1F;
    }
    if (c) {
        terminal_input(c);
    }
}
```

我们再在tty加一个检测ctrl键的逻辑(左右ctrl键的第二个字节都是0x1D),按下CTRL表示我们把前六位都清掉,这样我们就可以把字符转为对应的控制字符了。

#### foreground_pid

设置了foreground,让其可以中断真正的进程

#### 问题

我们的信号只有在敲下回车时才有反应,因为我们的信号被输入循环阻塞了,一直卡在内核态,我们的do_signal是在返回用户态时执行的。我们要做两件事:发送信号后,马上把进程放回就绪队列,并且在terminal_read_char判断是否有信号到来,有的话跳出循环:

#### 更多的问题

管道的进程怎么同时释放?

---

下一节来做ProcFS,并借助它和我们的信号来做ps和kill。