exec函数族

1)exec函数族说明

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

在Linux中使用exec函数族主要有两种情况:
     ● 当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用exec函数族中的任意一个函数让自己重生。
     ● 如果一个进程想执行另一个程序,那么它就可以调用fork()函数新建一个进程,然后调用exec函数族中的任意一个函数,这样看起来就像通过执行应用程序而产生了一个新进程(这种情况非常普遍)。

2)exec函数族语法

实际上,在Linux中并没有exec()函数,而是有6个以exec开头的函数,它们之间的语法有细微差别,本书在后面会详细讲解。

表2列举了exec函数族的6个成员函数的语法。

表2 exec函数族成员函数语法

所需头文件 #include <unistd.h>
函数原型 int execl(const char *path, const char *arg, ...)
int execv(const char *path, char *const argv[])
int execle(const char *path, const char *arg, ..., char *const envp[])
int execve(const char *path, char *const argv[], char *const envp[])
int execlp(const char *file, const char *arg, ...)
int execvp(const char *file, char *const argv[])
函数返回值 -1:出错

这6个函数在函数名和使用语法的规则上都有细微的区别,下面就从可执行文件查找方式、参数传递方式及环境变量这几个方面进行比较。
     ● 查找方式。读者可以注意到,表2中的前4个函数的查找方式都是完整的文件目录路径,而最后两个函数(也就是以p结尾的两个函数)可以只给出文件名,系统就会自动按照环境变量“$PATH”所指定的路径进行查找。
     ● 参数传递方式。exec函数族的参数传递有两种方式:一种是逐个列举的方式,而另一种则是将所有参数整体构造指针数组传递。在这里是以函数名的第5位字母来区分的,字母为“l”(list)的表示逐个列举参数的方式,其语法为const char *arg;字母为“v”(vertor)的表示将所有参数整体构造指针数组传递,其语法为char *const argv[]。读者可以观察execl()、execle()、execlp()的语法与execv()、execve()、execvp()的区别,它们的具体用法在后面的实例讲解中会具体说明。

这里的参数实际上就是用户在使用这个可执行文件时所需的全部命令选项字符串(包括该可执行程序命令本身)。要注意的是,这些参数必须以NULL结束。
     ● 环境变量。exec函数族可以默认系统的环境变量,也可以传入指定的环境变量。这里以“e”(environment)结尾的两个函数execle()和execve()就可以在envp[]中指定当前进程所使用的环境变量。

表3再对这6个函数中的函数名和对应语法做了一个小结,主要指出了函数名中每一位所表明的含义,希望读者结合此表加以记忆。

表3 exec函数名对应含义

前4位 统一为:exec
第5位 l:参数传递为逐个列举方式 execl、execle、execlp
v:参数传递为构造指针数组方式 execv、execve、execvp
第6位 e:可传递新进程环境变量 execle、execve
p:可执行文件查找方式为文件名 execlp、execvp

事实上,这6个函数中真正的系统调用只有execve(),其他5个都是库函数,它们最终都会调用execve()这个系统调用。在使用exec函数族时,一定要加上错误判断语句。exec很容易执行失败,其中最常见的原因有:
     ● 找不到文件或路径,此时errno被设置为ENOENT。
     ● 数组argv和envp忘记用NULL结束,此时errno被设置为EFAUL。
     ● 没有对应可执行文件的运行权限,此时errno被设置为EACCES。

3)exec使用实例

下面的第一个示例说明了如何使用文件名的方式来查找可执行文件,同时使用参数列表的方式。这里用的函数是execlp()。

/* execlp.c */
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>

int main()
    {
        if (fork() == 0)
        {
            /* 调用execlp()函数,这里相当于调用了“ps –ef”命令 */
            if ((ret = execlp("ps", "ps", "-ef", NULL)) < 0)
            {
                printf("Execlp error\n");
            }
        }
    }

在该程序中,首先使用fork()函数创建一个子进程,然后在子进程中使用execlp()函数。读者可以看到,这里的参数列表列出了在shell中使用的命令名和选项,并且当使用文件名进行查找时,系统会在默认的环境变量PATH中寻找该可执行文件。读者可将编译后的结果下载到目标板上,运行结果如下:

$ ./execlp
    PID TTY    Uid    Size    State    Command
    1          root   1832    S        init
    2          root   0       S        [keventd]
    3          root   0       S        [ksoftirqd_CPU0]
    4          root   0       S        [kswapd]
    5          root   0       S        [bdflush]
    6          root   0       S        [kupdated]
    7          root   0       S        [mtdblockd]
    8          root   0       S        [khubd]
    35         root   2104    S        /bin/bash /usr/etc/rc.local
    36         root   2324    S        /bin/bash
    41         root   1364    S        /sbin/inetd
    53         root   14260   S        /Qtopia/qtopia-free-1.7.0/bin/qpe -qws
    54         root   11672   S        quicklauncher
    65         root   0       S        [usb-storage-0]
    66         root   0       S        [scsi_eh_0]
    83         root   2020    R        ps -ef
    $ env
    …
    PATH=/Qtopia/qtopia-free-1.7.0/bin:/usr/bin:/bin:/usr/sbin:/sbin
    …

此程序的运行结果与在shell中直接输入命令“ps -ef”是一样的,当然,在不同系统的不同时刻可能会有不同的结果。

接下来的示例使用完整的文件目录来查找对应的可执行文件。注意,目录必须以“/”开头,否则将其视为文件名。

/* execl.c */
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>

int main()
    {
        if (fork() == 0)
        {
            /* 调用execl()函数,注意这里要给出ps程序所在的完整路径 */
            if (execl("/bin/ps","ps","-ef",NULL) < 0)
            {
                printf("Execl error\n");
            }
        }
    }

同样将代码下载到目标板上运行,运行结果同上例。

下面的示例利用execle()函数将环境变量添加到新建的子进程中,这里的“env”是查看当前进程环境变量的命令,代码如下:

/* execle.c */
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>

int main()
    {
        /* 命令参数列表,必须以NULL结尾 */
        char *envp[]={"PATH=/tmp","USER=david", NULL};

if (fork() == 0)
        {
            /* 调用execle()函数,注意这里也要指出env的完整路径 */
            if (execle("/usr/bin/env", "env", NULL, envp) < 0)
            {
                printf("Execle error\n");
            }
        }
    }

下载到目标板后的运行结果如下:

$ ./execle
    PATH=/tmp
    USER=sunq

最后一个示例使用execve()函数,通过构造指针数组的方式来传递参数,注意参数列表一定要以NULL作为结尾标识符。其代码如下:

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

int main()
    {
        /* 命令参数列表,必须以NULL结尾 */
        char *arg[] = {"env", NULL};
        char *envp[] = {"PATH=/tmp", "USER=david", NULL};

if (fork() == 0)
        {
            if (execve("/usr/bin/env", arg, envp) < 0)
            {
                printf("Execve error\n");
            }
        }
    }

下载到目标板后的运行结果如下:

$ ./execve
    PATH=/tmp
    USER=david

本文选自华清远见嵌入式培训教材《从实践中学嵌入式Linux应用程序开发》

文章来源:华清远见企业学院原文地址:http://www.farsight.com.cn/news/emb188.htm

更多相关Linux文章查看企业学院技术文章>>

Linux下多进程编程之exec函数语法及使用实例的更多相关文章

  1. 深入浅出--UNIX多进程编程之fork()函数

    0前言 上周都在看都在学习unix环境高级编程的第八章--进程控制.也就是这一章中.让我理解了unix中一些进程的原理.以下我就主要依照进程中最重要的三个函数来进行解说.让大家通过阅读这一篇文章彻底明 ...

  2. 多进程编程之system()函数

    1.system函数: 使用函数system,在程序中执行一个shell命令字符串很方便.它是一个和操作系统紧密相关的函数,用户可以使用它在自己的程序中调用系统提供的各种命令,执行系统的命令行,其实也 ...

  3. Linux/Unix C编程之的perror函数,strerror函数,errno

    #include <stdio.h> // void perror(const char *msg); #include <string.h> // char *strerro ...

  4. linux下多进程的调试

    linux下多进程的调试:  (1)follow-fork-mode           set follow-fork-mode [parent | child] ---- fork之后选择调试父进 ...

  5. Python 多进程编程之multiprocessing--Pool

    Python 多进程编程之multiprocessing--Pool ----当需要创建的子进程数量不多的时候,可以直接利用multiprocessing 中的Process 动态生成多个进程, -- ...

  6. Python 多进程编程之multiprocessing--Process

    Python 多进程编程之multiprocessing 1,Process 跨平台的进程创建模块(multiprocessing), 支持跨平台:windowx/linux 创建和启动      创 ...

  7. linux c语言 fork() 和 exec 函数的简介和用法

    linux c语言 fork() 和 exec 函数的简介和用法   假如我们在编写1个c程序时想调用1个shell脚本或者执行1段 bash shell命令, 应该如何实现呢? 其实在<std ...

  8. unix下网络编程之I/O复用(三)

    poll函数 在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll.poll提供的功能与select类似,不过在处理流设备时, ...

  9. linux下c程序调用reboot函数实现直接重启【转】

    转自:http://www.blog.chinaunix.net/uid-20564848-id-73878.html linux下c程序调用reboot函数实现直接重启 当然你也可以直接调用syst ...

随机推荐

  1. PHPCMS 实现上一篇、下一篇

    方法一:直接调用phpcms系统的函数 <div class="info"> <span>上一篇:<a href="{$previous_p ...

  2. 深入理解计算机中的 csapp.h和csapp.c

    csapp.h其实就是一堆头文件的打包,在http://csapp.cs.cmu.edu/public/code.html 这里可以下载.这是<深入理解计算机系统>配套网站. 在头文件的# ...

  3. Mesa 3D

    Mesa 3D是一个在MIT许可证下开放源代码的三维计算机图形库,以开源形式实现了OpenGL的应用程序接口. OpenGL的高效实现一般依赖于显示设备厂商提供的硬件,而Mesa 3D是一个纯基于软件 ...

  4. Tomcat不输入项目名进入自己项目(根目录指向自己的项目)

    <Host name="localhost" appBase="webapps" unpackWARs="true" autoDepl ...

  5. ASP.Net MVC开发基础学习笔记(5):区域、模板页与WebAPI初步

    一.区域—麻雀虽小,五脏俱全的迷你MVC项目 1.1 Area的兴起 为了方便大规模网站中的管理大量文件,在ASP.NET MVC 2.0版本中引入了一个新概念—区域(Area). 在项目上右击创建新 ...

  6. Android MVP理解

    Android默认采用的是MVC: View:对应于布局文件 Model:业务逻辑和实体模型 Controllor:对应于Activity 但是却存在很多问题: 1.这个View对应于布局文件,其实能 ...

  7. opacity与rgba

    background: rgba(255,255,255,0.6);容器本身透明度变化,它包含的子容器的透明度不变. opacity:0.6;容器及容器包含的子容器的透明度都会发生变化.

  8. HDU 2609 最小表示法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2609 题意:给定n个循环链[串],问有多少个本质不同的链[串](如果一个循环链可以通过找一个起点使得和 ...

  9. blade用法

    一.blade条件判断,foreach循环写法 @if(isset($fileInfo) && !empty($fileInfo)) @foreach($fileInfo as $k) ...

  10. js总结1