zoukankan      html  css  js  c++  java
  • 一个操作系统的实现(2)

    接上文:http://www.cnblogs.com/SuperXJ/archive/2010/12/02/1894639.html

                                                                                                                                         xiongjian

                                                                                                                               2010.12.22  msn:cug@live.cn

    10 下面开始进程,首先引入几个基本原则:

                  1)进程表A用来保存进程A的信息和进程A切换时,保存当前A运行时寄存器信息。定义为: PROCESSproc_table[1024]; 表示系统最多可以有1024个进程,PROCESS里面就保存了本进程运行时的各个寄存器信息(这样可以在进程切换回来的时候返回寄存器信息),该进程对应的LDT描述符(一般指向代码段和数据段),最后还有一个指向GDT中该进程位置的描述符。

    实现一个最简单的进程切换系统,需要下面3个子模块: 

    时钟中断处理程序/进程调度模块/至少1个主进程和2个子进程

    一个进程(ring1)的最小需要的元素为进程执行体,堆栈大小和位置。另外需要GDT中有一个选择符指向一个该进程的LDT。在时钟中断发生时,时钟中断处理函数(ring0)将esp指向将要运行的进程在进程表中的位置。

           当做到这里,系统应该可以定时触发时间中断处理函数,时间中断处理函数会轮流的调用进程A和B了。(这也是个ring0到ring1直接相互切换的过程)

    到此为止,操作系统的启动可以高于段落。

    其他需要注意的是:系统调用 

    系统调用的简单实现就是进程(ring1)将参数例如:num,保存在寄存器中,然后自己产生一个中断,例如int 100h.表示软件发生100H号中断(假设中断处理函数为sys_call)。

    I/O system(注意以下都是微内核形态) 

    IO首先第一个想到的就是键盘,其实键盘驱动非常简单,只要注意以下几个方面即可:

    1 建立键盘中断处理函数

    2 打开8259a的键盘中断

    3 解析中断发生时的数据以确定用户按下的具体按键

    4 键盘有一个缓冲区,用于保存用户按键信息,驱动需要及时的吧这个缓冲区的数据读取出来,如果不是一个完整数据,需要做临时缓存。

    系统有一个固定的显存内存位置,显示的最基本原理就是N个字节里面保存字符和颜色信息,最常见的是的565色。Linux的多终端机制的一般规则为多个终端对应一个屏幕,用户在使用一个屏幕的时候,可以随时切换到其他屏幕而好像是切换了用户一样。实现这样机制的一个进程叫做TTY进程,他作为第一个微内核以为的系统进程而存在,已区别于一般的用户进程。下面就来看看他的大概实现思路:

           Tty_task()
           {

                  Init_keyboard();

                  For(循环3次) init_tty(i);

                  While(1)

                  {

                         //依次遍历每一个tty

                         For(I= 0; i<3 ; i++)

                         {

                                Tty_read(i);//如果当前活跃的tty是我,则从键盘驱动中读取键盘缓存

                                In_process(i,key); //将读出的缓存存放到当前tty的缓存中,另外处理alt+Fn的切换控制台命令(即Fn)

                                Tty_write(i);

                         }

                  }

           }

    微内核有MM和FS,分别为内存管理进程和文件系统进程,下面看看一个微内核是如何实现进程创建的。

    ²       1 用户调用fork函数

    ²       2 fork调用sys_call(MM, FORK);  //表示消息要发给MM进程,消息内容为FORK,内部通过send_recv实现,即先发送在接收消息

    ²       3 send_recv内部使用int触发IDT中的一个中断,假设中断处理函数为s_call(ring0)

    ²       4 s_call内部会将消息从寄存器中取出,并向MM进程发送消息(msg_send() ).

    ²       5  msg_send(sender, dest, msg) //发送者/接收者/要发送的消息体   ××发送消息××

           {

                     检查发送者和接收者不能相等

                         检查是否会发生死锁,例如A->B->A  即A和B在同时相互发送消息,AB进程都会堵死在发送程序处。

                         If(接收者是处理RECEIVE状态)

                         {

                                在线性地址(物理地址)层面将要发送的消息复制到接收者的缓存中(虚拟地址-)线性地址)。因为发送者的消息是存放在物理地址里面的,所以必须要找到消息的物理位置才能将消息复制到接收者进程。

                         }     

                         Else 发送者处于SEND状态,并等待一直到接收者处于RECEIVE为止,通过调用进程调度进程实现。

             }

    ²       6 通过msg_send的调用,在物理地址层面,消息就被复制到了接收进程中(消息可以保持在接收进程的进程表项中)。下面看看接受进程:接收进程调用msg_recv来阻塞的接收消息

    msg_recv(接收者,发送者,接收消息的指针m)

    {

           If(接收者有来自硬件的消息)

           If(接收者队列里有消息) 将接收到的消息复制到m里。

           Else (没有消息) 阻塞

    }

    ²       7 到目前为止,进程可以实现发送和接收消息了。实现的根本原理还是通过物理地址的消息复制。 现在的进程有了以下状态:发送(消息)状态,接收(消息)状态,运行状态,休眠状态。 其中 发送/接收/休眠状态都不会获得CPU运行机会。

    ,同时MM进程会不停的查询,如果有消息,则调用do_fork真正创建进程。

    从这一系列流程可以看出,微内核仅仅给上层发送和接收消息2个接口,而不关心具体实现。具体的实现是他们通过参数传给具体的ring1进程(例如FS,MM……)来实现。

    以上就是微内核的思想,同时也是一个简单同步IPC思想

    未完待续(文件系统+内存管理)

    sylar MAIL: cug@live.cn
  • 相关阅读:
    Apache 流框架 Flink,Spark Streaming,Storm对比分析(1)
    Apache 流框架 Flink,Spark Streaming,Storm对比分析(2)
    spark日志配置及问题排查方式。
    Structure Streaming和spark streaming原生API访问HDFS文件数据对比
    fstream,sstream的学习记录
    控制位数和填充0
    激活windows10(已更新工具)
    7-13 统计工龄(排序)
    Insertion or Heap Sort
    Insert or Merge
  • 原文地址:https://www.cnblogs.com/SuperXJ/p/1913802.html
Copyright © 2011-2022 走看看