码桶
发现社区成员的开源项目
自制操作系统(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。