进程程序替换
用fork()函数创建子进程后执行的是和父进程一样的程序,子进程能够通过一种exec函数来执行另一个程序,当进程调用一种exec函数时,该程序的空间代码和数据完全被替换,从新进程的启动例程开始执行 下面是用程序替换函数来执行ls
#include<stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 printf("begin\n");
6 execl("/usr/bin/ls","ls","-a","-l","-i",NULL);
7 printf("end");
8 return 0;
9 }
程序执行结果如下
里面的第八行没有执行,表明第六行的函数执行之后并未退出
然后我们用子进程来执行这个程序替换程序
#include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/wait.h>
5 int main()
6 {
7 if(fork()==0)
8 {
9 printf("begin\n");
10 execl("/usr/bin/ls","ls","-a","-l","-i",NULL);
11 printf("end");
E> 12 exit(1);
13 }
E> 14 waitpid(-1,NULL,0);
15 printf("wait sucess\n");
16 return 0;
执行结果
替换函数
实际上有六种替换函数
#include <unistd.h>
extern char **environ;
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 char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
一个例子,用一个程序执行myexe程序
char *argv[]={"ls","-a","-l","-i",NULL};
8 if(fork()==0)
9 {
10 printf("begin\n");
11 // execl("/usr/bin/ls","ls","-a","-l","-i",NULL);
12 // execv("/usr/bin/ls",argv);
13 //execlp("ls","ls","-a","-l","-i",NULL)
14 //bin;
15 //execvp("ls",argv);
W> 16 char *env[]={"hhhh","hhhh","hhhh","hhhh",NULL};
17
18 execle("./myexe","myexe",NULL,env);
19 printf("end\n");
20 exit(-1);
21 }
22
23 waitpid(-1,NULL,0);
返回值
函数执行成功之后不会返回,如果调用出错就返回-1
参数说明
l:表示参数采用列表 v:vector,参数用数组存储 p:path,有p自动搜索环境变量 e:env 自己维护环境变量
这六种替换函数真正的系统调用只有execve,其他的都是库函数,最终都要调用execve
这些接口实际上没有太大差异,只有参数的不同。根据不同场景的需要调用不同的函数
为什么要使用程序替换呢,如果使用原有的程序打开要创建进程,使用替换打开就不需要原本的程序创建进程了
使用程序替换函数制作简易的shell
#include<stdio.h>
2 #include<stdlib.h>
3 #include<sys/wait.h>
E> 4 #include<unstd.h>
5 #define NUM 128
6 #define CMD_NUM 64
7 int main()
8 {
9 char command[NUM];
10 char *argv[CMD_NUM];
11 while(1)
12 {
13
14 printf("[who@myhostname mydir]# ");//提示符
15 command[0]=0;//用o(1)的时间复杂度来清空字符
16
17 fflush(stdout);//刷新缓冲区
fgets(command,NUM,stdin);//输入字符
E> 19 command[strlen(command)-1]='\0';//解析字符消除|n的影响
20 const char *sep=" ";
E> 21 argv[0]=strtok(command,sep);
22 int i=1;
E> 23 while(argv[i]=strtok(NULL,sep))
24 {
25 i++;
26 }
if(strcmp(argv[0],"cd")==0)
34 {
35 if(argv[1]!=NULL)
E> 36 chair(argv[1]);
37 continue;
38 }
E> 27 if(fork()==0)
28 {
29
E> 30 execvp(argv[0],argv);
31 exit(1);
32 }
在这个shell中,用户通过输入命令,shell读进命令,shell建立一个新的进程,然后在这个进程中运行命令程序并等待进程结束,然后shell读取新的一行输入,建立一个新的进程重复以上过程
所以要写一个shell,要循环一下过程
- 1.获取命令行
- 2.解析命令行
- 3.建立子进程(fork)
- 4.替换子进程(execvp)
- 5.父进程等待子进程退出(waitpid)
函数与进程的相似性
在我们写的C/C++程序中,我们的函数是一个函数调用另一个函数,同时传递一些参数,用return返回,每一个函数都有他的局部变量,这种通过参数和返回值在拥有私有数据的函数间通信的模式是结构化的基础。Linux鼓励将这种应用与程序之内的模式扩展到程序之间。
一个C程序可以fork/exec另一个程序,并传给它一些参数。这个被调用的程序执行一定的操作,然后通过exit(n)来
返回值。调用它的进程可以通过wait(&ret)来获取exit的返回值。