【内存管理】实现Fork(上)

    虽然文件系统这部分还有许多可以添加的,比如添加个查找文件或者给文件改个名字等功能,但这些已经是手到擒来的事情,等到之后有空再说。现在进入到了文件管理部分了,先模仿linux的fork系统调用,来实现自己的Fork,这也是实现自己的SHELL的第一步。

    一个进程需要一个PCB结构,一个GDT中的描述符来描述此进程的LDT,以及代码,数据和堆栈所占用的内存。所需要的内存我们只能在fork的时候才能分配,在这里先把静态的部分先搞定。记得在Init_PCB函数中我们只初始化了编译前就确定的进程的PCB,在这里要把整个系统最多支持的PCB先初始化一遍。

    首先先在PCB结构定义中追加两个字段,以方便管理:

    include/proc.h

Code:
  1. int available;      /* 此PCB是否可用 */  
  2. int parent_pid;     /* 此PCB所属进程的父进程的pid */  

    再修改Init_PCB函数:

    kernel/proc.c

Code:
  1. /*----------------------------------------------------------------------Init_PCB  
  2.     初始化与PCB的相关东东  
  3. */    
  4. void Init_PCB()   
  5. {   
  6.         /* 当前进程个数赋值 */  
  7.         d_Cur_Proc_Num = 9;   
  8.        
  9.         PCB *p_Cur_PCB = PCB_Table;                         /* 指向第一个PCB */  
  10.         Proc_Unique *p_Cur_Proc_Unique = Proc_Unique_Table; /* 指向第一个Proc_Unique */  
  11.         u32 Proc_Stack_Top = (u32)All_Proc_Stack_Space;     /* 定位各个进程的栈顶 */  
  12.         u32 Selector_First_LDT_Index = SELECTOR_FIRST_LDT;  /* 第一个进程的LDT在GDT的选择子 */  
  13.            
  14.         /* 填充各个进程的PCB */  
  15.         int rpl;   
  16.         int dpl;   
  17.         int eflags;   
  18.            
  19.         int i;   
  20.         for(i = 0;i < MAX_PROC;i++)   
  21.         {   
  22.             /*  
  23.                 如果是系统进程,RPL为1,LDT中的DPL为1,标志寄存器为0x1202  
  24.                 如果是用户进程,RPL为3,LDT中的DPL为3,标志寄存器为0x3202  
  25.                 以上除去GS,GS始终指向GDT中的视频段,RPL为3  
  26.                 ss,ds,es,fs指向当前进程的LDT的FLAT_RW段  
  27.                 gs指向GDT中的视频段,RPL为1  
  28.                 cs指向当前进程的LDT的FLAT_C段  
  29.             */  
  30.             if(Is_System_Proc[i] == 1)   
  31.             {   
  32.                 rpl = SA_RPL1;   
  33.                 dpl = DA_DPL1;   
  34.                 eflags = 0x1202;   
  35.             }   
  36.             else  
  37.             {   
  38.                 rpl = SA_RPL3;   
  39.                 dpl = DA_DPL3;   
  40.                 eflags = 0x3202;   
  41.             }   
  42.                
  43.             if(i < d_Cur_Proc_Num)   
  44.             {   
  45.                 p_Cur_PCB->available = 0;   /* 此PCB不可用 */  
  46.                    
  47.                 /* 填充各段寄存器 */  
  48.                 p_Cur_PCB->stack_frame.fs = SELECTOR_LDT_FLAT_RW + rpl + SA_TIL;   
  49.                 p_Cur_PCB->stack_frame.gs = SELECTOR_VIDEO + SA_RPL3;   
  50.                 p_Cur_PCB->stack_frame.es = SELECTOR_LDT_FLAT_RW + rpl + SA_TIL;   
  51.                 p_Cur_PCB->stack_frame.ds = SELECTOR_LDT_FLAT_RW + rpl + SA_TIL;   
  52.                 p_Cur_PCB->stack_frame.ss = SELECTOR_LDT_FLAT_RW + rpl + SA_TIL;   
  53.                 p_Cur_PCB->stack_frame.cs = SELECTOR_LDT_FLAT_C + rpl + SA_TIL;   
  54.                    
  55.                 Str_Cpy(p_Cur_PCB->proc_name,p_Cur_Proc_Unique->proc_name); /* 进程名拷贝 */  
  56.                    
  57.                 /* esp指进程栈的栈顶 */  
  58.                 p_Cur_PCB->stack_frame.esp = Proc_Stack_Top + p_Cur_Proc_Unique->proc_stack_size;   
  59.                 /* 为下一次赋值做准备 */  
  60.                 Proc_Stack_Top += p_Cur_Proc_Unique->proc_stack_size;   
  61.                    
  62.                 /* eip指向进程体 */  
  63.                 p_Cur_PCB->stack_frame.eip = (u32)p_Cur_Proc_Unique->proc_exec_addr;   
  64.                 /* eflags赋值 */  
  65.                 p_Cur_PCB->stack_frame.eflags = eflags;   
  66.                    
  67.                 /*    
  68.                     复制GDT的FLAT_RW和FLAT_C段描述符到当前进程的LDT中  
  69.                     并把LDT的这两个描述符的DPL设为1  
  70.                 */  
  71.                 Memory_Copy(&p_Cur_PCB->LDT[0],&GDT[1],sizeof(Descriptor));   
  72.                 p_Cur_PCB->LDT[0].attr1 |= dpl;   
  73.                 Memory_Copy(&p_Cur_PCB->LDT[1],&GDT[2],sizeof(Descriptor));   
  74.                 p_Cur_PCB->LDT[1].attr1 |= dpl;   
  75.   
  76.                 /* 指向下一个进程的Proc_Unique */  
  77.                 p_Cur_Proc_Unique++;   
  78.             }   
  79.             else  
  80.             {   
  81.                 p_Cur_PCB->available = 1;   /* 此PCB可用 */  
  82.                 PCB_Table[i].ticks = PCB_Table[i].priority = 0; /* 两者为0不能调度 */  
  83.             }   
  84.                
  85.             p_Cur_PCB->proc_id = i;     /* 进程号赋值 */  
  86.                
  87.             /* 当前进程的LDT在GDT的选择子赋值 */  
  88.             p_Cur_PCB->LDT_Selector = Selector_First_LDT_Index;   
  89.                
  90.             /* 填充此进程的LDT在GDT中的描述符 */  
  91.             Fill_Desc(Selector_First_LDT_Index / 8,(u32)p_Cur_PCB->LDT,sizeof(Descriptor) * 2 - 1,DA_LDT);   
  92.                
  93.             /* IPC有关的字段赋值 */  
  94.             p_Cur_PCB->ipc_status = NO_BLOCK;   
  95.             p_Cur_PCB->send_to = NO_PROC;   
  96.             p_Cur_PCB->receive_from = NO_PROC;   
  97.             p_Cur_PCB->has_int_msg = 0;   
  98.             p_Cur_PCB->p_message = 0;   
  99.             p_Cur_PCB->sending_queue_first = 0;   
  100.             p_Cur_PCB->sending_queue_next = 0;   
  101.                
  102.             /* FD的指针数组初始化为0 */  
  103.             int j;   
  104.             for(j = 0;j < MAX_FILE_PER_PROC;j++)   
  105.             {   
  106.                 p_Cur_PCB->fd_ptr_table[j] = 0;   
  107.             }   
  108.                
  109.             p_Cur_PCB->parent_pid = -1;     /* 没有父进程 */  
  110.                
  111.             /* 为下一次赋值做准备 */  
  112.             Selector_First_LDT_Index += 8;   
  113.             /* 指向下一个进程的PCB */  
  114.             p_Cur_PCB++;   
  115.         }   
  116.            
  117.         /* 所有进程的ticks和priority的初始化 */  
  118.         PCB_Table[0].ticks = PCB_Table[0].priority = 30;   
  119.         PCB_Table[1].ticks = PCB_Table[1].priority = 25;   
  120.         PCB_Table[2].ticks = PCB_Table[2].priority = 20;   
  121.         PCB_Table[3].ticks = PCB_Table[3].priority = 300;   
  122.         PCB_Table[4].ticks = PCB_Table[4].priority = 300;   
  123.         PCB_Table[5].ticks = PCB_Table[5].priority = 300;   
  124.         PCB_Table[6].ticks = PCB_Table[6].priority = 300;   
  125.         PCB_Table[7].ticks = PCB_Table[7].priority = 300;   
  126.         PCB_Table[8].ticks = PCB_Table[8].priority = 300;   
  127.            
  128.         /* 设定各用户进程所绑定的TTY */  
  129.         PCB_Table[0].bind_tty = 0;   
  130.         PCB_Table[1].bind_tty = 0;   
  131.         PCB_Table[2].bind_tty = 2;   
  132.         PCB_Table[3].bind_tty = 0;   
  133.         PCB_Table[4].bind_tty = 0;   
  134.         PCB_Table[5].bind_tty = 0;   
  135.         PCB_Table[6].bind_tty = 0;   
  136.         PCB_Table[7].bind_tty = 0;   
  137.         PCB_Table[8].bind_tty = 0;   
  138.            
  139.         /* 初值赋0 */  
  140.         d_Flag_Reenter = 0;    
  141.            
  142.         /* 给p_Next_PCB赋值,指向一个进程的PCB */  
  143.         p_Next_PCB = PCB_Table + 0;   
  144. }   

    以后就根据available来确定空闲的PCB,来分配个各子进程。

    OK,下一步就是动态的添加一个子进程的工作了。先添加一个文件kernel/mm.c,来存放管理内存的代码。

    首先是用户接口函数:

    kernel/mm.c

Code:
  1. /*--------------------------------------------------------------------------Fork   
  2.     Fork函数的用户接口  
  3.     调用失败返回-1,不生成子进程  
  4.     成功对父进程返回子进程号,对子进程返回0  
  5. */  
  6. int Fork()   
  7. {   
  8.     Message fork_msg;   
  9.     fork_msg.msg_type = MM_FORK;   
  10.     Send_Receive_Shell(BOTH,PROC_MM_PID,&fork_msg);   
  11.        
  12.     return fork_msg.r1;   
  13. }  

    我们又专门建立了一个进程专门处理内存管理的消息,执行体如下:

    kernel/mm.c

Code:
  1. /*-----------------------------------------------------------------------Proc_MM   
  2.     内存管理进程执行体  
  3. */  
  4. void Proc_MM()   
  5. {   
  6.     Init_MM();   
  7.        
  8.     Message mm_msg;   
  9.        
  10.     while(1)   
  11.     {   
  12.         Send_Receive_Shell(RECEIVE,ANY,&mm_msg);   
  13.            
  14.         switch(mm_msg.msg_type)   
  15.         {   
  16.             case MM_FORK:   
  17.                 mm_msg.r1 = Do_Fork(&mm_msg);   
  18.                 break;   
  19.                    
  20.             default:   
  21.                 Panic("UNKNOWN MSG TYPE!/n");   
  22.                 break;   
  23.         }   
  24.            
  25.         Send_Receive_Shell(SEND,mm_msg.src_proc_pid,&mm_msg);   
  26.     }   
  27. }  

    相关宏定义略过,添加一个进程的工作也不用赘述。

    我们看到此进程在执行前先执行了一个初始化函数Init_MM,来看看:

    kernel/mm.c

Code:
  1. /*-----------------------------------------------------------------------Init_MM  
  2.     初始化MM  
  3. */  
  4. static void Init_MM()   
  5. {   
  6.     memory_size = *(u32*)0x500; /* 取得内存大小 */  
  7.     free_memory_size = memory_size - CHILD_START_ADDR; /* 剩余内存大小 */  
  8.     block_memory_size = 1024 * 1024;    /* 每个子进程的大小 */  
  9.        
  10.     Printf("Memory Size:%dMB/n",memory_size / (1024 * 1024));   
  11. }  

    先从内存的0x500处得到了内存大小,我们知道在loader执行时打印出来了内存信息,我们就在loader进入内核前把内存大小存入了内存0x500处中,再这里就取了出来,干嘛要得到内存大小咧,肯定是为了管理内存咯。。先修改loader:

    boot/loader.asm

Code:
  1. ;把内存字节数送入内存500h处,供内核读取   
  2. mov eax,[d_Memory_Size]   
  3. mov dword [500h],eax   
  4.   
  5. ;********************************************************************   
  6. jmp Selector_Flat_C:Kernel_Entry_Point_Phy_Addr ;****正式进入内核****   
  7. ;********************************************************************  

    下一条语句是取得可用内存的剩余大小,我们要从内存的CHILD_START_ADDR开始存放子进程的内存,也就是从10MB开始,再下一条是确定一个子进程占用的内存大小,我们确定为1M,虽然有些浪费,但为了简便,也先只能如此了。最后打印内存大小。

    用到的宏如下:

    include/proc.h

Code:
  1. #define CHILD_START_ADDR    10 * 1024 * 1024    /* 第一个子进程的起始地址 */  

    下一步就是关键的Do_Fork函数了:

    kernel/mm.c

Code:
  1. /*-----------------------------------------------------------------------Do_Fork  
  2.     Fork函数功能函数  
  3. */  
  4. static int Do_Fork(Message *fork_msg)   
  5. {   
  6.     int parent_pid = fork_msg->src_proc_pid;    /* 父进程的进程号 */  
  7.        
  8.     /* 找一个可用的PCB,child_pid为进程号 */  
  9.     int child_pid;   
  10.     for(child_pid = 0;child_pid < MAX_PROC;child_pid++)   
  11.     {   
  12.         if(PCB_Table[child_pid].available == 1)   
  13.         {   
  14.             break;   
  15.         }   
  16.     }   
  17.        
  18.     /* 检测内存是否足够 */  
  19.     if(free_memory_size < block_memory_size)   
  20.     {   
  21.         return -1;  /* 不够返回-1 */  
  22.     }   
  23.     else  
  24.     {   
  25.         free_memory_size -= block_memory_size;  /* 空闲内存减去一个块大小 */  
  26.     }   
  27.        
  28.     Disable_Int();  /* 关中断,保持原子性 */  
  29.     u16 ldt_sel = PCB_Table[child_pid].LDT_Selector;    /* 把正确的LDT选择子拿出 */  
  30.     /* 把父进程的PCB复制到子进程 */  
  31.     Memory_Copy(&PCB_Table[child_pid],&PCB_Table[parent_pid],sizeof(PCB));   
  32.     PCB_Table[child_pid].LDT_Selector = ldt_sel;    /* LDT选择子赋回 */  
  33.     PCB_Table[child_pid].parent_pid = parent_pid;   /* 设置父进程号 */  
  34.     Is_System_Proc[child_pid] = Is_System_Proc[parent_pid]; /* 设置为父进程的值 */  
  35.     Sprintf(PCB_Table[child_pid].proc_name,"child:%d",child_pid);   /* 设置子进程名 */  
  36.        
  37.     u32 copy_src;   
  38.     u32 copy_dest;   
  39.     Descriptor *s,*dd,*dt;   
  40.     u32 seg_limit;   
  41.        
  42.     /* 如果是从非子进程fork子进程,则复制0-1M的内存 */  
  43.     if(parent_pid < KERNEL_PROC_NUM)   
  44.     {   
  45.         copy_src = 0;   
  46.     }   
  47.     /* 否则从作为父进程的子进程的LDT的数据段起始处开始复制 */  
  48.     else  
  49.     {   
  50.         s = &(PCB_Table[parent_pid].LDT[SELECTOR_LDT_FLAT_RW]);   
  51.         copy_src = (s->base_high << 24) | (s->base_mid << 16) | s->base_low;   
  52.     }   
  53.     /* 定位fork出的新的子进程占据的内存首地址 */  
  54.     copy_dest = CHILD_START_ADDR + (child_pid - KERNEL_PROC_NUM) * block_memory_size;   
  55.        
  56.     /* 设置新的子进程的LDT的2个描述符的首地址和段界限 */  
  57.     dd = &PCB_Table[child_pid].LDT[SELECTOR_LDT_FLAT_RW >> 8];   
  58.     dd->base_low = copy_dest & 0xffff;   
  59.     dd->base_mid = (copy_dest >> 16) & 0xff;   
  60.     dd->base_high = (copy_dest >> 24) & 0xff;   
  61.     seg_limit = (dd->limit_high_attr2 & DA_LIMIT_4K) ?    
  62.         (block_memory_size / 4096 - 1) : block_memory_size - 1;   
  63.     dd->limit_low = seg_limit & 0xffff;   
  64.     dd->limit_high_attr2 &= 0xf0;   
  65.     dd->limit_high_attr2 |= ((seg_limit >> 16) & 0xf);   
  66.        
  67.     dt = &PCB_Table[child_pid].LDT[SELECTOR_LDT_FLAT_C >> 8];   
  68.     dt->base_low = copy_dest & 0xffff;   
  69.     dt->base_mid = (copy_dest >> 16) & 0xff;   
  70.     dt->base_high = (copy_dest >> 24) & 0xff;   
  71.     seg_limit = (dt->limit_high_attr2 & DA_LIMIT_4K) ?    
  72.         (block_memory_size / 4096 - 1): block_memory_size - 1;   
  73.     dt->limit_low = seg_limit & 0xffff;   
  74.     dt->limit_high_attr2 &= 0xf0;   
  75.     dt->limit_high_attr2 |= ((seg_limit >> 16) & 0xf);   
  76.        
  77.     /* 复制1M内存到新的子进程分配好的地址处 */  
  78.     Memory_Copy((void*)copy_dest,(void*)copy_src,block_memory_size);   
  79.     Enable_Int();   /* 开中断,此时新的子进程有机会被调度 */  
  80.        
  81.     d_Cur_Proc_Num++;   /* 总进程数自增1 */  
  82.        
  83.     /* 此时如果子进程被调度,就RECEIVE消息阻塞住,发个消息给它,返回0表示它是子进程 */  
  84.     Message child_msg;   
  85.     child_msg.r1 = 0;   
  86.     Send_Receive_Shell(SEND,child_pid,&child_msg);   
  87.        
  88.     Printf("FREE SIZE:%dMB/n",free_memory_size / (1024 * 1024));   
  89.        
  90.     return child_pid;   /* 返回给父进程的子进程号 */  
  91. }  

    用到的宏如下:

    include/proc.h

Code:
  1. #define KERNEL_PROC_NUM     9           /* 在内核中的非子进程的数量 */  

    其中用到了一个新的函数Sprintf:

    lib/lib_kernel_in_c.c

Code:
  1. /*-----------------------------------------------------------------------Sprintf  
  2.     把格式字符串的内容写到缓冲区中  
  3. */  
  4. int Sprintf(char *dest,const char *fmt,...)   
  5. {   
  6.     char buf[128];  /* 解析好的串存放之地 */  
  7.     /* 指向格式字符串的后一个参数 */  
  8.     char *args_begin = (char*)((char*)(&fmt) + 4);   
  9.     /* 解析格式字符串 */  
  10.     int len = V_Printf(buf,fmt,args_begin);   
  11.     Memory_Copy(dest,buf,len);  /* 把解析好的串写到缓冲区中 */  
  12.     return len;     /* 返回串的长度 */  
  13. }  

    这样似乎就大功告成了,起始不然,记得在第7章的时候原书出现了一个va2la的函数,当然也没在意。在抓狂了1天多之后终于发现了这个问题。举个例子,在新fork出的子进程中如果要发个消息给某个进程,所定义的Message是分配在栈上的,那么内存地址肯定在10M开外了,在该子进程访问这个Message时肯定没问题,线性地址 = 虚拟地址 + LDT中数据段的首地址能正确找到它。但要把消息复制给别的进程时,此时控制权是掌控在内核态的,而内核态使用的是0-4G的扁平地址,所以就不能正确的找到要发送的Message了。在使用了消息实现的各功能函数如Milli_Delay时就会发生莫名其妙的错误。调用Printf时情况也一样。要修正这个错误很简单,首先添加一个转换函数:

    lib/lib_kernel_in_c.c

Code:
  1. /*----------------------------------------------------Virtual_Addr_2_Linear_Addr  
  2.     虚拟地址得到线性地址  
  3. */  
  4. void *Virtual_Addr_2_Linear_Addr(u32 proc_id,void *v_addr)   
  5. {      
  6.     /* 取得指定进程号的LDT的数据段描述符 */  
  7.     Descriptor *d = &(PCB_Table[proc_id].LDT[SELECTOR_LDT_FLAT_RW >> 3]);   
  8.     /* 取出基地址 */  
  9.     u32 base = (d->base_high << 24) | (d->base_mid << 16) | (d->base_low);   
  10.     /* 加上虚拟地址得到线性地址 */  
  11.     u32 la = base + (u32)v_addr;   
  12.     return (void*)la;   
  13. }  

    先修改System_Call_Write函数:

    kernel/system_call.c

Code:
  1. /*-------------------------------------------------------------System_Call_Write  
  2.     RINT 0,系统调用1号功能函数  
  3. */  
  4. void System_Call_Write(int unused1,int unused2,const char *buf,PCB *pcb)   
  5. {   
  6.     /* 把调用Printf函数的进程绑定的控制台的指针取出来 */  
  7.     Console *con = &Console_Table[pcb->bind_tty];   
  8.        
  9.     /* 取出解析好的缓冲区的线性地址 */  
  10.     char * linear_addr = Virtual_Addr_2_Linear_Addr(PCB_2_PID(pcb),buf);   
  11.        
  12.     /* 如果是Panic调用的或者是Assert调用的并且是系统进程调用的 */  
  13.     if((*linear_addr == MAGIC_CHAR_PANIC) || (*linear_addr == MAGIC_CHAR_ASSERT && Is_System_Proc[PCB_2_PID(pcb)] == 1))   
  14.     {   
  15.         Disable_Int();                      /* 为了能hlt住系统,必须关掉中断 */  
  16.         char *v = (char*)VIDEO_START_ADDR;  /* 指向显存首地址 */  
  17.         char *m = (char*)linear_addr + 1;   /* 略掉标记字符 */  
  18.            
  19.         /* 不超过显存则继续 */  
  20.         while((u32)v < VIDEO_START_ADDR + VIDEO_MEM_SIZE)   
  21.         {   
  22.             /* 如果串没结束,则打印之 */  
  23.             if(*m != '/0')   
  24.             {   
  25.                 *v++ = *m++;   
  26.                 *v++ = Make_Color(GREEN,BLACK);   
  27.             }   
  28.             else  
  29.             {   
  30.                 /* 结束了的话则把剩下的空间填为空,这样则使每隔10行打印一次 */  
  31.                 while((((u32)v - VIDEO_START_ADDR)  % (ROW_BYTE_NUM * 10)) != 0)   
  32.                 {   
  33.                     *v++ = ' ';   
  34.                     *v++ = Make_Color(WHITE,BLACK);   
  35.                 }   
  36.                 /* m重新指串的第2个字符 */  
  37.                 m = (char*)linear_addr + 1;   
  38.             }   
  39.         }   
  40.            
  41.         __asm__ __volatile__("hlt");        /* 叫停系统 */  
  42.     }   
  43.        
  44.     /* 如果在用户进程调用Assert,则略过标志字符 */  
  45.     if(*linear_addr == MAGIC_CHAR_ASSERT)   
  46.     {   
  47.         linear_addr++;   
  48.     }          
  49.        
  50.     /* 取出解析好的串的每一个字符,交给Out_Char打印 */  
  51.     while(*linear_addr != '/0')    
  52.     {   
  53.         Out_Char(con,*linear_addr++);   
  54.     }   
  55. }  

    很简单,在kernel/ipc.c中的Msg_Send和Msg_Receive函数中复制Message的地方也转换一下,这里也不赘述。

    OK,来看看Fork能不能正常工作,在进程A中先fork一下,再在子进程中Fork以下,生成一个子子进程。在父进程,子进程,子子进程中分别打印P,C,G:

    kernel/proc.c

Code:
  1. /*------------------------------------------------------------------------Proc_A  
  2.     进程A的执行体   
  3. */  
  4. void Proc_A()   
  5. {   
  6.     int pid = Fork();   
  7.     if(pid > 0)   
  8.     {   
  9.         Printf("parent is running!/n");   
  10.         while(1)   
  11.         {   
  12.             Printf("P ");   
  13.             Milli_Delay(200);   
  14.         }   
  15.     }   
  16.     else if(fd == 0)   
  17.     {   
  18.         Printf("child is running!/n");   
  19.         int pid1 = Fork();   
  20.         if(pid1 > 0)   
  21.         {   
  22.             while(1)   
  23.             {   
  24.                 Printf("C ");   
  25.                 Milli_Delay(200);   
  26.             }   
  27.         }   
  28.         else if(pid1 == 0)   
  29.         {   
  30.             Printf("grand child is running!/n");   
  31.             while(1)   
  32.             {   
  33.                 Printf("G ");   
  34.                 Milli_Delay(200);   
  35.             }   
  36.         }   
  37.         else  
  38.         {   
  39.             /* fork fail */   
  40.             while(1);   
  41.         }   
  42.            
  43.     }   
  44.     else  
  45.     {   
  46.         /* fork fail */  
  47.         while(1);   
  48.     }    
  49. }  

    跟上学期做的操作系统实验几乎是一样的吧。。

    make,bochs,运行,结果如下:

    中间的输出受到了FS进程的输入些许干扰,不过还是可以看出来的。

   

    这里还遗留了一个问题,就是读写文件等与文件系统相关的操作时还没转化地址,还有文件共享问题。这个留到下篇。。

你可能感兴趣的:(shell,video,table,System,Descriptor,delay)