Linux进程程序替换及制作简易的shell

文章详细介绍了Linux下用于进程替换的execl系列函数,包括execl,execlp,execv,execvp,execle,以及它们在程序替换过程中的作用和使用方法。同时,通过示例代码展示了如何创建一个简单的命令行Shell,处理用户输入的命令并执行。此外,还讨论了环境变量在execle函数中的使用和管理。
摘要由CSDN通过智能技术生成

创建子进程的目的:

1.想让子进程执行父进程代码的一部分(执行父进程对应的磁盘代码中的一部分)
2.想让子进程执行全新的程序(让子进程想办法,加载磁盘上指定的程序(进程的程序替换),执行新程序的代码和数据)

我们一共有这么多函数来实现进程程序替换,我们一个一个了解
image.png

一、execl

image.png

#include<stdio.h>
#include<unistd.h>
int main()
{
  printf("process is running\n");
  //将程序load到内存
  execl("/usr/bin/ls","ls",NULL);
  printf("process is done\n");
  return 0;
}

image.png
我们也可以带上参数,不管可变参数有多少个,最后一定要以NULL结尾

#include<stdio.h>
#include<unistd.h>
int main()
{
  printf("process is running\n");
  //讲程序load到内存
  execl("/usr/bin/ls","ls","--color=auto","-a","-l",NULL);
  printf("process is done\n");
  return 0;
}

image.png
不过我们发现我们不会执行process is done这句话,这是为什么呢?
image.png
mm_struct是进程的地址空间,程序替换的本质就是将制定的代码和数据加载到指定的位置,覆盖自己的代码和数据,并且进程替换的时候没有创建新的进程
image.png
这个函数在失败的时候返回-1,成功的时候不返回,为什么成功的时候不返回呢?因为后面的代码也被覆盖了,判断也毫无意义
如果我们用fork写会怎么样呢?

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
  pid_t id=fork();
  if(id==0)
  {
    execl("/usr/bin/ls","ls","--color=auto","-a","-l",NULL);
    //如果执行到exit表明一定执行失败
    exit(-1);
  }
  int status=0;
  pid_t ret=waitpid(id,&status,0);
  if(ret>0)
  {
    printf("wait success,exit code:%d\n",(status>>8)&0xFF);
  }
  return 0;
}

image.png
我们发现父进程正常执行,原因在于进程具有独立性一旦有执行流想替换代码或者数据,就会发生写时拷贝

二、execlp

image.png

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
  printf("process is running\n");
  execlp("ls","ls","--color=auto","-a","-l",NULL);
  printf("process is done\n");
  return 0;
}

image.png

三、execv

image.png

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
  printf("process is running\n");
  char * const argv[]={
    "ls",
    "--color=auto",
    "-a",
    "-l",
    NULL
  };
  execv("/usr/bin/ls",argv);
  printf("process is done\n");
  return 0;
}

image.png

四、execvp

image.png

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
  printf("process is running\n");
  char * const argv[]={
    "ls",
    "--color=auto",
    "-a",
    "-l",
    NULL
  };
  execvp("ls",argv);
  printf("process is done\n");
  return 0;
}

image.png
我们可以利用之前的main函数的两个参数来实现一个小命令

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc,char* argv[])
{
  //相当于./test ls -a -l .....
  //argv[1]就是ls指令,然后&argv[1]从ls包括ls往后读取,直到NULL
  execvp(argv[1],&argv[1]);
  return 0;
}

image.png

五、执行自己的程序

我们可以编写一个run.c
Makefile应该这样写,因为Makefile默认只能一个可执行,要避免这种情况,写成如下方式:
image.png

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
  printf("process is running\n");
  execl("./run","run",NULL);
  printf("process is done\n");
  return 0;
}

image.png
因为是程序替换,可以调用任何的后端语言对应的可执行程序

六、execle

image.png

#include<stdio.h>
#include<stdlib.h>
int main()
{
  //获取环境变量
  printf("PATH:%s\n",getenv("PATH"));
  printf("PWD:%s\n",getenv("PWD"));
  printf("MYENV:%s\n",getenv("MYENV"));
  return 0;
}
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
  printf("process is running\n");
  char *const envp[]={
    (char*)"MYENV=1234",
    NULL
  };
  //execle("./run","run",NULL,envp); 自定义环境变量
  extern char** environ;
  execle("./run","run",NULL,environ); //实际上,默认的环境变量你不传,子进程也能获取
  printf("process is done\n");
  return 0;
}

分别执行第一个execle和第二个execle,我们会发现第一次没有系统的环境变量,但是存在MYENV,第二次调用系统环境变量的时候又没有MYEVN了
image.png
image.png
如果我们想同时获取系统和自己的环境变量表,我们就需要使用putenv把我们的环境变量表导入到系统的environ中

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
  printf("process is running\n");
  char *const envp[]={
    (char*)"MYENV=1234",
    NULL
  };
  //execle("./run","run",NULL,envp); 自定义环境变量
  extern char** environ;
  putenv((char*)"MYENV=4321");
  execle("./run","run",NULL,environ); //实际上,默认的环境变量你不传,子进程也能获取
  printf("process is done\n");
  return 0;
}

image.png
我们的main函数也是被exec*系列的函数调用的,因为执行程序之前必须被加载到内存中
image.png

七、总结

image.png

八、制作简易shell

#include<stdio.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#define NUM 1024
char LineCommand[NUM];
#define OPT_NUM 64
char* myargv[OPT_NUM];//指针数组
int lastcode=0;
int lastsign=0;
int main()
{
  while(1)
  {
    char LineCommand[NUM]={0};
    char* myargv[OPT_NUM]={NULL};
    //输出提示符
    printf("用户名@主机 当前路径# ");
    //刷新缓冲区,因为没有\n不会刷新缓冲区
    fflush(stdout);
    //获取用户输入,预留一个\0,所以需要-1
    char* s=fgets(LineCommand,sizeof(LineCommand)-1,stdin);
    assert(s!=NULL);
     保证在release方式发布的时候,因为去掉assert了,所以s就没有被使用,而带来的编译告警, 什么都没做,但是充当一次使用
    (void)s;
    //这里会把用户的\n也输入进去,就会有2个\n
    //清楚最后一个\n
    LineCommand[strlen(LineCommand)-1]='\0';
    //printf("test:%s\n",LineCommand);
    //我们输入的是
    //"ls -a -l" 但是系统想要读取的是"ls" "-a" "-l"
    //字符串切割
    //用空格切割
    myargv[0]=strtok(LineCommand," ");
    int i=1;
    if(myargv[0]!=NULL&&strcmp(myargv[0],"ls")==0)
    {
      myargv[i++]="--color=auto";
    }
    //如果没有子串strtok会返回NULL
    //而我们的exec*系列的函数需要以NULL结束
    while(myargv[i++]=strtok(NULL," "));
    //子进程运行结束以后,继续用的还是父进程,即shell
    if(myargv[0]!=NULL&&strcmp(myargv[0],"cd")==0)
    {
      //我们执行cd ..没有用,原因在于我们改变的是子进程的目录,父进程的目录没有改变
      //这种指令需要我们父进程自己执行,这种指令叫做内置/内建指令
      if(myargv[1]!=NULL)
      {
        chdir(myargv[1]);
        continue;
      }
    }
    if(myargv[0]!=NULL&&myargv[1]!=NULL&&strcmp(myargv[0],"echo")==0)
    {
      if(strcmp(myargv[1],"$?")==0)
      {
        printf("%d,%d\n",lastcode,lastsign);
      }
      else
      {
        printf("%s\n",myargv[1]);
      }
      continue;
    }
    //条件编译
#ifdef DEBUG
    for(int i=0;myargv[i];++i)
    {
      printf("myargv[%d]:%s\n",i,myargv[i]);
    }
#endif
    //执行命令一般给子进程执行
    pid_t id=fork();
    assert(id>=0);
    (void)id;
    if(id==0)
    {
      //child
      printf("process is running\n");
      execvp(myargv[0],myargv);
      printf("process is done\n");
      //只要返回一定失败
      exit(-1);
    }
    int status=0;
    pid_t ret = waitpid(id,&status,0);
    lastcode=(status>>8)&0xff;
    lastsign=status&0x7f;
}
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值