操作系统L2实验笔记

分类:Operating System, 发布于:2019-05-05 14:59:21, 更新于:2019-05-09 00:07:33。 评论

要素过多不知道该从哪里写起。

概要

本次实验新增的内容有:

  • os.[ch]:定义handler结构体和中断处理函数;
  • semaphore.[ch]:信号量和相关函数;
  • thread.[ch]:多线程调度、信号量等待。

所有的数据结构全都是链表,基本没有容量问题。

信号量睡眠

为了实现信号量等待时的睡眠,我分别尝试了以下方法:

  • 设置状态为睡眠然后_yield(),会因为此时别的进程已经发出信号但没有收到而死锁。
  • 设置状态为即将睡眠然后_yield(),会在_yield()前一刻被中断(或者被其他处理器抢先处理)导致切换到了即将睡眠的线程然后boom。

最后我成功浪费了半个五一假期来解决这个问题导致的各种奇葩死锁和bug。假期结束后在床上突然领悟到只要用一把大锁保证程序进入sem_wait后一定能够成功睡眠(原子性)即可

这个美妙的用int $0x80进行系统调用来睡眠的方法成功触发了各种奇怪的错误然后继续浪费了我两天。于是我又修改回了原来的、最傻的、simple is best的实现方式,能触发神秘bug的系统调用版信号量存放在L2分支中,提交的分支使用L2-no-syscall分支的代码。

  • 先释放信号量的锁sem->lock(先释放以防止死锁),然后获取os_trap_lock,阻止其他线程此时进入中断;
  • 此时保存sem的地址到task->alarm中(设置闹钟),允许其他进程在进入睡眠前唤醒自己(删除闹钟);
  • 最后,解开os_trap_lock,调用_yield(),此部分过程中如果发生时钟中断不会产生任何影响;
  • _yield()处理过程中首先判断是否已经被唤醒,如果没有唤醒(task->alarm != NULL)则设置状态为睡眠(ST_S = sleeping),然后切换线程。

键盘中断

当CPU关中断时,IO设备的信号仍然能够被接收到,为了进行处理,在中断处理函数中进行了如下的判断:

  • 如果未持有os_trap_lock中断锁,则按照普通中断处理;
  • 如果当前已持有os_trap_lock,首先对中断类型进行判断:不允许中断过程中嵌套_yield和时间中断。判断嵌套的中断类型合法后,依序调用handler,但不会调用无对应事件的的handler(如saveswitch),最后返回中断前的上下文(即在嵌套中断不允许发生进程切换)。

这样就实现了简单的中断嵌套,允许在中断过程中被更高优先级的事件打断,快速处理后返回上一层中断继续处理。

删除task

为了减轻L3工作量我就先把这部分做了……删除一个task的顺序如下:

  • 获得os_trap_lock中断锁,设置自尽变量task->suicide
  • 下一次这个进程停止执行时(进行调度时),从任务列表中毁灭自己。

那么这个过程的正确性就依赖于设置suicide变量后进程仍然会再次被CPU执行。如果进程当前正在运行*running)或者可被切换(waken up),那么下一次中断就会自灭;唯一需要特殊处理的是睡眠进程(sleeping)。因此,我在时钟中断中添加了防死锁机制:每个一定时间唤醒所有的进程,强制让他们继续运行,这样就可以把睡眠的僵尸进程也拉出来灭了。

祖传bug

本次实验总共发现并修复了很多个PA祖传bug:

  • 提供了memmove函数,不使用额外内存。
  • 修正了printf打印-2147483648会因为取反整数溢出,输出10个符号的bug。
  • 修正了memset赋值为负会因为有符号数移位右边补1导致赋值错误的bug。

评论