目录
一.替换原理
二.替换函数
一共有6中替换函数 ,统称为exec函数。
#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const cha *file,char *const argv[]);
这些函数如果调用成功,则会加载新的程序从启动代码处开始执行。如果调用失败了会返回-1。
要执行一个全新的程序,首先要找到该程序的位置,以及怎么去执行,可以认为这些就是参数。
举例子看一下:
1.execl:第一个参数是执行参数的路径,第二个参数是可变参数列表,表示你如何执行这个程序,并以NULL结尾。
int main()
{
pid_t id=fork();
if(id==0)
{
printf("我是子进程,我的id=%d\n",getpid());
execl("/usr/bin/ls","ls","-a","-l",NULL);
//execl("./T","T",NULL); //也可运行自己写的程序
printf("我已经执行完毕\n");
}
else
{
int status=0;
int ret=waitpid(id,&status,0);
sleep(2);
if(ret>0)
{
printf("退出信号是%d,退出码是%d\n",(status>>8)&0xFF,status&0xFF);
printf("等待成功\n");
}
}
return 0;
}
[LF@ecs-100710 lesson7]$ ./test
我是子进程,我的id=20167
total 80
drwxrwxr-x 3 LF LF 4096 Oct 19 15:04 .
drwxrwxr-x 11 LF LF 4096 Oct 10 19:16 ..
-rwxrwxr-x 1 LF LF 8664 Oct 19 15:03 a.out
-rw-rw-r-- 1 LF LF 65 Oct 19 14:40 makefile
-rwxrwxr-x 1 LF LF 8464 Oct 6 10:25 path
-rw-rw-r-- 1 LF LF 186 Oct 6 10:25 path.c
drwxrwxr-x 2 LF LF 4096 Oct 6 11:14 shell
-rwxrwxr-x 1 LF LF 8352 Oct 6 09:38 T
-rw-rw-r-- 1 LF LF 100 Oct 6 09:38 t.c
-rwxrwxr-x 1 LF LF 8664 Oct 19 15:04 test
-rw-rw-r-- 1 LF LF 520 Oct 19 15:04 test.c
-rw-rw-r-- 1 LF LF 763 Oct 19 15:03 tt.c
退出信号是0,退出码是0
等待成功
2.execv:第一个参数是执行参数的路径,第二个参数是一个指针数组,这些指针指向了那些参数(与上面类似)。
char* argv_[]={"ls","-a","-l",NULL};
execv("/usr/bin/ls",argv_);
execlp(const char *file, const char *arg, ...):第一个参数表示要执行参数的名字,第二个参数是可变参数列表,表示如何执行这个程序,以NULL结尾。
例如:
execlp("ls","ls","-l","-a",NULL);
int execvp(const char *file,char * const argv[]):与上面类似,第二个参数是指针数组。
int execle(const char *path, const char *arg, ...,char *const envp[]):第一个参数是执行程序的路径,第二个参数表示如何执行这个程序,以NULL结尾,第三个参数是环境变量。
例如:先看下传递系统的环境变量:
int main()
{
extern**environ;
pid_t id=fork();
if(id==0)
{
printf("我是子进程\n");
char* const env[]={"PATHMY=hello world",NULL};
execle("./path","path",NULL,environ);
//execle("./path","path",NULL,env);
exit(9);
}
else
{
int status=0;
int ret=waitpid(id,&status,0);
sleep(2);
if(ret>0)
{
printf("退出信号是%d,退出码是%d\n",(status>>8)&0xFF,status&0xFF);
printf("等待成功\n");
}
}
return 0;
}
[LF@ecs-100710 lesson7]$ cat path.c
#include<stdio.h>
#include<stdlib.h>
int main()
{
printf("PATH:%s\n",getenv("PATH"));
printf(".........................\n");
printf("PATHMY:%s\n",getenv("PATHMY"));
return 0;
}
结果:

传递自己写的环境变量:execle("./path","path",NULL,env);环境变量是传递了,但是被覆盖了。

在之前已经学习过了,环境变量是可以被子进程传递下去的,可以用execl函数验证下,
execl("./path","path",NULL);,用这段代码运行,可以得到一样的结果

三.简易shell的制作
shell:一个命令行解释器,当有命令要执行时,shell会创建一个子进程,让子进程执行命令,而shell只需等待其退出即可。
过程:
选用execvp(const char *file,char * const argv[])函数更加方便,传递程序名,一个指针数组。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include<iostream>
using namespace std;
#define size 100
#define UU " "
int main()
{
char arr1[size];//存储程序名字
char* arr2[size];//程序如何执行
while(true)
{
cout<<"[LF@ecs-100710]#:";
fgets(arr1,size,stdin);//会读取\n,\n后面还有一个\0;
arr1[strlen(arr1)-1]='\0';//将\n置为\0
//printf("%s\n",arr1);
arr2[0]=strtok(arr1," ");//若arr1是 "ls -l -a",arr2[0]="ls",arr2[1]="-l",arr2[2]="-a"
int index=1; //arr2[3]=NULL;
if(strcmp(arr2[0],"ls")==0)
{
arr2[index++]=(char*)"--color=auto";//添加颜色
}
while(arr2[index]=strtok(NULL,UU))
{
index++;
}
//进行程序替换
pid_t id=fork();
if(id==0)
{
execvp(arr2[0],arr2);
exit(-1);
}
int status=0;
pid_t ret= waitpid(id,&status,0);
if(ret>0)
{
printf("等待子进程成功: sig: %d, code: %d\n", status&0x7F, (status>>8)&0xFF);
}
}
return 0;
}
结果:

执行cd ..命令或者切换路经的命令时,只是让创建的子进程进行了路径切换,而子进程是运行完毕就结束了,我们希望的是执行cd ..等命令时,是对父进程shell进行操作。由父进程shell执行的命令,叫做内建命令。
chdir:改变当前进程的路径
if(strcmp(arr2[0],"cd")==0&&arr2[1]!=NULL)
{
chdir(arr2[1]);//更改当前进程的路径
continue;
}
结果:

环境变量:
1.环境变量会被子进程继承下去,所以他会有全局属性
2.当我们进行程序替换的时候,当前进程的环境变量非但不会被替换,而且是继承父进程的! !因为环境变量是系统的数据
在shell内部新增自己的环境变量- putenv 接口(自己写的shell里面的环境变量是继承父进程的,在自己写的shell里面增加环境变量,不会影响父进程中的环境变量)
if(strcmp(arr2[0],"export")==0&&arr2[1]!=NULL)
{
putenv(arr2[1]);
continue;
}
演示:先创建一个程序打印不存在的环境变量
[LF@ecs-100710 lesson2]$ cat test.c
#include<stdio.h>
#include<stdlib.h>
extern char** environ;
int main()
{
printf("%s\n",getenv("AABB"));
return 0;
}
结果:

后面运行自己的shell,在自己的shell里面增加环境变量AABB,并运行test程序打印该环境变量。
结果:

出错了,并且在自己的shell里面查看环境变量AABB也不存在。
原因:因为把环境变量放入到 putenv(arr2[1]);,而arr2的内容每次都会重新赋值,所以要将新导入的环境放入一个固定的地方。
例如:
void putline(char* p)
{
putenv(p);
}
char arr3[size];//保存环境变量
if(strcmp(arr2[0],"export")==0&&arr2[1]!=NULL)
{
strcpy(arr3,arr2[1]);
putline(arr3);//将环境变量导入这个进程中。父进程创建的子进程会继承父进程的环境变量
continue;
}
结果:

完整代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include<iostream>
using namespace std;
#define size 100
#define UU " "
void putline(char* p)
{
putenv(p);
}
int main()
{
char arr1[size];//存储程序名字
char* arr2[size];//程序如何执行
char arr3[size];//保存环境变量
while(true)
{
cout<<"[LF@ecs-100710]#:";
fgets(arr1,size,stdin);//会读取\n,\n后面有一个\0;
arr1[strlen(arr1)-1]='\0';
//printf("%s\n",arr1);
arr2[0]=strtok(arr1," ");
int index=1;
if(strcmp(arr2[0],"ls")==0)
{
arr2[index++]=(char*)"--color=auto";
}
while(arr2[index]=strtok(NULL,UU))
{
index++;
}
//进行程序替换
if(strcmp(arr2[0],"cd")==0&&arr2[1]!=NULL)
{
chdir(arr2[1]);//更改当前进程的路径
continue;
}
if(strcmp(arr2[0],"export")==0&&arr2[1]!=NULL)
{
strcpy(arr3,arr2[1]);
putline(arr3);//将环境变量导入这个进程中。父进程创建的子进程会继承父进程的环境变量
continue;
}
pid_t id=fork();
if(id==0)
{
execvp(arr2[0],arr2);
exit(-1);
}
int status=0;
pid_t ret= waitpid(id,&status,0);
if(ret>0)
{
printf("等待子进程成功: sig: %d, code: %d\n", status&0x7F, (status>>8)&0xFF);
}
}
return 0;
}
本文详细介绍了Linux系统中的进程替换原理,包括各种exec函数的使用,如execl、execv、execlp等。此外,还展示了简易Shell的制作过程,探讨了如何利用execvp函数执行命令以及如何处理环境变量。在Shell中,通过chdir函数实现路径切换,而putenv接口用于在Shell内部添加新的环境变量。

1493

被折叠的 条评论
为什么被折叠?



