【Linux】进程控制之进程程序替换

目录

前言

替换的原理

替换函数

记忆技巧

函数使用

execl

execlp

execv

execvp 

execle

execvpe 

调用其它语言的程序

模拟实现一个shell


前言

关于本文可以先去看看上一篇【Linux】进程控制详解-CSDN博客可以更好的理解这里的内容

学完本篇文章,你就可以自己设计一个mini版的shell解释器,还可以用你写自己的代码区执行其它语言的程序。

替换的原理

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

替换函数

返回值:如果调用成功则加载新的程序从启动代码开始执行,不在返回;调用失败则返回-1。

记忆技巧

  • l(list):表示参数采用列表
  • v(vector):表示参数采用数组
  • p(path):第一个参数path不用输入路径,给出命令名即可,它会在环境变量PATH当中搜索对应的命令
  • e(env):将自己维护的环境变量传递给需要替换的进程
函数名参数格式是否带路径是否使用当前环境变量
execl列表
execlp列表
execle列表否,需自己维护环境变量
execv数组
execvp数组
execvpe数组否,需自己维护环境变量

函数使用

一旦发生了替换,那么替换函数后面的代码就不会再执行了。

int main()
{
  printf("当前进程的开始代码\n");
  execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
  printf("当前进程的结束代码\n");
  return 0:
}

 

注:在调用替换函数时末尾最好加上NULL代表结束。

虽然我们可以不用创建子进程来使用替换函数,但是我们创建了子进程,替换的进程就是子进程而父进程不受影响,那么父进程就可以聚焦在读取数据,解析数据,指派进程执行代码等功能了。 

execl

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

int main()
{
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

 

execlp

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

int main()
{
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    execlp("ls", "ls", "-a", "-l", NULL);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

execv

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

#define NUM 16

int main()
{
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    char* const _argv[NUM] = {
      (char*)"ls",
      (char*)"-a",
      (char*)"-l",
      NULL 
    };
    execv("/usr/bin/ls", _argv);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

execvp 

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

#define NUM 16

int main()
{
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    //execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
    //execlp("ls", "ls", "-a", "-l", NULL);
    char* const _argv[NUM] = {
      (char*)"ls",
      (char*)"-a",
      (char*)"-l",
      NULL 
    };
    execvp("ls", _argv);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

execle

 mycmd.c

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

int main(int argc, char* argv[])
{
  if(argc != 2)
  {
    printf("can not execute\n");
    exit(1);
  }

  printf("获取环境变量:MY_VALUE:%s\n", getenv("MA_VALUE"));
  if(strcmp(argv[1], "-a") == 0)
  {
    printf("hello 我是a\n");
  }
  else if(strcmp(argv[1], "-b") == 0)
  {
    printf("hello 我是b\n");
  }
  else 
  {
    printf("defalut\n");
  }
  return 0;
}

myproc.c 

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

#define NUM 16
const char* myfile = "/home/hjx/for_linuxtest/test43/mycmd";

int main(int argc, char* argv[], char* env[])
{

  char* const _env[NUM] = {
    (char*)"MY_VALUE=332335454",
    NULL 
  };
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    execle(myfile, "mycmd", "-a", NULL, _env);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

 

所以mycmd.c就拿到了这里的环境变量

execvpe 

和上面的类似就不再演示了

 其实系统调用的接口只有一个——execve

而以上介绍的函数是操作系统是为了满足不同的调用场景提供的基本封装。

调用其它语言的程序

调用Python语言 

print("hello python")
print("hello python")
print("hello python")
print("hello python")
print("hello python")
print("hello python")
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    execlp("python", "python", "test.py", NULL);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

模拟实现一个shell

有了上面的这些知识,那么我们可以自己设计一个简易版的shell。

shell运行原理:父进程解析命令,替换子进程并执行命令,父进程等待子进程退出

我们需要循环一下过程

  1. 打印提示信息
  2. 获取用户输入的命令和选项
  3. 对命令和选项进行解析
  4. 创建子进程(fork)
  5. 替换子进程(execvp)
  6. 父进程等待子进程退出(waitpid)

shell代码链接minishell

效果展示


今天的分享就到这里了,如果内容有错或不足的话,还望指出,谢谢!!!

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值