1.进程的程序替换(exec)
(1).替换原理
- 用
fork
创建子进程后执行的是和父进程相同的程序
,当然可以执行不同的分支
(如果我们用fork创建一个子进程之后让子进程做和父进程同样的事
,那么这个子进程没有任何意义
)。 - 所以在
fork
之后,我们应该调用exec函数
用来替换子进程的程序和数据
,让子进程执行和负进程不同的程序
。当进程调用exec
函数时,该进程的用户空间得到代码和数据完全被新的程序替换
。 - 调用exec并
不创建新的进程
。
(2).六种替换函数
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[]);
这6个函数其实可以分为两类:
- execl:表示参数采用列表的形式
例如:execl("/bin/ls","ls","-a","NULL")
- execv:表示参数采用数组的形式
例如:
char* myargv[]={"ls","-a,"NULL"};
execv("bin/ls",myargv);
- p(PATH):可以自动搜索环境变量PATH
- e(env):需要自己组装环境变量
(3).exec类函数用法
char* const argv[]={"ls","-a","NULL"};
char* const envp[]={"PATH=/bin:/user/bin","NULL"};
execl("/bin/ls","ls","-a","NULL");
execlp("ls","ls","-a","NULL");
execle("/bin/ls","ls","-a","NULL",envp);
execv("/bin/ls",argv);
execvp("ls",argv);
execve("/bin/ls",argv,envp);
注:这6个函数只有execve
是真正的系统调用,其他5个函数都是在execve
上包装的,它们6个函数有以下的关系。
2.实现一个简单的shell
(1).shell的功能
- Linux下,
shell
为操作系统的外壳
,为用户提供了使用操作系统的接口
,它是命令语言
、命令解释程序
及程序设计语言
的统称。 - 命令解释器:把用户
输入的命令
翻译给核心,并把核心执行的结果反馈给用户
。
(2).具体过程
-
打印一个
字符串
(包括用户名,主机名,当前文件路径
)和shell的提示符
(管理员为#
,普通用户为$
) -
在提示符后输入一串
字符串(命令
),它接收用户输入的字符串命令,进行命令分析
-
派生一个
子进程
,由子进程实现命令的功能
-
子进程
结束
时,再次发出提示符
(3).代码实现
#include<stdio.h>
#include<stdlib.h>
#include<stdlib.h>
#include<sys/times.h>
#include<wait.h>
#include<string.h>
#include<unistd.h>
#include<pwd.h>
#define MAX 128
#define NUM 128
#define BUFNUM 1024
#define COUNT 16
//获取用户名
void GetLoginname(){
struct passwd* pwd=getpwuid(getuid());
printf("[%s",pwd->pw_name);
}
//获取主机名
void Gethostname(){
char name[MAX];
gethostname(name,sizeof(name));
printf("@%s",name);
}
//获取当前文件的路径
void Getpath(){
char buf[NUM];
getcwd(buf,sizeof(buf));
char* p=buf+strlen(buf)-1;
while(*p!='/'){
--p;
}
++p;
printf(" %s]$ ",p);
}
int main(){
while(1){
//打印[zsc@localhost MyShell]
GetLoginname();
Gethostname();
Getpath();
fflush(stdout);//不刷新缓冲区会影响下边
//输入命令
char buf[BUFNUM];
fgets(buf,BUFNUM,stdin);
buf[strlen(buf)-1]=0;
//解析命令,把命令分隔成单个字符串放入到myargv中
char* myargv[COUNT];
int index=0;
myargv[index++]=strtok(buf," ");
char* ret=NULL;
while(ret=strtok(NULL," ")){
myargv[index++]=ret;
}
myargv[index]=NULL;
pid_t id=fork();
if(id<0){//error
perror("use fork");
exit(1);
}
//子进程进行程序替换,执行程序
else if(id==0){//child
execvp(myargv[0],myargv);
exit(1);
}
//父进程只要等待即可
else{//parent
waitpid(id,NULL,0);
}
}
return 0;
}