一、进程程序替换
进程程序替换:
进程程序替换过程只是将代码和数据替换,并没有创建新的进程,pid不变。
Linux操作系统中的shell就是运用这个原理处理客户请求的,不是每个请求都是shell亲力亲为的,所以shell会创建子程序替换他,在实现shell的过程中我们会用到exec函数,所以我们先了解一下exec函数族并对其每个的用法用代码实现一遍。
exec函数家族包括6种:execl、execlp、execle、execv、execvp、execve,如下:
它们有什么区别呢?
函数名中:
l(list):参数采用列表
v(vector):参数采用数组
p(path):由p自动搜索环境变量(path)
e(env):表示自己维护环境变量
所以,各函数的区别如下:
这些函数如果调用成功,则加载新的程序从启动代码开始执行,不再返回;
如果调用出错则返回-1;
也就是说exec函数只有出错返回值,没有成功返回值。
exec函数应用举例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main(){
char *const argv[]={"ps","-ef",NULL};
char *const envp[]={"PATH=/bin:/usr/bin","TERM=console",NULL};
execl("/bin/ps","ps","-ef",NULL);
//带p,可以使用环境变量PATH,无需写全路径
execlp("ps","ps","-ef",NULL);
//带e,需要自己组装环境变量
execle("ps","ps","-ef",NULL,envp);
execv("/bin/ps",argv);
//带p,可以使用环境变量PATH,无需写全路径
execvp("ps",argv);
//带e,需要自己组装环境变量
execve("/bin/ps",argv,envp);
exit(0);
}
其实,只有execve是真正的系统调用,其他五个函数最终都调用execve。
二、编写一个简易shell
1、看一下简易shell的编写
2、了解shell的工作过程
时间从左向右,如图:
shell从用户读入字符串“ls",shell建立一个新的进程,用来运行ls的程序并等待此进程结束。
然后shell读取新的一行输入,建立一个新的进程,在这个进程运行程序,并等待其结束。
所以我们知道,要写一个shell,需要循环以下过程:
a)
获取命令行
b)解析命令行
c)建立一个子进程(fork)
d)替换子进程(execvp)
e)父进程等待子进程退出(wait)
实现代码:
1 #include<unistd.h>
2 #include<sys/wait.h>
3 #include<stdio.h>
4 #include<stdlib.h>
5 #include<string.h>
6
7 char *argv[8];
8 int argc=0;
9
10 void do_parse(char *buf){
11 int i;
12 int status=0;
13
14 for(argc=i=0;buf[i];i++){
15 if(!isspace(buf[i])&&status==0){
16 argv[argc++]=buf+i;
17 status=1;
18 }
19 else if(isspace(buf[i])){
20 status=0;
21 buf[i]=0;
22 }
23 }
24 argv[argc]=NULL;
25 }
26
27 void do_execute(void){
28 pid_t pid=fork();
29
30 switch(pid){
31 case -1:
32 perror("fork");
33 exit(EXIT_FAILURE);
34 break;
35 case 0:
36 execvp(argv[0],argv);
37 perror("execvp");
38 exit(EXIT_FAILURE);
39 default:
40 {
41 int st;
42 while(wait(&st)!=pid)
43 ;
44 }
45 }
46 }
47
48 int main(){
49 char buf[1024]={};
50 while(1){
51 printf("myshell> ");
52 scanf("%[^\n]%*c",buf);
53 do_parse(buf);
54 do_execute();
55 }
56 }
结果如下: