UNIX环境高级编程 - - - 进程exec族函数


前言

本文为笔者学习笔记,若有不妥之处,欢迎斧正。

一、exec族函数简介

exec族函数的作用:用在fork函数创建新的进程后,经常会在新进程中调用exec函数去执行另外的一个程序。

注意:当进程调用exec函数时,该进程被完全替换为新程序。在调用exec函数时并不创建新的进程,所以前后的进程pid没有改变。

exec族函数有:execl、execv execle execve execlp execvp fexecve等。

二、exec族函数详解

1.函数原型

#include <unistd.h>

int execl(const char *path, const char *arg, ... /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char  *) NULL */);
int execle(const char *path, const char *arg, ... /*, (char *) NULL, 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[]);

作用:在fork函数后,让子进程去执行另一个程序。

参数说明:

  1. path:可执行文件路径名
  2. arg:可执行程序所带的参数,第一个参数为可执行文件的名字,没有带有路径且arg必须以NULL结束。
  3. file:如果参数file中包含/,则将其视为路径名,否则就按path环境变量,在黄静变量所指的目录中搜寻可执行文件。
  4. envp:指向环境字符串的各字符指针构成的数组的指针

返回值:
函数执行成功时不会返回。
函数执行失败时,会设置errno并返回-1,然后原程序的调用点继续往下执行。

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

l : 使用参数列表

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

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

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

2.详解上述API

  • int execl(const char *path, const char arg, … / (char *) NULL */);

注意事项:函数的参数path一定得有,arg表示参数,跟在path后的为执行文件的文件名,后面是可执行文件的参数,可以有很多个,但是最后必须有NULL结尾。

代码演示:

/*
 *	调用 execl 函数的进程
 */
#include <stdio.h>

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


        return 0;
}

/*
 *被调用execl函数进程调用的进程
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(void)
{
    
    
        printf("before execl\n");
        if(execl("./aaa","aaa","bbb","ccc","ddd","eee",NULL)==-1)
        {
    
    
                printf("execl fail\n");
        }
        printf("after execl\n");

        return 0;
}

运行结果如下:

before execl
argv[0]:aaa
argv[1]:bbb
argv[2]:ccc
argv[3]:ddd
argv[4]:eee

代码演示:

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

int main(void)
{
    
    
        printf("before execl\n");
        if(execl("/bin/ls","ls","-l",NULL)==-1)
        {
    
    
                printf("execl fail\n");
        }
        printf("after execl\n");

        return 0;
}

运行结果如下:

before execl
total 48
-rwxr-xr-x 1 cjh cjh 8304 May 15 15:14 aaa
-rw-r--r-- 1 cjh cjh  191 May 15 15:13 aaa.c
-rwxr-xr-x 1 cjh cjh 8344 May 15 15:14 demo1
-rw-r--r-- 1 cjh cjh  291 May 15 15:14 demo1.c
-rwxr-xr-x 1 cjh cjh 8344 May 15 15:21 demo2
-rw-r--r-- 1 cjh cjh  273 May 15 15:21 demo2.c
  • int execlp(const char *file, const char arg, … / (char *) NULL */);

注意事项:带p的一类exac函数,包括execlp、execvp、execvpe,如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。举个例子,PATH=/bin:/usr/bin

代码演示:

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

int main(void)
{
    
    
        printf("before execl\n");
        if(execlp("ls","ls","-l",NULL)==-1)
        {
    
    
                printf("execl fail\n");
        }
        printf("after execl\n");

        return 0;
}

运行结果:

before execl
total 64
-rwxr-xr-x 1 cjh cjh 8304 May 15 15:14 aaa
-rw-r--r-- 1 cjh cjh  191 May 15 15:13 aaa.c
-rwxr-xr-x 1 cjh cjh 8344 May 15 15:14 demo1
-rw-r--r-- 1 cjh cjh  291 May 15 15:14 demo1.c
-rwxr-xr-x 1 cjh cjh 8344 May 15 15:21 demo2
-rw-r--r-- 1 cjh cjh  273 May 15 15:21 demo2.c
-rwxr-xr-x 1 cjh cjh 8344 May 15 15:24 demo3
-rw-r--r-- 1 cjh cjh  270 May 15 15:24 demo3.c
  • int execle(const char *path, const char *arg, …, char * const envp[]);

注意事项:函数的参数path一定得有,arg表示参数,跟在path后的为执行文件的文件名,后面是可执行文件的参数,可以有很多个,但是最后必须有NULL结尾

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

目的是为了参数可视化,将所有的参数放在数组中。

注意事项:带v不带l的一类exac函数,包括execv、execvp、execve,应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。

注意事项:带e的一类exac函数,包括execle、execvpe,可以传递一个指向环境字符串指针数组的指针。

注意事项:带p的一类exac函数,包括execlp、execvp、execvpe,如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。

代码演示:

//文件execvp.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execvp(const char *file, char *const argv[]);

int main(void)
{
    
    
    printf("before execlp****\n");
    char *argv[] = {
    
    "ps","-l",NULL};
    if(execvp("ps",argv) == -1) 
    {
    
    
        printf("execvp failed!\n");     
    }
    printf("after execlp*****\n");
    return 0;
}

运行结果:

before execlp****
F S   UID    PID   PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S  1000   1917   1908  0  80   0 -  5646 wait   pts/0    00:00:00 bash
0 R  1000   5564   1917  0  80   0 -  7229 -      pts/0    00:00:00 ps

三、Linux system函数

函数原型:

#include <stdlib.h>

int system(const char *command);

system函数实质上的过程如下:

1.fork一个子进程;

2.在子进程中调用exec函数去执行command;

3.在父进程中调用wait去等待子进程结束。 对于fork失败,system()函数返回-1。 如果exec执行成功,也即command顺利执行完毕,则返回command通过exit或return返回的值。
(注意,command顺利执行不代表执行成功比如command:“rmdebuglog.txt”,不管文件存不存在该command都顺利执行了) 如果exec执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在,system()函数返回127. 如果command为NULL,则system()函数返回非0值,一般为1。

注意事项:system()函数用起来很容易出错,返回值太多,而且返回值很容易跟command的返回值混淆。

笔者不推荐使用system函数,推荐使用下面要介绍的popen函数

四、Linux popen函数

函数原型:

#include <stdio.h>

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);

函数作用:允许一个程序将另外一个程序作为新进程来启动,并可以传递数据或者通过它接受数据。

参数说明:

1.执行一个以NULL结束的shell命令字符串的指针。

2.打开文件流的权限,只读或只写

返回值:
如果调用fork()或pipe()失败,或者不能分配内存将返回NULL,否则返回标准I/O流。popen()没有为内存分配失败设置errno值。如果调用fork()或pipe()时出现错误,errno被设为相应的错误类型。如果type参数不合法,errno将返回EINVAL。

popen函数原理:popen()函数通过创建一个管道,调用fork()产生一个子进程,执行一个shell以运行命令来开启一个进程。

注意事项:这个管道必须由pclose()函数关闭,而不是fclose()函数。pclose()函数关闭标准I/O流,等待命令执行结束,然后返回shell的终止状态。如果shell不能被执行,则pclose()返回的终止状态与shell已执行exit一样。

popen缺点:使用popen的不好影响是,针对每个popen调用,不仅要启动一个被请求的程序,还要启动一个shell,即每个popen调用将多启动两个进程。

代码演示:

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


int main(void)
{
    
    
        char ret[1024]={
    
    0};
        FILE *fp;
        int size;

        fp=popen("ps","r");

        size = fread(ret,1,1024,fp);
        printf("read %d byte,ret:%s\n",size,ret);

        pclose(fp);
        return 0;
}

运行结果如下:

 PID TTY          TIME CMD
  1917 pts/0    00:00:00 bash
  5685 pts/0    00:00:00 ps

猜你喜欢

转载自blog.csdn.net/weixin_51363326/article/details/116849762
今日推荐