摘要
本章主要是说一下进程替代用到的函数exec以及自己实现的简易shell
目录
摘要
一、进程程序替换
1、替换原理
2 、替换函数
3、函数解释与命令理解
4、代码演示
1、execl
2、execv
3、execlp
4、execvp
二、简易的myshell
三、代码
myshell
exec
一、进程程序替换
1、替换原理
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
也就是说可以利用一个函数进行控制另外一个函数,就像可以利用c语言写一个程序从而控制c++的程序,c++的写法也不需要我会,只需要给一个参数的传递,c++那边用自己的语法进行去编写就可以利用c语言进行控制了。
2 、替换函数
其实有六种以exec开头的函数,统称exec函数,这里是直接利用man指令进行查看,这里我是使用man 3 exec进行查看,但是刚开始我没有找到,然后发现是因为没有安装库函数包,下面这行代码是进行安装的。
sudo yum install man-pages
3、函数解释与命令理解
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。
命名理解如下:
l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量
4、代码演示
1、execl
如上方函数的execl的使用如下方代码所示就是,个人上方手册查到的函数用法,execl就是需要绝对路径去进行访问使用,也就是环境变量,然后后面是指令使用,这里是利用ls指令进行演示的,
1 #include <stdio.h> 2 #include <stdlib.h>3 #include <unistd.h>4 #include <sys/wait.h>5 6 int main()7 {8 pid_t id=fork();9 if(id==0)10 { //子进程11 printf("子进程开始运行,pid:%d\n",getpid());12 sleep(1);13 execl("/usr/bin/ls","ls","-a","-l",NULL);14 exit(1);15 }16 else17 { //父进程18 printf("父进程开始运行,pid:%d\n",getpid());19 int status=0;20 pid_t id=waitpid(-1,&status,0);//阻塞等待,等到子进程死去,得到他的错误码21 if(id>0)22 {23 printf("wait success,exit code:%d\n",WEXITSTATUS(status));24 }25 }26 return 0;27 }
~
2、execv
这个函数后面是可以利用数组进行访问这个这个命令的指令,也就是可以用数组进行匹配使用。
8 int main()9 {10 pid_t id=fork();11 if(id==0)12 { //子进程13 printf("子进程开始运行,pid:%d\n",getpid());14 sleep(1);15 char *const _argv[NUM]= 16 {17 (char*)"-ls",18 (char*)"-a",19 (char*)"-l",20 NULL21 };22 // execl("/usr/bin/ls","ls","-a","-l",NULL);23 execv("/usr/bin/ls",_argv);24 exit(1);25 }26 else27 { //父进程28 printf("父进程开始运行,pid:%d\n",getpid());29 int status=0; 30 pid_t id=waitpid(-1,&status,0);//阻塞等待,等到子进程死去,得到他的错误码31 if(id>0) 32 { 33 printf("wait success,exit code:%d\n",WEXITSTATUS(status));34 } 35 } 36 return 0; 37 }
~
3、execlp
下方这块代码和execl使用方式差不多,但是有一点不同,就是环境变量的寻找这个不需要绝对路径。
int main()9 {10 pid_t id=fork();11 if(id==0)12 { //子进程13 printf("子进程开始运行,pid:%d\n",getpid());14 sleep(1);
W> 15 char *const _argv[NUM]=16 {17 (char*)"-ls",18 (char*)"-a",19 (char*)"-l",20 NULL21 };22 //execl("/usr/bin/ls","ls","-a","-l",NULL);23 execlp("ls","ls","-a","-l",NULL);24 //execv("/usr/bin/ls",_argv); 25 exit(1);26 }27 else28 { //父进程29 printf("父进程开始运行,pid:%d\n",getpid()); 30 int status=0; 31 pid_t id=waitpid(-1,&status,0);//阻塞等待,等到子进程死去,得到他的错误码32 if(id>0) 33 { 34 printf("wait success,exit code:%d\n",WEXITSTATUS(status));35 } 36 } 37 return 0; 38 }
4、execvp
这里和上面execl差不多,但是也只需要相对路径就可以了,这里就演示了四个,后面两个用法就是按照手册进行更改就可以了。
8 int main()9 {10 pid_t id=fork();11 if(id==0)12 { //子进程13 printf("子进程开始运行,pid:%d\n",getpid());14 sleep(1);15 char *const _argv[NUM]=16 {17 (char*)"-ls",18 (char*)"-a",19 (char*)"-l",20 NULL21 };22 //execl("/usr/bin/ls","ls","-a","-l",NULL);23 //execlp("ls","ls","-a","-l",NULL);24 //execv("/usr/bin/ls",_argv);25 execvp("ls",_argv); 26 exit(1);27 }28 else 29 { //父进程 30 printf("父进程开始运行,pid:%d\n",getpid()); 31 int status=0; 32 pid_t id=waitpid(-1,&status,0);//阻塞等待,等到子进程死去,得到他的错误码33 if(id>0) 34 { 35 printf("wait success,exit code:%d\n",WEXITSTATUS(status));36 } 37 } 38 return 0;39 }
二、简易的myshell
这里将实现一个简易的shell,shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序 并等待这个进程结束。所以要写一个shell,需要循环以下过程:
1. 获取命令行
2. 解析命令行
3. 建立一个子进程(fork)
4. 替换子进程(execvp)
5. 父进程等待子进程退出(wait)
从而就可以拆分出下面几步
1. 打印出提示信息
2. 获取用户的键盘输入[输入的是各种指令和选项: "ls -a -l -i"]
3. 命令行字符串解析:"ls -a -l -i" -> "ls" "-a" "-i"(这里完全可以利用strtok这个函数进行拆分字符串)
4. TODO,内置命令, 让父进程(shell)自己执行的命令,我们叫做内置命令,内建命令
5. fork()
也就是对应上面的五个过程,代码如下测试如下。
#include <stdio.h>2 #include <stdlib.h>3 #include <unistd.h>4 #include <sys/wait.h>5 #include <string.h>6 #include <sys/types.h>7 8 #define NUM 10249 #define SIZE 3210 #define SEP " "11 12 char cmd_line[NUM];13 char* g_argv[SIZE];14 15 int main()16 {17 while(1)18 { //1、打印出信息提示19 printf("[root@localhost myshell]#");20 fflush(stdout);21 memset(cmd_line,'\0',sizeof cmd_line);22 //2、获取从键盘输入的各种数据23 if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)24 {25 continue;26 }27 cmd_line[strlen(cmd_line)-1]='\0';28 //3、命令字符串进行解析29 g_argv[0]=strtok(cmd_line,SEP);//第一调用,需要首先获取原始字符串30 int index=1;31 if(strcmp(g_argv[0], "ls") == 0)32 {
W> 33 g_argv[index++] = "--color=auto";34 }35 if(strcmp(g_argv[0], "ll") == 0)36 {
W> 37 g_argv[0] = "ls";
W> 38 g_argv[index++] = "-l";
W> 39 g_argv[index++] = "--color=auto";40 }
W> 41 while(g_argv[index++]=strtok(NULL,SEP));42 if(strcmp(g_argv[0], "cd") == 0) //not child execute, father execute 43 {44 if(g_argv[1] != NULL) chdir(g_argv[1]); //cd path, cd ..45 46 continue;47 }48 pid_t id=fork();49 if(id==0)50 {51 printf("下面功能让子进程进程的\n");52 execvp(g_argv[0],g_argv);53 exit(1);54 }55 int status=0;56 pid_t ret=waitpid(id,&status,0);57 if(ret>0)printf("exit code:%d\n",WEXITSTATUS(status));58 59 }60 return 0;61 62 }
三、代码
myshell
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/types.h>#define NUM 1024
#define SIZE 32
#define SEP " "char cmd_line[NUM];
char* g_argv[SIZE];int main()
{while(1){ //1、打印出信息提示printf("[root@localhost myshell]#");fflush(stdout);memset(cmd_line,'\0',sizeof cmd_line);//2、获取从键盘输入的各种数据if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL){continue;}cmd_line[strlen(cmd_line)-1]='\0';//3、命令字符串进行解析g_argv[0]=strtok(cmd_line,SEP);//第一调用,需要首先获取原始字符串int index=1;if(strcmp(g_argv[0], "ls") == 0){g_argv[index++] = "--color=auto";}if(strcmp(g_argv[0], "ll") == 0){g_argv[0] = "ls";g_argv[index++] = "-l";g_argv[index++] = "--color=auto";}while(g_argv[index++]=strtok(NULL,SEP));if(strcmp(g_argv[2], "cd") == 0) //not child execute, father execute{if(g_argv[1] != NULL) chdir(g_argv[1]); //cd path, cd ..continue;}pid_t id=fork();if(id==0){printf("下面功能让子进程进程的\n");execvp(g_argv[0],g_argv);exit(1);}int status=0;pid_t ret=waitpid(id,&status,0);if(ret>0)printf("exit code:%d\n",WEXITSTATUS(status));}return 0;}
exec
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>#define NUM 10int main()
{pid_t id=fork();if(id==0){ //子进程printf("子进程开始运行,pid:%d\n",getpid());sleep(1);char *const _argv[NUM]={(char*)"-ls",(char*)"-a",(char*)"-l",NULL};//execl("/usr/bin/ls","ls","-a","-l",NULL);//execlp("ls","ls","-a","-l",NULL);//execv("/usr/bin/ls",_argv);execvp("ls",_argv);exit(1);}else { //父进程printf("父进程开始运行,pid:%d\n",getpid());int status=0;pid_t id=waitpid(-1,&status,0);//阻塞等待,等到子进程死去,得到他的错误码if(id>0){printf("wait success,exit code:%d\n",WEXITSTATUS(status));}}return 0;
}