exec族函数

exec族函数的作用:

在使用fork函数创建新的进程后,我们经常会在新进程中调用exec函数取执行另一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的id并没有改变。

功能:

在调用进程内部执行一个可执行文件。可执行文件可以是二进制文件,也可以是任何linux下可执行的脚本文件。

函数族:

exec函数族分别是:execl、execlp、execle、execv、execvp、execvpe

函数原型:

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[]);

返回值:

exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从源程序的调用点接着往下执行。

参数说明:

path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件的名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按PATH环境变量,在它所指定的各目录中搜索可执行文件。

execl

接下去我们开始编写代码进行execl的使用,首先我们创建一个echoarg.c用于打印参数:

//echoarg.c
#include <stdio.h>

int main(int argc,char *argv[]){
        int i=0;
        for(i=0;i<argc;i++){
                printf("argv[%d]:%s\n",i,argv[i]);
        }

        return 0;
}

编译为可执行程序echoarg:gcc echoarg.c -o echoarg;
之后我们在编写一个程序调用execl:

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

int main(void){
        printf("before execl\n");
        //int execl(const char *path, const char *arg, ...);
        if(execl("./bin/echoarg","echoarg","abc",NULL) == -1)
        {
                printf("execl failed\n");
        }
        printf("after execl\n");
        return 0;
}

如果我们成功调用execl函数就不会执行printf(“before execl\n”);之后的代码,转而执行新的可执行文件,让我们运行看看,运行结果:
before execl
execl failed
after execl
可以发现三个打印函数都被执行了,说明execl被没有调用成功,但是我们不知道原因是什么,此时需要调用一个perror来返回错误信息:

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

int main(void){
        printf("before execl\n");
        //int execl(const char *path, const char *arg, ...);
        if(execl("./bin/echoarg","echoarg","abc",NULL) == -1)
        {
                printf("execl failed\n");
                perror("why");
        }
        printf("after execl\n");
        return 0;
}

运行后结果:
before execl
execl failed
why: No such file or directory
after execl
错误信息显示,在我们指定的路径下并没有这个文件,那么我们就可以知道是文件路径指定错误,此时echoarg可执行文件就在我们的当前路径下而不是bin文件夹下,因此需要修改路径:

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

int main(void){
        printf("before execl\n");
        //int execl(const char *path, const char *arg, ...);
        if(execl("./echoarg","echoarg","abc",NULL) == -1)	//参数列表的结尾以NULL结束
        {
                printf("execl failed\n");
                perror("why");
        }
        printf("after execl\n");
        return 0;
}

运行结果:
before execl
argv[0]:echoarg
argv[1]:abc
此时execl调用成功,调用execl后并不会执行后面的代码转而去执行我们的echoarg可执行文件。

小玩法,如何用C语言调取当前的时间

根据上述代码,我们可以调用execl执行一个新的可执行文件,那么其实像ls,date之类的指令在linux中也是一个可执行文件,首先我们通过whereis 指令 来获取指令在系统中的位置,之后将该路径写入上述代码即可完成我们需要的功能,比如调取当前的时间:
首先找到date的路径
whereis date
date: /bin/date /usr/share/man/man1/date.1.gz
修改路径:

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

int main(void){
        printf("before execl\n");
        //int execl(const char *path, const char *arg, ...);
        if(execl("/bin/date","date",NULL) == -1)
        {
                printf("execl failed\n");

                perror("why");
        }
        printf("after execl\n");
        return 0;
}

运行结果:
before execl
Fri May 10 16:06:28 CST 2024
成功获取当前时间

execlp

上述代码中,我们如果想使用系统中的某些参数比如ls,date,ps我们都需要首先通过whereis 来查找路径位置比较麻烦,因此我们可以考虑使用execlp来进行调用,它能通过环境变量PATH查找到我们需要的可执行文件。系统会先从当前文件去找,如果找不到就去环境变量找:

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

int main(void){
        printf("before execl\n");
        //int execl(const char *path, const char *arg, ...);
        if(execlp("date","date",NULL) == -1)	//无需指定路径,只需要写我们需要的,它自己会去找到路径
        {
                printf("execl failed\n");

                perror("why");
        }
        printf("after execl\n");
        return 0;
}

运行结果:
./a.out
before execl
Fri May 10 16:27:31 CST 2024

如何通过环境变量去寻找我们需要的文件

首先我们可以通过echo P A T H 去查看我们的环境变量: / u s r / l o c a l / s b i n : / u s r / l o c a l / b i n : / u s r / s b i n : / u s r / b i n : / s b i n : / b i n : / u s r / g a m e s : / o p t / F r i e n d l y A R M / t o o l s c h a i n / 4.5.1 / b i n 因此每当我们需要使用的可执行文件在这些路径下,我们都可以不用管当前处于什么路径直接使用,比如 l s 存在于 b i n 中而环境变量中有 b i n 这个目录,因此我们在任何路径下都可以使用 l s 来查看当前文件。但是像我们之前编译的 e c h o a r g 函数只能在我们当时编译的那个路径下才可以使用,如何让 e c h o a r g 也像 l s 一样可以在任何情况下都调用呢?答案是添加环境变量: 1. 首先通过 p w d 指令查看我们的当前路径 / h o m e / C L C / p r o c e s s 2. 将当前的路径添加到环境变量 e x p o r t P A T H = PATH去查看我们的环境变量: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/opt/FriendlyARM/toolschain/4.5.1/bin 因此每当我们需要使用的可执行文件在这些路径下,我们都可以不用管当前处于什么路径直接使用,比如ls存在于bin中而环境变量中有bin这个目录,因此我们在任何路径下都可以使用ls来查看当前文件。 但是像我们之前编译的echoarg函数只能在我们当时编译的那个路径下才可以使用,如何让echoarg也像ls一样可以在任何情况下都调用呢? 答案是添加环境变量: 1.首先通过pwd指令查看我们的当前路径 /home/CLC/process 2.将当前的路径添加到环境变量 export PATH= PATH去查看我们的环境变量:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/opt/FriendlyARM/toolschain/4.5.1/bin因此每当我们需要使用的可执行文件在这些路径下,我们都可以不用管当前处于什么路径直接使用,比如ls存在于bin中而环境变量中有bin这个目录,因此我们在任何路径下都可以使用ls来查看当前文件。但是像我们之前编译的echoarg函数只能在我们当时编译的那个路径下才可以使用,如何让echoarg也像ls一样可以在任何情况下都调用呢?答案是添加环境变量:1.首先通过pwd指令查看我们的当前路径/home/CLC/process2.将当前的路径添加到环境变量exportPATH=PATH:/home/CLC/process
此时再查看环境变量我们的路径已经被加入
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/opt/FriendlyARM/toolschain/4.5.1/bin:/home/CLC/process
这是我们就可以在任何路径下使用我们process目录下的可执行文件,也不需要加./:
CLC@Embed_Learn:~$ echoarg 11 22 33
argv[0]:echoarg
argv[1]:11
argv[2]:22
argv[3]:33

execvp

其实就是将重新写一个数组,将参数放到数组里,之后调用execvp里面只需要加入我们访问的可执行文件和argv数组即可。

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

int main(void){
        printf("before execl\n");
        //int execl(const char *path, const char *arg, ...);
        char *argv[]={"date",NULL};

        if(execvp("date",argv) == -1)
        {
                printf("execl failed\n");

                perror("why");
        }
        printf("after execl\n");
        return 0;
}

运行结果:
./a.out
before execl
Fri May 10 16:30:46 CST 2024

exec族函数配合fork使用

实现功能:当父进程检测到输入为1的时候,创建子进程把配置文件的字段值修改掉。
具体实现流程跟之前博文中修改配置文件操作类似,代码如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(){
        //pid_t getpid(void);
        pid_t pid;
        int data;

        while(1){
                printf("please input a data\n");
                scanf("%d",&data);
                if(data == 1){
                        pid = fork();
                        if(pid > 0){
                                wait(NULL);
                        }
                        if(pid == 0){
                                int fdSrc;
                                char *readBuf=NULL;

                                fdSrc = open("config.txt",O_RDWR);
                                int size = lseek(fdSrc,0,SEEK_END);
                                lseek(fdSrc,0,SEEK_SET);        //将光标恢复到头
                                readBuf = (char *)malloc(sizeof(char)*size+8);

                                int n_read = read(fdSrc,readBuf,size);

                                char *p = strstr(readBuf,"LENG=");
                                if(p == NULL){
                                        printf("not found\n");
                                        exit(-1);
                                }else{
                                        printf("change success\n");
                                }

                                p=p+strlen("LENG=");
                                *p = '5';

                                lseek(fdSrc,0,SEEK_SET);
                                int n_write = write(fdSrc,readBuf,strlen(readBuf));
                                close(fdSrc);
                                exit(0);
                        }

                }else{
                        printf("wait,no request\n");
                }
        }
        return 0;
}

代码功能:当我们输入一个非1的数时会提醒wait,no request,当我们输入1时,会将config.txt中LENG的值修改为5。
在这里插入图片描述
之后我们可以结合今天学的execl的方式,将我们的代码简化:
1.首先编译上述代码为一个可执行程序

gcc demo20.c -o myChange
//demo20.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(){
        int fdSrc;
        char *readBuf=NULL;

        fdSrc = open("config.txt",O_RDWR);
        int size = lseek(fdSrc,0,SEEK_END);
        lseek(fdSrc,0,SEEK_SET);        //将光标恢复到头
        readBuf = (char *)malloc(sizeof(char)*size+8);

        int n_read = read(fdSrc,readBuf,size);

        char *p = strstr(readBuf,"LENG=");
        if(p == NULL){
         printf("not found\n");
         exit(-1);
        }else{
         printf("find success\n");
        }

        p=p+strlen("LENG=");
        *p = '5';

        lseek(fdSrc,0,SEEK_SET);
        int n_write = write(fdSrc,readBuf,strlen(readBuf));
        printf("change success!\n");
        close(fdSrc);
        exit(0);

        return 0;
}

2.拷贝一个新文件demo18.c
将判断子进程之后的代码全部删除,通过加入execl(“./dataChange”,“dataChange”,NULL)

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(){
        //pid_t getpid(void);
        pid_t pid;
        int data;

        while(1){
                printf("please input a data\n");
                scanf("%d",&data);
                if(data == 1){
                        pid = fork();
                        if(pid > 0){
                                wait(NULL);
                        }
                        if(pid == 0){
                                execl("./myChange","myChange",NULL);
                        }

                }else{
                        printf("wait,no request\n");
                }
        }
        return 0;
}

在这里插入图片描述
成功修改config.txt中的内容

  • 34
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值