进程控制——进程等待,程序替换

目录

1.进程等待

1.1进程等待必要性

1.2进程等待的方法 

1.3获取子进程status

​2.程序替换

2.1替换原理

2.2替换函数 

2.3execl使用实例:

2.4 minishell实现

 

 


1.进程等待

1.1进程等待必要性

·子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。

·父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对, 或者是否正常退出。

·父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

1.2进程等待的方法 

wait方法 

#include<sys/types.h>

#include<sys/wait.h>

pid_t wait(int* status);

wait是一个阻塞接口,功能是等待当前调用者的任意一个子进程退出(如果已经有退出的直接处理),获取返回值,释放资源

返回值:成功返回被等待进程pid,失败返回-1

status参数:是一个int空间的地址,用于向指定空间中存放子进程的退出返回值

waitpid方法 

pid_t waitpid(pid_t pid,int* status,int options); 

waitpid接口既可以等待任意一个子进程退出,也可以等待指定的子进程退出

pid参数:>0则表示等待指定pid的子进程退出;

                 -1表示等待任意一个子进程的退出

waitpid接口既可以阻塞等待,也可以使用非阻塞等待

options参数:0-表示默认阻塞等待;

                      WNOHANG-设置为非阻塞(当前没有子进程退出则会返回0)

返回值:成功则返回处理的退出子进程的pid,若没有子进程退出则返回0;出错则返回-1

阻塞接口:

         为了完成一个功能发起了一个调用,但是这个调用完成条件不具备,则接口一直等待不返回。

        操作流程简单,但是对资源利用率较低。

非阻塞接口:

        为了完成一个功能发起了一个调用,但是这个调用完成条件不具备,则立即报错返回。

        操作流程稍微复杂一些(通常需要循环操作),但是对资源的利用率较高。

1.3获取子进程status

wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。

如果传递NULL,表示不关心子进程的退出状态信息。

否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。

status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):

通过wait获取的返回值,有多个信息,其中进程的退出码只是其中一部分,并且只用了1个字节保存(如果exit给的退出码过大,就会截断)

coredump翻译过来就是核心转储,当程序异常退出的时候,保存程序运行信息,便于能够事后调试,默认处于关闭状态(隐私&安全,占据磁盘空间)

异常信号值--作用就是保存退出异常退出原因,为0则表示程序是正常退出,非0则表示异常退出

因此 要获取一个进程的退出码,首先得确定这个进程是否是正常退出,如果是,才有意义

代码中关心的问题:

        如何判断进程是否是正常退出:取出status中的低7位--status & 0x7f

        如何从status中取出退出码:取出status中的低16位中的高8位--(status>>8) & 0xff

获取status实例:

 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/wait.h>//wait&waitpid
 
 int main()
{
    pid_t cpid=fork();
    if(cpid < 0)
    {
     perror("fork error");
   }else if(cpid==0)
   {
    printf("i am child process!\n");
    sleep(5);
    exit(99);
   }
   int status,ret;
   while((ret=waitpid(-1,&status,WNOHANG))==0){
    printf("现在子进程还没退出...\n");
     sleep(2);
   }                                                                                                                                                                                    
   if(ret<0){
    perror("fork error");
     return -1;
   }
   if((status & 0x7f)== 0){
      printf("%d 子进程退出了! 返回值是:%d\n",ret,(status>>8)&0xff);
   }else{
      printf("子进程是异常退出的!\n");
   }
   sleep(10000000);
   return 0;
 }

 异常退出实例:

2.程序替换

2.1替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

其实就是加载一个新的程序到内存中,将指定进程的pcb页表映射信息进行修改,让其调度管理新的程序运行。

2.2替换函数 

 其实有六种以exec开头的函数,统称exec函数:

 #include  <unistd.h>

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);   //int execlp("ls","ls","-l",NULL)

int execle(const char *path, const char *arg, ...,char *const env[]);//int execle("/bin/ls","ls","-l",NULL,env);

int execv(const char *path, char *const argv[]);//

int execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const env[]);

返回值:

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。

如果调用出错则返回-1

所以exec函数只有出错的返回值而没有成功的返回值。

2.3execl使用实例:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

  int main(int argc,char *argv[],char *env[])
 {
   printf("本来运行程序的起始~\n");
   execlp("ls","ps",NULL);                                                            
   printf("本来程序结束\n");
 }

原程序在运行到execlp函数时发生了替换,执行了ls程序,原程序不再执行。 

2.4 minishell实现

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>

int main()
 {
  while(1)
   {
    printf("【user@host ~】$ ");
    fflush(stdout);//刷新标准输出缓冲区
                          
    char cmd[1024]={0};
    fgets(cmd,1023,stdin);//获取键盘输入的一行数据
    cmd[strlen(cmd)-1]='\0';//取出行尾的换行字符
                   
    int argc=0;                  
    char *argv[32]={NULL};                    
    char *ptr=cmd;
    argv[argc++]=strtok(cmd," ");//字符串分割函数,以指定字符作为间隔符对字符串进行分割
    while((argv[argc]=strtok(NULL," "))!=NULL)
     {                          
      argc++;
     }                
    if(strcmp(argv[0],"cd")==0)
     {
        chdir(argv[1]); //改变工作路径, 工作路径改到哪里,哪里就是当前所在目录
        continue;    
     }
    pid_t child_pid=fork();
    if(child_pid<0)
    {
        perror("fork error");                                                                                                                                                          
        continue;              
    }else if(child_pid==0){
       //子进程进行程序替换,执行指令程序
       execvp(argv[0],argv);
       perror("execvp error");
       exit(-1);
    }
      wait(NULL);//每一个子进程运行完毕后,才能开始捕捉下一个输入进行操作
  }
    return 0;
 }

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值