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[]);
l,ce,cv结尾需要用到路径,lp,vp,vpe只需要知道文件名即可
execl("/bin/ls",“ls”,"-l",NULL);
execlp(“ls”,“ls”,"-l",NULL);
env[0]=“MYENV=10000”;
env[1]=NULL;
execle("./env",“env”“ls”,"-l",NULL,env);/
MYENV:[10000]//这里就是程序替换打印的
/前面不定参后面env传> 进去,
// 替换的程序是./env,第一个参数是它自己后面为不定参
int i;
23 for(i=0;i<argc;i++){
24 printf(“argv=[%d]=[%s]\n”,i,argv[i]);//获取参数
25 }
26 extern char environ;
27 for(i=0;environ[i]!=NULL;i++){
28 printf(“env[%d]=[%s]\n”,i,environ[i]);//打印所有的环境变量
29 }
30 printf(“MYENV:[%s]\n”,getenv(“MYENV”));//单独获取一个环境变量
打印结果
char *env[32];
W> 10 env[0]=“MYENV=10000”;
11 env[1]=NULL;
12 execle("./env",“env”“ls”,"-l",NULL,env);
nihaoshijie
argv=[0]=[envls]
argv=[1]=[-l]
env[0]=[MYENV=10000]
MYENV:[10000]
会发现只到了env[0]其他的环境变量没有了本来应该打印所有的环境变量,意味着自己设置环境变量则原有的环境变量则继承不下来,
execle("./env",“env”“ls”,"-l",NULL,env) ;此处的env我们设置了什么环境变量环境变量才有什么不设置则没有
而对于execl和execlp的实现就是等价于execle(…,environ),把所有的环境变量都传进去了
extern charenviron;
18 execle("./env",“env”“ls”,"-l",NULL,environ);//,
19 printf(“nihao \n”);
env[21]=[XDG_DATA_DIRS=/home/chenyongjie/.local/share/flatpak/exports/share/:/var/lib/flatpak/exports/share/:/usr/local/share/:/usr/share/]
env[22]=[SSH_CONNECTION=192.168.96.1 1265 192.168.96.128 22]
env[23]=[LESSOPEN=||/usr/bin/lesspipe.sh %s]
env[24]=[XDG_RUNTIME_DIR=/run/user/1000]
env[25]=[OLDPWD=/home/chenyongjie]
env[26]=[_=./exec]
MYENV:[(null)]
此处打印了所有的环境变量而myenv就找不到了没有设置
环境变量并不一定具备全局特性都是设置的设置什么才有什么
eg: ls -a -l
execle("/bin/ls",“ls”,"-a","-l",NULL,env);
在getenv()后,
在虚拟地址空间中有一个段为参数和环境变量,当调用execle的时候c程序替换过程为:将程序加载到内存然后将pcb与内存关联并且更新虚拟地址空间,更新页表信息然后在虚拟地址空间中的环境变量和参数段放的就是-l并且把设置的env也放在这个位置,如果不放环境变量则无法替换,所以环境变量的全局特性主要就是程序替换时候的传参
execl("/bin/ls",“ls”,"-l",NULL);
对于这个函数该函数为库函数调用的是execve,在execve里面设置了环境变量调用execl,我们的参数把它设置为指针数char *const argv [组然后在把自己的环境变量传进来char *const envp[]);
execve
execl
execlp
execle
execv
execvp
execvpe
而对于execv,和execvp使用了字符串指针数组
execl与execv的区别:参数的赋予是以指针数组赋予还是不定参形式赋予
有p和没有p的区别:第一个参数程序名称是否需要路径
有e和没有e区别:环境变量是否需要自己设置
程序替换就是讲进程的虚拟地址空间所映射在物理内存的区域进行改变,改变陈成为另一个程序在内存中的位置,更新页表信息重新初始化虚拟地址空间,
目的:为了让进程运行另一个程序,用于创建子进程
minishell:体会shell原理以及替换的目的
shell原理:如果打开一个终端,终端一启动首先运行一个shell程序(bash),bash就是一个进程,这个进程干了一件事阻塞的去捕捉我们键盘的数据,可以理解成(scanf),如果用户输入一个Ls -a,此时程序就获取到了ls -a然后对ls -a进行解析,将它解析成ls和a认为ls就是想要运行的程序-a就是参数解析完毕,shell创建子进程(fork),ls的功能不是由shell来完成shell只是进行解析,fork之后就有另一个进程做的第一步就是对子进程进行程序替换(execl(ls,-a)后这个子进程就是运行的是ls程序,子进程运行完退出然后父进程进行wait()等待子进程退出(否则会成僵尸进程)然后回来继续等待输入(scanf)
shell处理流程:
while(1){
1>获取标准输入
2>对输入字符串进行解析(获取程序名称和参数)
3>创建子进程然后进行程序替换
4>进程等待
}是一个死循环
字符串如何解析如果在标准输入的字符串为标准输入在缓冲区ls不是一开始就有而是有很多空格和table键然后中间也是 ls -l 后面也有空格该怎么解析,一次性获取一行将整个命令和参数一次获取到
eg ./loop -l //./loop为argv[0]而-l为argv[1]怎么去掉空格获取两个字符串拿个指针指向字符串的位置如果为空格字符(制表符)向前走知道不为空格(制表符)为止就是字符串的第一个位置为止然后用argv[0]指向ls的起始位置然后走到字符刚完将后面空白的地方全部替换成\0然后字符串截止,然后指针继续走知道走到下一个字符处(下一个参数的起始位置)
scanf是从标准输入缓冲区拿数据,键盘在获取到电信号获取到数据放到标准输入缓冲区然后scanf从标准输入拿出数据ls -l在获取完后还有\n下次再获取数据就获取不出来所以死循环.
//minishell实现
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 int main(){
6 while(1){
7 printf("[meimeida@host] $");
8 fflush(stdout);//刷新到缓冲区直接显示因为字符在一行不要换行
9 char buf[1024]={0};
10 if(scanf("%s[\n]%*c",buf)!=1){//正则表达式\n遇到换行
11 //为止%*c为剩下的字符全部不要了%s是剩下的字符串 不要了
12 getchar();//获取一个回车
13 }
14 printf("%s\n",buf);
//解析流程:去除空白字符获取程序名称和参数
16 char *ptr=buf;
17 char *argv[32];
18 int argc=0;
19 while(*ptr!=’\0’){
20 //当前位置为非空白字符,argv指向字符的起始位置
E> 21 if(!isspace(*ptr)) { //isspace检测空白字符\n \r \t \v,不是空白字符时
22 argv[argc++]=ptr;//字符串的起始位置 argv[0]=ptr;argc++先使用后加
E> 23 while(!isspace(*ptr)&&*ptr!=’\0’){//开始走字符串
24 ptr++;
25 }
26 }
27 else{
28 *ptr=’\0’;//空白字符串替换成\0
29 ptr++;
30 }
31 }
32 //argv[argc]=NULL;//循环完毕,下一个位置以空截止
33 //int i=0;
34 //for(;i<argc;i++){
// printf("[%s]",argv[i]);
36 //}
37 //printf("\n");
38 argv[argc]=NULL;
39 int pid=fork();
40 if(pid<0){
41 exit(-1);
42 }
43 else if(pid==0){
44 execvp(argv[0],argv);//用的是指针所以用v,而且不需要路径,没有给路径在
45 //指定路径下找
46 exit(0);//防止子进程替换失败
47 }
E> 48 wait(NULL);
49 }
50 return 0;
51 }