Linux下进程的替换及exec函数族

什么是进程替换

进程的替换简单来讲就是将进程的代码替换为另一个程序的代码。但是进程还是原先的进程,只不过内容换掉而已。而exec 函数工作是将当前进程替换为一个新进程。可以根据指定的文件名或者路径找到可执行文件。

请注意:进程的替换并不是创建新的进程,只是替换而已。创建新的进程请选择fork或者vfork。

exec函数族原型及代码示例

#include <unistd.h>

extern char **environ;
int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
int execle(const char *path, const char *arg0, ... /*,
       (char *)0, char *const envp[]*/);
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
int execv(const char *path, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
int execvp(const char *file, char *const argv[]);

exec系列函数应该用新的进程映像替换当前进程映像。新映像应该由一个称为新进程映像文件的常规可执行文件构建。执行成功不会返回,因为调用的进程映像被新的进程映像覆盖。

execl

#include <stdio.h>
#include <unistd.h> //execl

int main(int argc, char const *argv[])
{

	execl("/bin/ls","ls","-l",NULL);//执行/bin目录下的ls

	printf("DONE\n");

	return 0;
}

输出结果:
在这里插入图片描述
当进程调用一种execl函数时,该进程完全由新程序代换,exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。想想printf为什么没有输出字符串"DONE"呢? 原因是当前进程的正文都被替换了,那么execl后的语句,即便execl退出了,都不会被执行。所以没有打印出来。

execlp
execlp不需要传可执行文件的路径,只需传可执行名字就好了。前提是可执行文件必须在$PATH环境变量路径下:

在这里插入图片描述

#include <stdio.h>
#include <unistd.h> //execl

int main(int argc, char const *argv[])
{

	execlp("ls","ls","-l",NULL);//

	printf("DONE\n");

	return 0;
}

输出结果:
在这里插入图片描述

execle
execle的最后一个字母e,表示存有环境变量字符串地址的指针数组的地址。而环境变量的作用域是进程。

execle有两个功能:

1.在进程中启动程序
2.把当前的环境变量改掉

test.c

#include <unistd.h>
#include <stdio.h>
extern char** environ;

int main(int argc, char **argv)
{
    //printf("test pid=%d\n", getpid()); 
    int i;
    for (i=0; environ[i]!=NULL; ++i)
    {
        printf("%s\n", environ[i]); //打印环境变量信息
    }
    return 0;
}

execle.c

#include <stdio.h>
#include <unistd.h> //execle

int main(int argc, char const *argv[])
{
	char *env[] = { "NAME=minger", "AGE=18", NULL};

	execle ("./test", "test", NULL, env);

	return 0;
}

输出结果:
在这里插入图片描述
可以看出确实将给定的环境变量传递过来了

execv

int execv(const char *path, char *const argv[]);

把可执行文件放到指针数组里char *const argv[]。

execv.c

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
	char *arg[]={"ls", "-l", NULL};

	execv("/bin/ls", arg);
	
	perror("execv");
	return 0;
}

输出结果
在这里插入图片描述

execvp

int execvp(const char *file, char *const argv[]);

带 p 的exec函数:execlp,execvp,表示第一个参数path不用输入完整路径,只有给出命令名即可,它会在环境变量PATH当中查找命令。

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
	char *arg[]={"ls", "-l", NULL};

	execvp("ls", arg);
	
	perror("execv");
	return 0;
}

输出结果:
在这里插入图片描述execvp表示命令所需的参数以char *arg[]形式给出且arg最后一个元素必须

是NULL。

制作shell命令解释器(外部命令)

外部命令:
在/bin/ 目录下能找到命令的可执行程序的令被称为外部命令。
如:ls、pwd等,如:ls、pwd等,通过exec来执行可执行程序实现命令功能。

内部命令:
在/bin/ 目录下找不到可执行程序的命令被称为内部命令。
如:cd、exit、export等,shell程序内部通过调用函数来实现命令功能(如shell通过调用chdir函数来实现cd命令)。

shell外部命令实现

#include<errno.h>  
#include<string.h>  
#include<stdio.h>  
#include<unistd.h>  
#include<fcntl.h>  
#include <sys/wait.h> 
#include <stdlib.h> 

void Sehll_command_parser(char  brkuf[]);
void Show_env(char env[]);
#define MAXLINE 100  


int main(int argc, char **argv)  
{  
    char   brkuf[MAXLINE];//用来存放命令行输入  

    Sehll_command_parser(brkuf);
    return 0; 
}  

void Sehll_command_parser(char  brkuf[])
{

        pid_t  pid;//子进程id  
        int   status = 0;//waitpid中的参数  
        char Envbuf[MAXLINE];//  
        char *brk=" ";//命令中命令以及参数是以空格为区分的,brk就代表了区分的空格,来区分命令和参数  
        char *com = " ";//要将接收到的命令行字符串拆分,拆分的每一个子字符串放到c中,然后传递给strtemp  
        char * str[MAXLINE];//作为给exec函数族传递参数的字符串  
        int flags=0;//用来表征命令行命令中有“<<”,"<","<"这些符号  
        int pos,ii;//pos用来表征<<,<,>这个几个符号的位置  
        
        char *strtemp[15] = {0};//对接收到的命令行输入拆分之后,放到这个缓冲区中,进行处理  
                
        if(setenv("SHOW","->",1)!=0)//设置环境变量,作为命令输入的提示符  
            printf("设置环境变量失败");  
        char* env=getenv("SHOW");  
        printf("%s",env);  
        while (fgets(brkuf, MAXLINE, stdin) != NULL)  //获取字符串
        {  
            memset(str,'\0',MAXLINE);  
            memset(strtemp,'\0',MAXLINE);//每次循环的开始都要把str,strtemp清空,来接受新的命令,flag作为标志位,也要复位  
            flags=0;  
      
           if (brkuf[strlen(brkuf) - 1] == '\n')//当命令行中输入是空格然后回车的时候,要提示重新输入,不能报错  
                brkuf[strlen(brkuf) - 1] = 0; //把最后一位'\n'变成字符串结束标志'\0'
      
            if(strcmp(brkuf,"qt")==0)//判断是不是结束shell进程的标志  
                        break;  
           
            //判断是否是修改环境变量SHOW,修改命令提示符的操作  
            if(strcmp(brkuf,";/")==0)  
            {  
                    printf("修改环境变量SHOW=");  
                    fgets(Envbuf,MAXLINE,stdin);  
                    setenv("SHOW",Envbuf,1);  
                    env=getenv("SHOW");  
                    env[strlen(env)-1]='\0';  
                    printf("%s",env);  
                    continue;  
            }  
                       
           if(strcmp(brkuf,"\n")==0)//当直接在命令行中回车,要提示重新输入命令,不能报错  
            {   
                Show_env(env);
                continue;       
            } 
         //int ppgid =getpgid(getpid());  //父进程id,也就是本进程id 
                                    
          com=strtok(brkuf,brk);//对字符串使用strtok函数拆分,将子字符串保存到strtemp中  
           int i=0;  
           while(com)  //如果cmo 不等于NULL
           {  
                              
                strtemp[i]=com;  
                if(strcmp(strtemp[i],">>")==0||strcmp(strtemp[i],">")==0||strcmp(strtemp[i],"<")==0)//判断有没有<<,<,>这些字符,有则把标志位置为1  
                {  
                    flags=1;  
                    pos=i;  
                }                                         
                i++;  
                com=strtok(NULL,brk);  
                      
            }  
            
            if ((pid = fork()) < 0)   
            {  
                printf("fork error");  
            }              
            else if (pid == 0)   //子进程
            {       

                  if((strcmp(strtemp[i-1],"&"))==0)//如果有&,则对进程进行后台处理的过程  
                    {  
                              
                        setpgid(getpid(),getpid());//子进程建立新的组,与父进程控制终端脱离,成为后台进程  
                        ii=0;  
                        for(;ii<i-1;ii++)  
                            str[ii]=strtemp[ii];      
                    }  
                    else//没有&,则依然为父进程组中的进程  
                    {  
              
                        ii=0;  
                        for(;ii<i;ii++)  
                                str[ii]=strtemp[ii];  
                    } 

                    if(flags==1)//有<<,<,>表示,要重定向处理。  
                    {  
                                memset(str,'\0',100);  
                                int fd;  
                                if(strcmp(strtemp[pos],">>")==0)  
                                {  

                                    fd=open(strtemp[pos+1],O_CREAT|O_RDWR|O_APPEND,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);/*>>是以追加的方式输入*/  
                                    dup2(fd,1); 

                                }  
                                if(strcmp(strtemp[pos],">")==0)  
                                {  
                                    fd=open(strtemp[pos+1],O_CREAT|O_RDWR,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);/*>是以取代的方式输入*/  
                                    ftruncate(fd,0);  
                                    dup2(fd,1);  
              
                                }  
                                if(strcmp(strtemp[pos],"<")==0)//<表示将stdin进行重定向,重定向到strtemp[pos+1]这个文件上  
                                {   
                                    fd=open(strtemp[pos+1],O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);  
                                    dup2(fd,0);  
                                }  
                                ii=0;  
                                for(;ii<pos;ii++)  
                                {
                                        str[ii]=strtemp[ii];//将处理万的strtemp传递给str,str作为execvp的实参。  
                                        printf("str %d is %s",ii,str[ii]);
                                }
                      
                                close(fd);  
                    }  
                      
                execvp(str[0],str);//执行命令行命令  
                printf("couldn't execute: %s\n", brkuf);  
                exit(127);               
            }  
            wait(NULL); //父进程等待子进程结束。
            Show_env(env);
        }  
}

void Show_env(char env[])
{
    env=getenv("SHOW");  
    printf("%s",env);  
}

输出结果:
在这里插入图片描述

在这里插入图片描述

扫二维码关注微信公众号,获取技术干货

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值