【1++的Linux】之进程(五)

作者主页:进击的1++
专栏链接:【1++的Linux】

文章目录

  • 一,什么是进程替换
  • 二,替换函数
  • 三,实现我们自己的shell

一,什么是进程替换

我们创建出来进程是要其做事情的,它可以去调用函数,或者是执行其他的程序,子进程通过exec函数族执行其他的程序就叫做进程替换。也就是在调用进程内部执行一个可执行文件。当进程调用一种exec函数时,该进程的代码和数据完全被新程序替换,新程序从main函数开始执行,由于未创建新进程,所以替换前后进程的id等并不改变。
在加载新程序之前,父子进程的关系是:代码共享,数据写时拷贝。
当子进程加载新程序的时候就是一种“写入”,此时代码也就需要进行写时拷贝,进行分离!!!

二,替换函数

下面是六种exec开头的函数,统称exec函数。

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 execve(const char *path, char *const argv[], char *const envp[]);


其中只有execve()是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。
这些函数,如果调用成功则加载新的程序开始执行,不再返回;若调用失败,则返回-1 。
下面是这些函数的演示:

#include
#include
int main()
{
    //execl---带路径,参数包传参
    //execl("/usr/bin/ps","ps","-ef",NULL);
   // execlp("ls","-l",NULL);
   
      char* env[]={"PATH=/bin:/usr/bin",NULL};
      char* argv[]={"ls","-l",NULL};
     // execv("/usr/bin/ls",argv);
     // execle("./mike","mike",NULL,env);
       // execvp("ls",argv);
        execve("/usr/bin/ls",argv,env);
    return 0;
}

execl运行结果(要写路径,参数格式未列表)
【1++的Linux】之进程(五)_第1张图片
execlp运行结果(带p的:可以使用环境变量PATH,无需写路径)
【1++的Linux】之进程(五)_第2张图片
execle运行结果:

【1++的Linux】之进程(五)_第3张图片
execv运行结果:
【1++的Linux】之进程(五)_第4张图片
execvp运行结果:

【1++的Linux】之进程(五)_第5张图片
execve运行结果:

【1++的Linux】之进程(五)_第6张图片
我们总结以下:

带p可以使用环境变量PATH,无需写完整路径
带e,自己组装环境变量。// 改变替换程序的环境变量,正确来说,让替换程序只保留 env 的环境变量
带l参数格式为列表
带v参数格式为数组

事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节。
【1++的Linux】之进程(五)_第7张图片

exec*实际上就是一个加载器的底层接口。

三,实现我们自己的shell

代码如下:

#include
#include
#include
#include
#include
#include
#define NUM 32
char cmd_line[NUM];
char* _argv[32];
char myval[32];//这个buffer用来保存我们添加的环境变量,不然保存在cmd_line中会被覆盖。
int main()
{
    extern char** environ;//是一个外部的全局变量,储存着系统的全局变量。
    while(1)
    {
        //打印提示信息
        printf("[hyp @myshell]#");
        fflush(stdout);
        memset(cmd_line,'\0',sizeof(cmd_line));
        //用户输入
        if(fgets(cmd_line,sizeof(cmd_line),stdin)==NULL)
        {
            continue;
        }
        cmd_line[strlen(cmd_line)-1]='\0';
        //分割字符
        _argv[0]=strtok(cmd_line," ");
        int i=0;
        if(strcmp(_argv[0],"ls")==0)//加颜色
        {
            _argv[++i]="--color=auto";
        }
        if(strcmp(_argv[0],"ll")==0)
        {
            _argv[0]="ls";
            _argv[++i]="--color=auto";
            _argv[++i]="-l";
        }
        while(_argv[i])//分割
        {
            i++;
            _argv[i]=strtok(NULL," ");
            
        }

        if(strcmp(_argv[0],"export")==0 && _argv[1]!=NULL)
        {
            strcpy(myval,_argv[1]);
            int ret=putenv(myval);
            if(ret==0)
            {
                printf("%s export success\n",myval);
            }

            continue;
        }
	        
	    if(strcmp(_argv[0],"cd")==0)
	    {
	        if(_argv[1]!=NULL)
	        {
	        //内置命令,让父进程自己执行的命令,本质就是shell的一个函数调用。
	            chdir(_argv[1]);//改变当前工作目录
	        }
	        continue;
	
	    }
	    int id=fork();
	    if(id==0)//child
	    {
	        printf("child MYVAL:%s\n",getenv("MYVAL"));
	        printf("PATH:%s\n",getenv("PATH"));
	        execvp(_argv[0],_argv);
	        exit(1);
	    }

	    //father
	    int status=0;
	    int ret=waitpid(id,&status,0);
	    if(ret>0)
	    {
	        printf("退出码:%d\n",WEXITSTATUS(status));
	
	    }
   }
    return 0;
}

你可能感兴趣的:(1++的Linux,linux,进程替换,实现自己的shell)