linux进程编程之exec族函数

一、初识exec函数

1、函数说明:fork函数是用于创建一个子进程,该子进程几乎是父进程的副本,而有时我们希望子进程去执行另外的程序,exec函数族就提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件

2、exec函数族常见用途:

1. 当进程不需要再往下继续运行时,调用exec函数族中的函数让自己得以延续下去。

2. 如果当一个进程想执行另一个可执行程序时,可以使用fork函数先创建一个子进程,然后通过子进程来调用exec函数从而实现可执行程序的功能。当进程调用exec函数时,该进程被完全替换为新进程,因为调用exec函数并不会创建新进程,所以前后进程的ID并没有改变。

二exec族相关API

  1. 函数原型

exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:

l : 使用参数列表

p:使用文件名,并从PATH环境进行寻找可执行文件

v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。

e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量

  1. 使用注意点

在使用exec函数族时,一定要加上错误判断语句。调用成功后不会返回,调用失败时,会设置errno并返回-1,可以通过调用perror来打印出错信息;每个exec族函数最后一个参数必须时NULL

因为exec很容易执行失败,其中最常见的原因有:

一、找不到文件或路径,此时errno被设置为ENOENT。

二、数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT。

三、没有对应可执行文件的运行权限,此时errno被设置为EACCES

三、代码实现

1、execl和execlp

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
 
int main()
{
        execl("/bin/ls", "ls", "-l", NULL);//第一个参数为ls所在目录,第二个参数可理解为调用ls,第三个参数为所需功能,第四个参数NULL表结束
        perror("execl");
        exit(1);
}
include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
        pid_t pid;//定义一个进程标识符
        int data=10;
        while(1){

                printf("please input a data\n");
                scanf("%d",&data);
                if(data == 1){
                        int fdSrc;
                        pid=fork();//如果输入的data为1,创建一个子进程
                        if(pid > 0){
                                wait(NULL);//父进程等待子进程
                        }else if(pid == 0){
                                execl("./changData","changData","config.txt",NULL);//子进程调用execl函数实现数据交换
                        }
                }else{
                        printf("wait do nothing\n");
                }

        }
        return 0;

}

如果是用execlp,那么第一个参数就可以不用加ls的路径了,直接是ls就可以了,因为系统会从PATH 环境变量中查找文件并执行

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

2、execv和execvp

如果是execv和execvp的话,区别execl和execlp的是后面的参数就要是一个指针数组的形式

但execv和execvp区别跟上面execl和execlp一样,execvp第一个参数可以不用加ps的路径了,直接是ps就可以了,因为系统会从PATH 环境变量中查找文件并执行

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
 
int main()
{
        char *argv[] = {"ls", "-l", NULL};
        execv("/bin/ls", argv);
        perror("execl");
        exit(1);
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
        printf("before execl:\n");
        char *argv[]={"ps",NULL,NULL};
        if(execvp("ps",argv) == -1){

                printf("execl failed!\n");
        }
        printf("after execl\n");
        return 0;

}

execle()、execvpe()对于初学者可不做掌握,今后在工作中可能会遇到,到时候自行补充即可

3、system函数

函数原型:

函数说明

system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。

system与exec区别

  1、system()和exec()都可以执行进程外的命令,system是在原进程上开辟了一个新的进程,但是exec是用新进程(命令)覆盖了原有的进程

  2、system()和exec()都有能产生返回值,system的返回值并不影响原有进程,但是exec的返回值影响了原进程

system()函数功能强大,我们可以参考看下linux版system函数的源码:

#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>

int system(const char * cmdstring)
{
    pid_t pid;
    int status;

    if(cmdstring == NULL){
           
         return (1);
    }

    if((pid = fork())<0){

            status = -1;
    }else if(pid = 0){

        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);

        -exit(127); //子进程正常执行则不会执行此语句

    }else{
            while(waitpid(pid, &status, 0) < 0){

                if(errno != EINTER){

                    status = -1;

                    break;

                }
            }
        }
        return status;
}
  1. popen函数

函数介绍

提到system函数,就不得不提到popen函数,根据上面system函数的源代码:system函数的执行需要通过调用fork()函数创建一个子进程,子进程通过execl函数调用shell对传参的可执行文件进行实现。这也意味着system函数实现需要依赖execl函数实现自身功能。因此system函数的结果将直接显示在终端上,这样原本运行的结果就无法保存在文件中用于实现信息交互等功能。

popen函数比system在实际应用中的好处可以获得运行的输出结果

函数原型

comand为执行命令,type为操作类型,只能是读和写的一种,一般为“r”和“w”,返回值是一个文件类型指针

代码实现

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

int main()
{

        char ret[1024]={0};
        FILE *fp;
        fp=popen("ps","r");
        int n_read=fread(ret,1,1024,fp);
        printf("read ret %d byte,ret=%s\n",n_read,ret);
        return 0;

}
四、实用小技巧

如何用程序实现在linux总端下查看时间呢?这里我们就可以用上面所学实战,首先得知道查看系统指令是date,这里我用exec和system实例一下。

exec

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

int main()
{
        printf("before execl:\n");

        if(execl("/bin/date","date",NULL) == -1){

                printf("execl failed!\n");
                perror("why");

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

}

system

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


int main()
{
        printf("before execl:\n");

        if(system("date") == -1){

                printf("execl failed!\n");
        }
        printf("after execl\n");
        return 0;

}

通过以上编译结果,可以清楚看到exec执行完后会覆盖了原有的进程,所以after execl这句话不会输出,而system是在原进程上开辟了一个新的进程,所以并不影响after execl这句话的输出,对此我们能对二者的区别印象更加深刻。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值