• Linux系统编程——进程替换:exec 函数族


    在 Windows 平台下,我们可以通过双击运行可执行程序,让这个可执行程序成为一个进程;而在 Linux 平台,我们可以通过 ./ 运行,让一个可执行程序成为一个进程。

    但是,如果我们本来就运行着一个程序(进程),我们如何在这个进程内部启动一个外部程序,由内核将这个外部程序读入内存,使其执行起来成为一个进程呢?这里我们通过 exec 函数族实现。

    exec 函数族,顾名思义,就是一簇函数,在 Linux 中,并不存在 exec() 函数,exec 指的是一组函数,一共有 6 个:

    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 execve(const char *path, char *const argv[], char *const envp[]);其中只有 execve() 是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。

      exec 函数族提供了六种在进程中启动另一个程序的方法。exec 函数族的作用是根据指定的文件名或目录名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。 

      进程调用一种 exec 函数时,该进程完全由新程序替换,而新程序则从其 main 函数开始执行。因为调用 exec 并不创建新进程,所以前后的进程 ID (当然还有父进程号、进程组号、当前工作目录……)并未改变。exec 只是用另一个新程序替换了当前进程的正文、数据、堆和栈段(进程替换)。

      exec 函数族的 6 个函数看起来似乎很复杂,但实际上无论是作用还是用法都非常相似,只有很微小的差别。

      l(list):参数地址列表,以空指针结尾。

      v(vector):存有各参数地址的指针数组的地址。

      p(path):按 PATH 环境变量指定的目录搜索可执行文件。

      e(environment):存有环境变量字符串地址的指针数组的地址。 

      exec 函数族装入并运行可执行程序 path/file,并将参数 arg0 ( arg1, arg2, argv[], envp[] ) 传递给此程序。

      exec 函数族与一般的函数不同,exec 函数族中的函数执行成功后不会返回,而且,exec 函数族下面的代码执行不到。只有调用失败了,它们才会返回 -1,失败后从原程序的调用点接着往下执行。execl() 示例代码:

      1. #include <unistd.h>
      2. int main(int argc, char *argv[])  
      3. {  
      4.     printf("before exec ");     
      5.     /* /bin/ls:外部程序,这里是/bin目录的 ls 可执行程序,必须带上路径(相对或绝对) 
      6.        ls:没有意义,如果需要给这个外部程序传参,这里必须要写上字符串,至于字符串内容任意 
      7.        -a,-l,-h:给外部程序 ls 传的参数 
      8.        NULL:这个必须写上,代表给外部程序 ls 传参结束 
      9.     */  
      10.     execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);      
      11.     // 如果 execl() 执行成功,下面执行不到,因为当前进程已经被执行的 ls 替换了  
      12.     perror("execl");  
      13.     printf("after exec ");       
      14.     return 0;  
      15. }  

        运行结果如下:

        execv()示例代码:

        execv() 和 execl() 的用法基本是一样的,无非将列表传参,改为用指针数组。

        1. int main(int argc, char *argv[])  
        2. {  
        3.     // execv() 和 execl() 的用法基本是一样的,无非将列表传参,改为用指针数组  
        4.     // execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);      
        5.     /* 指针数组 
        6.        ls:没有意义,如果需要给这个外部程序传参,这里必须要写上字符串,至于字符串内容任意 
        7.        -a,-l,-h:给外部程序 ls 传的参数 
        8.        NULL:这个必须写上,代表给外部程序 ls 传参结束 
        9.     */  
        10.     char *arg[]={"ls", "-a", "-l", "-h", NULL};  
        11.     // /bin/ls:外部程序,这里是/bin目录的 ls 可执行程序,必须带上路径(相对或绝对)  
        12.     // arg: 上面定义的指针数组地址  
        13.     execv("/bin/ls", arg);   
        14.     perror("execv");          
        15.     return 0;  
        16. }  execlp() 或 execl() 示例代码:

          execlp() 和 execl() 的区别在于,execlp() 指定的可执行程序可以不带路径名,如果不带路径名的话,会在环境变量 PATH指定的目录里寻找这个可执行程序,而 execl() 指定的可执行程序,必须带上路径名。

          1. #include <stdio.h>  
          2. #include <unistd.h>    
          3. int main(int argc, char *argv[])  
          4. {  
          5.     // 第一个参数 "ls",没有带路径名,在环境变量 PATH 里寻找这个可执行程序  
          6.     // 其它参数用法和 execl() 一样  
          7.     execlp("ls", "ls", "-a", "-l", "-h", NULL);      
          8.     /* 
          9.     char *arg[]={"ls", "-a", "-l", "-h", NULL}; 
          10.     execvp("ls", arg); 
          11.     */      
          12.     perror("execlp");       
          13.     return 0;  
          14. }

            execle() 或 execve() 示例代码:

            execle() 和 execve() 改变的是 exec 启动的程序的环境变量(只会改变进程的环境变量,不会影响系统的环境变量),其他四个函数启动的程序则使用默认系统环境变量。

            execle()示例代码:

            1. #include <stdio.h>  
            2. #include <unistd.h>  
            3. #include <stdlib.h> // getenv()    
            4. int main(int argc, char *argv[])  
            5. {      // getenv() 获取指定环境变量的值  
            6.     printf("before exec:USER=%s, HOME=%s ", getenv("USER"), getenv("HOME"));        
            7.     // 指针数据  
            8.     char *env[]={"USER=MIKE", "HOME=/tmp", NULL};        
            9.     /* ./mike:外部程序,当前路径的 mike 程序,通过 gcc mike.c -o mike 编译 
            10.         mike:这里没有意义 
            11.         NULL:给 mike 程序传参结束 
            12.         env:改变 mike 程序的环境变量,正确来说,让 mike 程序只保留 env 的环境变量 
            13.      */  
            14.     execle("./mike", "mike", NULL, env);      
            15.     /* 
            16.     char *arg[]={"mike", NULL};      
            17.     execve("./mike", arg, env);  
            18.     */        
            19.     perror("execle");        
            20.     return 0;  
            21. 外部程序,mike.c 示例代码:
              1. #include <stdio.h>  
              2. #include <stdlib.h>  
              3. #include <unistd.h>  
              4. int main(int argc, char *argv[])  
              5. {  
              6.     printf(" in the mike fun, after exec:  ");  
              7.     printf("USER=%s ", getenv("USER"));  
              8.     printf("HOME=%s ", getenv("HOME"));        
              9.     return 0;  
              10. }  
              11. 运行结果如下:

  • 相关阅读:
    pip包安装问题
    spyder中让生成的图像单独在窗口中显示
    错误的英语提示翻译 以及经常犯的无错误
    程序结构
    运算符
    js jq计算器
    jQuery筛选选择器
    jQuery获取标签信息
    javascript的getTime函数
    animate动画
  • 原文地址:https://www.cnblogs.com/yuanshuang/p/5642279.html
Copyright © 2020-2023  润新知