本章目标
1.进程替换函数
1.进程替换函数
在前面我们已经介绍了进程替换的原理,在这里我们介绍这个进程替换函数

我们先介绍这六个
1.execl
execl他有一个参数,第一个是表示它要替换的函数的路径,后面的是这个函数要怎么样的去执行,说白话就是你在shell上如何去写的,在这里就如何去填写.这里面用了一个可变参数,因为对于一个程序来说,它的选项的个数是不确定的.但是无论这个参数有多些它的最后一个参数永远都是以NULL为结尾的.
c
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
int main()
{
pid_t id = fork();
if(id==0)
{
execl("/usr/bin/ls","ls","-a","-l",NULL);
}
else
{
int stat = 0;
pid_t rid = waitpid(id,&stat,0);
if(rid==id)
{
printf("success\n");
}
}
return 0;
}

结果和我们之前演示的一样
2.execlp
这个函数与上面函数的区别是,我们第一个参数不需要去传路径了,我们只需要传我们要执行的命令是什么,当我们进程程序替换的时候,它回去PATH环境变量里面进行匹配.
二者的区别就是这一个
c
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
int main()
{
pid_t id = fork();
if(id==0)
{
execlp("ls","ls","-a","-l",NULL);
}
else
{
int stat = 0;
pid_t rid = waitpid(id,&stat,0);
if(rid==id)
{
printf("success\n");
}
}
return 0;
}
~

结果是一样的,这里面要注意的问题就是,我们有的时候第二个参数是可以省略的,shell会自己进行补充,但是我们不推荐这种写法,会增加记忆成本不说,对于后面的东西我们也不好解释
3.execle
这个与第一个相比,它是允许我们自定义环境变量的,我们下面演示下
c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
int main()
{
const char* env[]=
{
"PATH=/home/mutou/code/linux-c/exectest",
NULL
};
pid_t id =fork();
if(id==0)
{
execle("/home/mutou/code/linux-c/exectest/tmp/test1","test1",NULL,env);
exit(1);
}
else
{
int stat = 0;
pid_t rid = waitpid(id,&stat,0);
if(id==rid)
{
printf("exit code->%d\n",WEXITSTATUS(stat));
}
}
return 0;
}
c
#include<stdio.h>
int main(int argc,char* argv[],char* env[])
{
int i = 0;
for(;env[i];i++)
printf("%s\n",env[i]);
return 0;
}
我们在另一个文件中接受这个环境变量.然后打印

成功替换
4.execv
这个与第一个的区别是,它可以传一个参数表进去,我们可以在调用前就可以实现这个东西该如何调用.剩下的与第一个使用方法没有区别.我们带l就是可变参数,v可以把它理解成为向量表.
我们下面写一个例子演示下
c
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
char* argv[] =
{
"ls",
"-a",
"-l",
NULL
};
pid_t id = fork();
if(id==0)
{
execv("/usr/bin/ls",argv);
exit(1);
}
else
{
int stat = 0;
pid_t rid = waitpid(id,&stat,0);
if(rid==id)
{
printf("exit code->%d\n",WEXITSTATUS(stat));
}
}
return 0;
}

5.execvp
这个带p都是从环境变量当中找,我们不需要指定路径
和上面的execlp是一样的.其他的用法与execv是一致的.
c
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
char* argv[] =
{
"ls",
"-a",
"-l",
NULL
};
pid_t id = fork();
if(id==0)
{
execvp(argv[0],argv);
exit(1);
}
else
{
int stat = 0;
pid_t rid = waitpid(id,&stat,0);
if(rid==id)
{
printf("exit code->%d\n",WEXITSTATUS(stat));
}
}
return 0;
}
我们在这里不省略的参数的好处是向这种直接传一个数组的,带环境变量我们可以直接用命令行参数表的第一个元素传进去.
6.execvpe
显而易见这个是用向量表同时可以从环境变量当中找路径
我们现在演示两个版本
第一个传父进程的环境变量去找
第二个覆盖原来的环境变量

该一下环境变量,不会的可以参考我往期的博客.
c
{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/mutou/code/linux-c/exectest/tmp",
NULL
};
extern char** environ;
pid_t id = fork();
if(id==0)
{
execvpe((const char*)argv[0],argv,environ);
exit(1);
}
else
{
int stat = 0;
pid_t rid = waitpid(id,&stat,0);
if(rid==id)
{
printf("exit code -> %d\n",WEXITSTATUS(stat));
}
}
return 0;
}

我们下面修改一下改成我们自己的环境变量
c
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
char* argv[]=
{
"test1"
,"-a"
,"-l"
,NULL
};
char* env[]=
{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/mutou/code/linux-c/exectest/tmp",
NULL
};
extern char** environ;
pid_t id = fork();
if(id==0)
{
execvpe((const char*)argv[0],argv,env);
exit(1);
}
else
{
int stat = 0;
pid_t rid = waitpid(id,&stat,0);
if(rid==id)
{
printf("exit code -> %d\n",WEXITSTATUS(stat));
}
}
return 0;
}

同样可以跑
7.exec系列命名规则和参数解释
1.exec系列的函数,有很多
他们会按照以下规则组合
1.l表示使用参数列表
2.v表示使用数组
3.e表示需要自行组装环境变量
4.p表示会自己去寻找路径,从环境变量当中匹配
l和v是对立的,二者只会出现一个
exec系列的函数成功是没有返回值的
失败会将错误码设置为-1
这6个函数实际上都是库函数
他们调用都是execve这个系统调用,因为程序替换会有很多应用场景,所以库函数对其封装了很多个版本的,对于不需要自行传环境变量的版本,默认都是用的environ这个环境变量

8.exec调用其他解释性语言的程序或者shell脚本
python解释器或者是shell,我们都是他们的程序也要变成进程去执行,那么我们的exec函数自然可以进行调用我们下面用python和shell脚本分别实验
c
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
char* argv[]=
{
"bash",
"test.sh",
NULL
};
pid_t id = fork();
if(id==0)
{
execvp(argv[0],argv);
exit(1);
}
else
{
int stat = 0;
pid_t rid = waitpid(id,&stat,0);
if(rid==id)
{
printf("exit code is %d\n",WEXITSTATUS(stat));
}
}
return 0;
}
c
#!/bin/bash
echo "aaaaaaaaaaaa"

c
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
char* argv[]=
{
"python3",
"/home/mutou/code/linux-c/exectest/tmp/test.py",
NULL
};
pid_t id = fork();
if(id==0)
{
execvp(argv[0],argv);
exit(1);
}
else
{
int stat = 0;
pid_t rid = waitpid(id,&stat,0);
if(rid==id)
{
printf("exit code is %d\n",WEXITSTATUS(stat));
}
}
return 0;
}
python
#!/bin/python3
print("aaaaaaa")

成功运行,要注意python的解释器用不了bash的环境变量,要指定具体路径,PATH这个环境变量是只针对可执行文件的.我们这里面的是脚本文件,bash找不到文件会从PATH中找但是python3用不了.