rCore_Tutorial_CP6&7&8
因为有点摆了所以就变成了n章合起来写了,也没记什么具体的东西,第九章抽象的要死,感觉没有学到啥也没记,就这样结束了8
CP6
第六章是文件系统,并没有提到怎么写驱动。。。然后简要的说了下superblock,inode,多重索引之类的操作系统课上学过的东西,代码实现也有点过于复杂,本着开摆的精神,不予记录
CP7
第七章提到了IO重定向与管道,没做过底层开发真不知道管道这么写的。。。
IO重定向与管道
fork系统调用在创建子进程时,同时也将父进程的所有fd给子进程复制了一遍(这里实现的时候是把父进程的fd都push了一遍,数据结构是vector,被关闭的fd会变成none,none也会被推进子进程的fd列表里),子进程是共享父进程的fd的,所以shell拉起来的新任务能够接受用户输入和输出在屏幕上,很合理
首先明确一个一切皆文件的思想,pipe实际上就是将一个缓冲区当成一个文件提供给两个进程,两方各持一个只读/写的fd,从而实现进程间通信
为此pipe只实现打开两个fd指向同一个缓冲区,而实际使用则是父进程拿到调用pipe拿到fd之后调用fork和子进程共享fd,然后父子进程分别把读/写端关掉进行通信。
而对shell实现输入输出重定向,实际上只是在拉起子进程的时候,将子进程的fd 0/1/2close掉,再吧需要重定向的文件dup一下重新分配到对应的fd号上
对于shell管道符的实现也是通过pipe之后分别启动两个子进程,第一个子进程把写端dup到fd 1上,第二个子进程把读端dup到fd 0上,可以看这个回答的例子
pipe example
signal实现
最后简单的介绍了一下信号的实现,信号是在程序从内核态返回用户态时进行处理的,使用系统调用发送信号,或是产生错误以及硬件中断产生信号时,会将未处理的信号注册到对应的进程上。
当在trap处理完毕返回用户态前,对信号进行检查,如果存在信号则进行处理,部分信号只能由内核处理,剩下的信号可以用用户自定义的handler返回用户态处理。当一个进程由于调度恢复到CPU继续执行时,也是从内核态返回,同样会进行sign处理
signaction系统调用注册信号处理函数,而kill实际上是发送信号。像Ctrl C还有segment fault这类信号都能被用户handler,吊啊。不过Ctrl C发的是SIGINT,应该是interrupt,可以被用户捕获,真正的kill是SIGKILL,就是经典的kill -9
,这个是不能被捕获的
印象里dirsearch就是捕获了Ctrl C来让用户二次确认是否退出,也可以尝试直接忽略Ctrl C写流氓软件?
CP8
说起来这章内容应该在操作系统课上学过,好像当初重点讲的就是同步和互斥。。。。可惜记不太清了已经
用户线程与内核线程
进程实现,进程是可以分为用户态实现和内核实现的。用户态实现需要用户自行对线程进行维护,也就直接在进程的空间里面维护上各个进程的状态并将初始启动的进程作为0进程。但用户态实现的进程存在一个缺点,即用户态进程的实现对内核是透明的,内核无法感知到用户线程的存在,也就导致内核仍然以进程为调度单位,线程不会被时钟中断打断,只能通过主动交出控制权的方式让出CPU。并且如果一个线程阻塞,那么操作系统可能就认为整个进程阻塞,就把CPU让给别的进程了,也完全没有体现多线程的并发性。
内核线程调度的实现,就是通过将进程作为内存分配的单位,而线程作为CPU调度的单位进行(操作系统的记忆开始复苏),调度方面和进程调度别无二致,只是进程中不再保留运行相关的数据,所有的运行情况都通过在进程中新开一个线程控制块进行记录。
关于资源的控制,在线程退出时,主线程通过waittid对其他线程的资源进行释放,这一步是释放了其他线程占用的进程的虚拟空间,而一旦主线程退出,则直接销毁其他线程以及整个进程,释放了进程占用的物理空间
锁
锁的实现方面,有用户实现的锁,比较简陋,也有硬件实现的锁,需要硬件提供原子操作,比较生猛的是允许用户态关中断,但是这样子就会出现一个用户态关中断永久占用CPU为所欲为的情况
说到底条件竞争是由于操作系统对线程进行了抢占式调度,只需要将获取锁的操作放在内核中进行,内核不会被抢占,就能保证互斥了。但是感觉上只能实现单核CPU上的互斥,如果存在多核,多个CPU上的内核对锁进行竞争是不是又会出问题呢?
看到有说法说多核情况下就用原子操作实现自旋锁完成内核内部对锁的访问,如果多核情况下两个盒同时执行同一个原子操作又会怎么样呢?(好像也有专门的硬件实现,比较原始的一种是直接控制总线,只允许单独CPU访问内存,多核退化到单核)
信号量
信号量是锁的强化实现,当线程进入临界区时信号量–,出临界区是信号量++,信号量大于0时线程可以进入临界区,同样需要保证对信号量的操作是原子的(但是怎么保证?感觉应该可以直接原子操作)
信号量初值为1即为互斥锁,并且可以设置初值为0,然后使用生产者对信号量++消费者对信号量–,实现一个进程间的顺序执行,也就是同步
管程与条件变量
这个是用来解决线程间同步问题的,而不是互斥与临界区。(信号量和条件变量感觉差不多。。。都是用来解决同步的)不过条件变量要配合锁使用
使用情形为线程对共享值的操作需要等待条件时,需要先进入临界区,再等待条件,而这会由于线程占用锁并等待条件,造成死锁。条件变量即为在线程等待条件时将锁释放,并将线程挂起到等待队列,当条件满足时通过条件变量将挂起的线程恢复,并上锁,确保其对共享变量的操作在临界区内
但说实话,内核中的各项实现仍然有被竞争的可能,对锁进行操作时会出现一个递归的对锁的互斥锁操作,感觉实际上应该还要保证这些锁操作的函数在执行的时候也上一个简单的原子操作自旋锁来保证绝对互斥?但是会不会出现获取lock失败时把自己挂起又占用锁进而导致的死锁呢?