【Linux】程序替换接口

本文介绍了程序替换的概念,包括execve、execvp、execv、execl、execlp、execle等函数的使用,并通过示例详细解释了它们的区别。此外,还展示了如何模拟实现一个简单的shell,通过接收用户输入,解析命令并使用exec函数进行程序替换来执行系统指令。最后,文章提供了几个关于进程创建和输出问题的思考题。
摘要由CSDN通过智能技术生成

程序替换

main 函数回顾说明

main(int argc,char* argv[],char* env[])
argc:程序运行参数的个数
argv:程序运行参数,字符指针数组,最后一个参数之后,以NULL结尾
env:程序的环境变量,字符指针数组,最后一个参数之后,以NULL结尾
在这里插入图片描述

程序替换

execve 指定运行参数argv

int execve(char* path,char* argv[],char* env[]);
功能: 将 path 这个路径名所指定的程序加载到内存中,然后让当前进程调度管理这个程序的运行
argv:用于设定这个程序的运行参数
env:用于设定这个程序的环境遍历
返回值:替换成功运行新程序 没有返回值;失败返回-1

使用示例:

// execve.c 程序

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

int main()
{
  printf("这是我程序运行的起始位置~\n");

  char* argv[]={"-a","-d","-c","xzz",NULL};   //argv 字符函数必须以 NULL 结尾
  char* env[]={"MYVAL=999","CLASS=98",NULL};
  
//进行程序替换,argc 程序替换运行当前程序
  execve("./argc",argv,env);
  //execve("/bin/ls",argv,env);    //指定系统调用接口路径,指定运行参数和环境变量

   return 0;
}

其中所调用的 argc 程序:

//argc.c 程序

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

int main(int argc,char* argv[],char* env[])
{
  int i=0;
  for(;argv[i]!=NULL;++i)   //将运行参数打印
    printf("argv[%d]=[%s]\n",i,argv[i]);

  printf("~~~~~~~~~~~~~~~~~~\n");

  i=0;
  for(;env[i]!=NULL;++i)   //打印环境变量
    printf("env[%d]=[%s]\n",i,env[i]);

  return 0;
}

运行结果:
在这里插入图片描述

execvp 不需要指定环境变量

int execvp(char* file,char* argc[],cahr* env);
第一个参数不用指定路径,默认会在 PATH环境变量指定的路径下找
环境变量默认使用当前已有的,不用手动设置环境变量

使用示例:

int main()
{
  printf("这是我程序运行的起始位置~\n");

  //char* argv[]={"-a","-d","-c","xzz",NULL};   //argv 字符函数必须以 NULL 结尾
  //char* env[]={"MYVAL=999","CLASS=98",NULL};
 // execve("./argc",argv,env);

  
  char *argv[]={"ls","-l",NULL};
  char* env[]={NULL};
  
  //使用运行参数 argv 中 ls -l 替换当前程序
  execvp("ls",argv);   
  //env 默认采用已有的环境变量,且不需要指定路径

//execvp("./argc",argv);
  
 // 等价于 execve("ls",argc,env-main中第三个参数)

  printf("这是我程序运行的终止~~~\n");


  return 0;
}

在这里插入图片描述

execv

int execv(char* path,char* argv[]);
不用设置环境变量,但是需要路径

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

int main()
{
  printf("这是我程序运行的起始位置~\n");
  
  char *argv[]={"ls","-l",NULL};

  execv("ls",argv);   //不指定路径时会发生错误
  perror("execv error\n");

  printf("这是我程序运行的终止~~~\n");
  return 0;
}

当没有指定 ls 的位置时,会发生替换失败:

在这里插入图片描述

当指定路径之后:

execv("/bin/ls",argv);

在这里插入图片描述

execv(“bin/ls”,argv); 需要指定路径
execvp(“ls”,argv); 不需要指定路径
execve(“bin/ls”,argv,env); 手动设置环境变量,并且需要指定路径

execl 、execlp 、execle

上述三种替换每次都需要定义一个参数字符指针数组,比较麻烦,因此常会这三种替换函数:

int execl(char* path,…/char arg*/);
int execlp(char* file,…);
int execle(char* path,…,char* env[]);

使用示例:

execlp("ls","ls","-l",NULL)        //可以不指定环境变量
execle("bin/ls","ls","-l",NULL,env)      //需要指定环境变量以及系统调用接口的路径

程序替换的作用

shell 是一个命令行解释器,捕捉用户的输入信息,了解用户想要干什么,然后执行对应的shell指令--------系统内核与用户之间沟通的桥梁

模拟实现 shell :

(1)父进程捕捉用户输入 [ ls -l -a ];
(2)解析输入,得到命令名称,各个参数 [ls] [-l] [-a];
(3)创建子进程;
(4)对子进程进行程序替换,将子进程要进行调度的程序换成要执行的 shell 指令程序;
(5)父进程等待子进程退出(等待指令执行完毕----避免僵尸进程),然后捕捉用户的下一个输入。

    1 #include<stdio.h>                                                         
    2 #include<stdlib.h>
    3 #include<string.h>
    4 #include<unistd.h>
    5 #include<sys/wait.h>
    6 
    7 int main()
    8 {
    9   while(1){
   10     printf("【user@host~ 】$ ");
   11     fflush(stdout);    //刷新缓冲区
   12 
   13     char cmd[32]={0};
   14     fgets(cmd,1023,stdin) ;      //获取字符
   15 
   16     cmd[strlen(cmd)-1]='\0';   //将键盘输入的字符后加上 \0 表示结束
   17 
   18     printf("[%s]\n",cmd);   //打印一下输入的字符信息
   19 
   20     int argc=0;
   21     char* argv[32]={NULL};    //用于保存分割信息
W> 22     char* ptr=cmd;   //保存获取到的字符信息
   23     argv[argc++]=strtok(cmd," ");        //以空格对字符串进行分割处理
   24 
   25 
   26     while((argv[argc]=strtok(NULL," "))!=NULL){  //strtok 第一个参数 NULL       ,表示默认以上一次终止位置作为新分割的起始位置进行分割   
   27       
   28       argc++;
   29     }
   30 
   31 //打印分割后的字符信息
   32     int i=0;
   33     for(;argv[i]!=NULL;++i){
   34       printf("[%s]\n",argv[i]);
   35     }
   36 //如果第一个字符信息为 cd ,则需要更改工作路径
   37     if(strcmp(argv[0],"cd")==0){
   38       chdir(argc[1]);      //更改当前工作路径
   39       continue;
   40     }
   41 
   42 
   43     //创建子进程
   44     pid_t cpid=fork();
   45     if(cpid<0){
   46       perror("fork error!");
   47       continue;
   48     }
   49     else if(cpid==0){
   50       //子进程实现程序替换
   51       execvp(argv[0],argv);                                               
   52       perror("execvp perror");
   53       exit(-1);  //只有程序替换失败才会执行这行代码
   54     }
   55     wait(NULL); //每一个子进程运行完毕后,才能开始捕捉下一个输入进行操作
   56   }
   57 
   58   return 0;
   59 }
   60 



运行结果:

进入模拟 shell 界面:

在这里插入图片描述

模拟实现 ls:

在这里插入图片描述

模拟实现 cd(返回上一层目录):

在这里插入图片描述

小点点

wc -l :统计个数(行数统计)
du :查看磁盘空间
pstree :查看目录的树形结构

在这里插入图片描述


Q1:不算main函数进程,一一共创建了多少个进程?

在这里插入图片描述

Q2:请问打印了多少个 * ?

int main()
{
	for(int i=0;i<2;++i){
		fork();
		printf("*\n");
	}
	return 0;
}

答案:6个

存在 \n 说明会刷新缓冲区,因此创建子进程时不会复制父进程内容(*)

在这里插入图片描述

Q3:请问打印了多少个 * ?

int main()
{
	for(int i=0;i<2;++i){
		fork();
		printf("*");
	}
	return 0;
}

答案:8个

不存在 \n 说明不会刷新缓冲区,因此 * 不会打印,暂时存储在缓冲区里,因此创建子进程会同时复制父进程缓冲区中内容(*)

在这里插入图片描述

ps:欢迎评论交流~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值