Wait和Exit

    光是Fork还不够,我们的子进程将来完成了对应的工作后是要被抹杀掉的,我们比较仁慈,让它自己了结自己,这就是Exit功能,应该叫自杀比较合适,但linux中用的是exit,还是别那么有个性吧。。

    父进程有时需要等待一个子进程执行完毕后,才能开始继续执行,这就是Wait。

    下面来实现这两个功能吧。

    首先修改PCB,添加两个字段:

    include/proc.h

Code:
  1. u32 status;         /* Wait,Exit函数相关的状态 */  
  2. int exit_status;    /* 一个子进程Exit后返回给父进程的退出状态码 */  

    在Init_PCB函数中初始化这两个字段:

    kernel/proc.c

Code:
  1. p_Cur_PCB->status = 0;          /* 即不WAITING也不HANGING */  
  2. p_Cur_PCB->exit_status = -1;    /* 默认-1 */  

    添加status字段描述的两个状态值:

    include/proc.h

Code:
  1. #define WAITING             0x1         /* 等待子进程Exit */   
  2. #define HANGING             0x2         /* 等待父进程Wait */   
  3. #define NO_CHILD_PROC       -1          /* 如果父进程调用Wait时没有子进程,则返回-1 */  

    接着把框架搭好,添加Wait和Exit的用户接口:

    kernel/mm.c

Code:
  1. /*--------------------------------------------------------------------------Wait  
  2.     Wait函数的用户接口  
  3.     成功返回则返回结束的子进程的进程号  
  4.     在container中填充子进程的退出码  
  5. */  
  6. u32 Wait(int *container)   
  7. {   
  8.     Message wait_msg;   
  9.     wait_msg.msg_type = MM_WAIT;   
  10.     Send_Receive_Shell(BOTH,PROC_MM_PID,&wait_msg);   
  11.     *container = wait_msg.r1;   /* container内存放退出码 */  
  12.     return wait_msg.r2;     /* 返回Exit的子进程的进程号 */  
  13. }   
  14.   
  15. /*--------------------------------------------------------------------------Exit  
  16.     Exit函数的用户接口  
  17.     形参为该进程的退出码  
  18. */  
  19. void Exit(int exit_status)   
  20. {   
  21.     Message exit_msg;   
  22.     exit_msg.msg_type = MM_EXIT;   
  23.     exit_msg.i1 = exit_status;  /* i1字段存放退出码 */  
  24.     Send_Receive_Shell(SEND,PROC_MM_PID,&exit_msg); /* 只发不收 */  
  25.     while(1);   /* 死循环,等待父进程或自己抹杀 */  
  26. }  

    再在MM进程中处理这两种消息:

    kernel/mm.c

Code:
  1. /*-----------------------------------------------------------------------Proc_MM   
  2.     内存管理进程执行体  
  3. */  
  4. void Proc_MM()   
  5. {   
  6.     Init_MM();   
  7.        
  8.     Message mm_msg;   
  9.        
  10.     int reply;  /* 是否发回消息 */  
  11.     while(1)   
  12.     {   
  13.         Send_Receive_Shell(RECEIVE,ANY,&mm_msg);   
  14.            
  15.         switch(mm_msg.msg_type)   
  16.         {   
  17.             /* 处理MM_FORK */  
  18.             case MM_FORK:   
  19.                 reply = 1;   
  20.                 mm_msg.r1 = Do_Fork(&mm_msg);   
  21.                 break;   
  22.             /* 处理MM_WAIT */  
  23.             case MM_WAIT:   
  24.                 reply = 0;   
  25.                 Do_Wait(&mm_msg);   
  26.                 break;   
  27.             /* 处理MM_EXIT */  
  28.             case MM_EXIT:   
  29.                 reply = 0;   
  30.                 Do_Exit(&mm_msg);   
  31.                 break;     
  32.             default:   
  33.                 Panic("UNKNOWN MSG TYPE!/n");   
  34.                 break;   
  35.         }   
  36.            
  37.         if(reply)   
  38.         {   
  39.             Send_Receive_Shell(SEND,mm_msg.src_proc_pid,&mm_msg);   
  40.         }   
  41.     }   
  42. }  

    相关宏如下:

    include/const.h

Code:
  1. #define MM_WAIT                     16          /* WAIT消息 */   
  2. #define MM_EXIT                     17          /* EXIT消息 */  

    OK,接下来是具体功能的实现了,先看Wait的实现:

    kernel/mm.c

Code:
  1. /*-----------------------------------------------------------------------Do_Wait  
  2.     处理MM_WAIT消息的功能函数  
  3. */  
  4. static void Do_Wait(Message *wait_msg)   
  5. {   
  6.     u32 i,child_num = 0;   
  7.     int parent_pid = wait_msg->src_proc_pid;    /* 获得要WAIT的进程的PID */  
  8.     /* 遍历所有进程 */  
  9.     for(i = 0;i < MAX_PROC;i++)   
  10.     {   
  11.         /* 如果发现子进程 */  
  12.         if(PCB_Table[i].parent_pid == parent_pid)   
  13.         {   
  14.             child_num++;    /* 子进程数加1 */  
  15.             /* 如果有某个子进程已经在等待父进程WAIT了 */  
  16.             if(PCB_Table[i].status & HANGING)   
  17.             {   
  18.                 /* 抹杀子进程,并返回父进程调用Wait函数后继续执行 */  
  19.                 Clean_Child_Proc(i);   
  20.                 return;   
  21.             }   
  22.         }   
  23.     }   
  24.        
  25.     /* 该父进程有至少一个子进程,但目前为止没有子进程Exit,自己先WAITING,等待子进程Exit */  
  26.     if(child_num > 0)   
  27.     {   
  28.         PCB_Table[parent_pid].status |= WAITING;   
  29.     }   
  30.     /* 该父进程没有子进程,给父进程发送no_child_msg消息 */  
  31.     else  
  32.     {   
  33.         Message no_child_msg;   
  34.         no_child_msg.r1 = -1;   /* 错误码 */  
  35.         no_child_msg.r2 = NO_CHILD_PROC;    /* 子进程号 */  
  36.         Send_Receive_Shell(SEND,parent_pid,&no_child_msg);   
  37.     }   
  38. }  

    流程很简单,看注释即可。

    抹杀子进程的函数如下:

    kernel/mm.c

Code:
  1. /*--------------------------------------------------------------Clean_Child_Proc  
  2.     取走子进程的返回码,连同子进程号发给父进程,并设置子进程PCB可用  
  3. */  
  4. static void Clean_Child_Proc(u32 child_pid)   
  5. {   
  6.     Message wait_msg;   
  7.     wait_msg.r1 = PCB_Table[child_pid].exit_status; /* 退出码 */  
  8.     wait_msg.r2 = child_pid;    /* 子进程PID */  
  9.     Send_Receive_Shell(SEND,PCB_Table[child_pid].parent_pid,&wait_msg);   
  10.        
  11.     PCB_Table[child_pid].parent_pid = -1;   /* 父进程号置-1 */  
  12.     PCB_Table[child_pid].available = 1;     /* 设置子进程PCB可用,并不参与调度了 */  
  13. }  

    还得修改调度函数Schedule,当PCB可用时不允许被调度:

    kernel/proc.c

    接着是Exit的实现了:

    kernel/mm.c

Code:
  1. /*-----------------------------------------------------------------------Do_Exit  
  2.     处理MM_EXIT消息的功能函数  
  3. */  
  4. static void Do_Exit(Message *exit_msg)   
  5. {   
  6.     u32 exit_proc_pid = exit_msg->src_proc_pid; /* 取得调用Exit进程的进程号 */  
  7.     PCB *exit_pcb = &PCB_Table[exit_proc_pid];   
  8.     u32 exit_parent_pid = exit_pcb->parent_pid; /* 得到子进程的父进程号 */  
  9.        
  10.     /* 让FS进程处理退出进程共享文件的清理工作 */  
  11.     Message fs_exit_msg;   
  12.     fs_exit_msg.msg_type = MM_EXIT;   
  13.     fs_exit_msg.i1 = exit_proc_pid;   
  14.     Send_Receive_Shell(BOTH,PROC_FS_PID,&fs_exit_msg);   
  15.        
  16.     exit_pcb->exit_status = exit_msg->i1;       /* 把退出码放到子进程PCB中的相应字段 */  
  17.     free_memory_size += block_memory_size;  /* 更新剩余内存 */  
  18.        
  19.     Printf("FREE SIZE:%dMB/n",free_memory_size / (1024 * 1024));   
  20.        
  21.     /* 如果父进程处于WAITING状态 */  
  22.     if(PCB_Table[exit_parent_pid].status & WAITING)   
  23.     {   
  24.         PCB_Table[exit_parent_pid].status &= ~WAITING;  /* 状态更新 */  
  25.         Clean_Child_Proc(exit_proc_pid);    /* 抹杀子进程 */  
  26.     }   
  27.     /* 反之 */  
  28.     else  
  29.     {   
  30.         exit_pcb->status |= HANGING;    /* 则子进程等待 */  
  31.     }   
  32.        
  33.     /*   
  34.         过继功能,即当子进程有子子进程时,子进程理应先Wait等待子子进程运行结束  
  35.         子进程再Exit。但允许子进程在子子进程还没Exit的情况下Exit掉,这样要把子子  
  36.         进程过继给A进程,也就是LINUX中的INIT进程,让A进程来Wait这些子子进程的Exit  
  37.         这样就要求A进程不断的执行Wait。  
  38.     */  
  39.     /* 遍历所有进程表 */  
  40.     int i;   
  41.     for(i = 0;i < MAX_PROC;i++)   
  42.     {   
  43.         /* 如果发现存在子子进程 */  
  44.         if(PCB_Table[i].parent_pid == exit_proc_pid)   
  45.         {   
  46.             /* 则把子子进程的父进程号设置A进程的进程号 */  
  47.             PCB_Table[i].parent_pid = PROC_A_PID;   
  48.             /* 如果进程A正好在WAITING并且当前子子进程在HANGING,则抹杀掉子子进程 */  
  49.             if((PCB_Table[PROC_A_PID].status & WAITING) && (PCB_Table[i].status & HANGING))   
  50.             {   
  51.                 PCB_Table[PROC_A_PID].status &= ~WAITING;   
  52.                 Clean_Child_Proc(i);   
  53.             }      
  54.         }   
  55.     }   
  56. }  

    流程也很清楚,不赘述。有一点要说明,在函数的开头发了个消息给FS进程,这是清理共享文件的问题。下面来看代码:

    首先在FS进程中接收这个消息:

    kernel/fs.c

Code:
  1. /* 接收MM_EXIT消息,处理共享FD的问题 */  
  2. case MM_EXIT:   
  3.     Do_MM_Exit(&m);   
  4.     break;  

    接着是功能函数:

Code:
  1. /*--------------------------------------------------------------------Do_MM_Exit  
  2.     处理MM_EXIT的功能函数  
  3. */  
  4. static void Do_MM_Exit(Message *m)   
  5. {   
  6.     PCB *p = &PCB_Table[m->i1];     /* 取得子进程的PCB指针 */  
  7.     /* 遍历FD指针数组 */  
  8.     int i;   
  9.     for(i = 0;i < MAX_FILE_PER_PROC;i++)   
  10.     {   
  11.         /* FD指针不为0 */  
  12.         if(p->fd_ptr_table[i] != 0)   
  13.         {   
  14.             p->fd_ptr_table[i]->fd_inode->shared_count--;   /* i结点共享次数减1 */  
  15.             p->fd_ptr_table[i]->fd_count--; /* FD共享此数减1 */  
  16.             /* 如果FD共享次数<=0,则把FD的fd_inode清0,表示可用 */  
  17.             if(p->fd_ptr_table[i]->fd_count <= 0)   
  18.             {   
  19.                 p->fd_ptr_table[i]->fd_inode = 0;   
  20.             }   
  21.             p->fd_ptr_table[i] = 0; /* 相应数组项可用 */  
  22.         }              
  23.     }   
  24. }  

    嗯,可以测试一下了吧:

    进程A在生成子进程B后就不断的Wait,接收到就打印子进程的进程号和退出码;子进程B生成子子进程C后不等进程C自杀,就先自己自杀了;而进程C先Delay一小会再自杀,目的是让进程B比自己先自杀,这样可以测试一下过继功能。

    kernel/proc.c

Code:
  1. /*------------------------------------------------------------------------Proc_A  
  2.     进程A的执行体   
  3. */  
  4. void Proc_A()   
  5. {      
  6.     int pid = Fork();   
  7.     int pid1;   
  8.     int exit_status;   
  9.     int child_pid;   
  10.     if(pid > 0)   
  11.     {   
  12.         Printf("parent is running!/n");   
  13.         child_pid = Wait(&exit_status);   
  14.         Printf("child (%d) exited,exit status is %d/n",child_pid,exit_status);   
  15.   
  16.         while(1)   
  17.         {   
  18.             child_pid = Wait(&exit_status);   
  19.             if(child_pid == -1)   
  20.             {   
  21.                 //no child!   
  22.             }   
  23.             else  
  24.             {   
  25.                 Printf("child (%d) exited,exit status is %d/n",child_pid,exit_status);   
  26.             }   
  27.         }   
  28.     }   
  29.     else if(pid == 0)   
  30.     {   
  31.         Printf("child is running!/n");   
  32.         pid1 = Fork();   
  33.         if(pid1 > 0)   
  34.         {   
  35.             Exit(1234);   
  36.         }   
  37.         else if(pid1 == 0)   
  38.         {   
  39.             Printf("grand is running!/n");   
  40.             Milli_Delay(200);   
  41.             Exit(5678);   
  42.         }        
  43.     }   
  44. }   

    运行结果如下:

    嗯,感觉不错,不过测试还是不够充分,还是那句话,碰到BUG再说吧。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值