Linux函数exec

Linux函数exec

一、概述:
当用fork函数创建新的子进程后,子进程往往要调用一种exec函数以执行另一个程序。当程序调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段和栈段。

三、有7种不同的exec函数可供使用,它们常常被统称为exec函数。

       #include <unistd.h>

       extern char **environ;

1.     int execl(const char *path, const char *arg, ...);
2.     int execlp(const char *file, const char *arg, ...);
3.     int execle(const char *path, const char *arg,..., char * const envp[]);
4.     int execv(const char *path, char *const argv[]);
5.     int execvp(const char *file, char *const argv[]);
6.     int execvpe(const char *file, char *const argv[],char *const envp[]);

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

函数名中的字符可以帮助理解和分辨:
l表示以参数列表的形式调用
v表示以参数数组的方式调用
e表示可传递环境变量
p表示PATH中搜索执行的文件,如果给出的不是绝对路径就会去PATH搜索相应名字的文件,如PATH没有设置, 则会默认在/bin,/usr/bin下搜索。

返回值:
如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno 中。

二、exec族函数的作用
exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。

与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。

三、实例1:在执行文件execl.c过程中调用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.c结果如下:
在这里插入图片描述

文件:execl.c

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

int main(void)
{
    printf("before execl\n");
    if(execl("./echoarg","file1","file2",NULL) == -1)
    {
        printf("execl failed!\n");
        perror("why");
    }
    printf("after execl\n");
    return 0;
}

注:perror(s) 用来将上一个函数发生错误的原因输出到标准设备(stderr)。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno的值来决定要输出的字符串。
在这里插入图片描述
若调用execl失败,返回-1,然后开始执行 printf(“after execl\n”);(打印"after execl")。
若成功则不执行 printf(“after execl\n”);(不打印"after execl")。

四、实例2:
我们都知道ls命令是linux下最常用的命令之一,用来列出目录下的文件。我们也可以调用execl来执行ls的命令。

ls指令运行结果:
在这里插入图片描述
我们可以通过whereis ls 指令来获得ls的文件路径。
在这里插入图片描述
execl.c

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

int main(void)
{
    printf("before execl\n");
    if(execl("/bin/ls","ls",NULL,NULL) == -1)
    {
        printf("execl failed!\n");
        perror("why");
    }
    printf("after execl\n");
    return 0;
}

编译运行结果如下:
在这里插入图片描述
ls -l则会把详细列表打印出来:
在这里插入图片描述

可以尝试在execl函数的第三个参数写为“-l”:
execl.c

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

int main(void)
{
    printf("before execl\n");
    if(execl("/bin/ls","ls","-l",NULL) == -1)
    {
        printf("execl failed!\n");
        perror("why");
    }
    printf("after execl\n");
    return 0;
}

编译运行结果如下:
在这里插入图片描述

五、实例3:获取日期和时间
Linux指令date 可以用来显示或设定系统的日期与时间
在这里插入图片描述
我们也可以调用execl函数来获取日期和时间(原理同实例2相同)

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

int main(void)
{
    printf("before execl\n");
    if(execl("/bin/date","date",NULL,NULL) == -1)
    {
        printf("execl failed!\n");
        perror("why");
    }
    printf("after execl\n");
    return 0;
}

编译运行结果如下:
在这里插入图片描述

六、在前面几个实例中调用函数execl第一个参数需要给出绝对路径,而函数execlp通过环境变量PATH查找到可执行文件(如实例6的“date”)。

实例6:execlp.c

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

int main(void)
{
    printf("before execlp\n");
    if(execlp("date","date",NULL,NULL) == -1)
    {
        printf("execlp failed!\n");
        perror("why");
    }
    printf("after execlp\n");
    return 0;
}

在这里插入图片描述
实例7.execvp.c

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

int main(void)
{
        printf("before execvp\n");
        char *argv[] = {"data",NULL,NULL};
        if(execvp("date",argv) == -1) {
                printf("execvp failed!\n");
                perror("why");
        }

        printf("after execvp\n");

        return 0;
}

与execlp不同,execvp以参数数组的方式调用。
在这里插入图片描述
七、前面在讲函数fork和vfork中我们提到:
fork有以下两种用法。
(1) 一个父进程希望复制自己,使父进程和子进程同时执行不同的代码段。这在网络服务进程中是常见的—父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求。
(2) 一个进程要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程从返回后立刻调用exec。

所以我们可以把fork和exec配合使用。

实例8:通过调用fork创建子进程来对文件进行更改。
TEST.config

SPEED=3
LENG=9
SCORE=9
LEVEL=0

change.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 argc,char *argv[])
{
        int  fdsrc;
        char *readbuf=NULL;

        if(argc!=2){
                printf("param error\ni");
                exit(-1);
        }

        fdsrc=open(argv[1],O_RDWR);

        int size=lseek(fdsrc,0,SEEK_END);
        lseek(fdsrc,0,SEEK_SET);

        readbuf=(char *)malloc(sizeof(size)+8);
        int n_read=read(fdsrc,readbuf,size);

        char *p=strstr(readbuf,"LENG=");

        if(p==NULL){
                printf("not found\n");
                exit(-1);
        }

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

        lseek(fdsrc,0,SEEK_SET);
        int n_write=write(fdsrc,readbuf,strlen(readbuf));

        close(fdsrc);
        return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>

int main()
{
        pid_t pid;

        int data = 0;

        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("./change","change","TEST.config",NULL);
                        }
                }else{
                        printf("wait...\n");
                }
        }
        return 0;
}

在这里插入图片描述
demo.c中调用当输入data = 1时,函数fork创建子进程,子进程调用execl来执行change(可执行文件),在change中对TEST.config进行更改。
文件TEST.config中"LENG"被修改成 了9。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值