目录
一共有6中替换函数 ,统称为exec函数。
- #include
` - 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
- #include
- 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只需等待其退出即可。
过程:
选用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
- #include
- 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
- #include
- #include
- #include
- #include
- #include
- #include
- 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;
- }