• 【Linux 进程】exec族函数详解


    exec族的组成:
    在Linux中,并不存在一个exec()的函数形式,exec指的是一组函数,一共有6个,分别是:
    #include <unistd.h>
    extern char **environ;
    int execl(const char *path, const char *arg, ...);
    int execlp(const char *file, const char *arg, ...);
    int execle(const char *path, const char *arg, ..., char *const envp[]);
    int execv(const char *path, char *const argv[]);
    int execvp(const char *file, char *const argv[]);
    int execve(const char *path, char *const argv[], char *const envp[]);
    其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数
     
    exec族函数的作用:
    exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
    与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,颇有些神似"三十六计"中的"金蝉脱壳"。看上去还是旧的躯壳,却已经注入了新的灵魂。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。
    现在我们应该明白了,Linux下是如何执行新程序的,每当有进程认为自己不能为系统和用户做出任何贡献了,他就可以发挥最后一点余热,调用任何一个exec,让自己以新的面貌重生;或者,更普遍的情况是,如果一个进程想执行另一个程序,它就可以fork出一个新进程,然后调用任何一个exec,这样看起来就好像通过执行应用程序而产生了一个新进程一样
    事实上第二种情况被应用得如此普遍,以至于Linux专门为其作了优化,我们已经知道,fork会将调用进程的所有内容原封不动的拷贝到新产生的子进程中去,这些拷贝的动作很消耗时间,而如果fork完之后我们马上就调用exec,这些辛辛苦苦拷贝来的东西又会被立刻抹掉,这看起来非常不划算,于是人们设计了一种"写时拷贝(copy-on-write)"技术,使得fork结束后并不立刻复制父进程的内容,而是到了真正实用的时候才复制,这样如果下一条语句是exec,它就不会白白作无用功了,也就提高了效率。
     
     

    exec函数族关系:

    事实上,这6个函数中真正的系统调用只有execve,其他5个都是库函数,它们最终都会调用execve这个系统调用,调用关系如下图所示:

     
     exec函数族使用举例:
    1 #ifdef HAVE_CONFIG_H
      2 #include <config.h>
      3 #endif
      4 
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <unistd.h>
      8 #include <string.h>
      9 #include <errno.h>
     10 
     11 int main(int argc, char *argv[])
     12 {
     13   //以NULL结尾的字符串数组的指针,适合包含v的exec函数参数
     14   char *arg[] = {"ls", "-a", NULL};
     15   
     16   /**
     17    * 创建子进程并调用函数execl
     18    * execl 中希望接收以逗号分隔的参数列表,并以NULL指针为结束标志
     19    */
     20   if( fork() == 0 )
     21   {
     22     // in clild 
     23     printf( "1------------execl------------
    " );
     24     if( execl( "/bin/ls", "ls","-a", NULL ) == -1 )
     25     {
     26       perror( "execl error " );
     27       exit(1);
     28     }
     29   }
     30   
     31   /**
     32    *创建子进程并调用函数execv
     33    *execv中希望接收一个以NULL结尾的字符串数组的指针
     34    */
     35   if( fork() == 0 )
     36   {
     37     // in child 
     38     printf("2------------execv------------
    ");
     39     if( execv( "/bin/ls",arg) < 0)
     40     {
     41       perror("execv error ");
     42       exit(1);
     43     }
     44   }
     45   
     46   /**
     47    *创建子进程并调用 execlp
     48    *execlp中
     49    *l希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志
     50    *p是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件
     51    */
     52   if( fork() == 0 )
     53   {
     54     // in clhild 
     55     printf("3------------execlp------------
    ");
     56     if( execlp( "ls", "ls", "-a", NULL ) < 0 )
     57     {
     58       perror( "execlp error " );
     59       exit(1);
     60     }
     61   }
     62   
     63   /**
     64    *创建子里程并调用execvp
     65    *v 望接收到一个以NULL结尾的字符串数组的指针
     66    *p 是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件
     67    */
     68   if( fork() == 0 )
     69   {
     70     printf("4------------execvp------------
    ");
     71     if( execvp( "ls", arg ) < 0 )
     72     {
     73       perror( "execvp error " );
     74       exit( 1 );
     75     }
     76   }
     77   
     78   /**
     79    *创建子进程并调用execle
     80    *l 希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志
     81    *e 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境
     82    */
     83   if( fork() == 0 )
     84   {
     85     printf("5------------execle------------
    ");
     86     if( execle("/bin/ls", "ls", "-a", NULL, NULL) == -1 )
     87     {
     88       perror("execle error ");
     89       exit(1);
     90     }
     91   }
     92   
     93   /**
     94    *创建子进程并调用execve
     95    * v 希望接收到一个以NULL结尾的字符串数组的指针
     96    * e 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境
     97    */
     98   if( fork() == 0 )
     99   {
    100     printf("6------------execve-----------
    ");
    101     if( execve( "/bin/ls", arg, NULL ) == 0)
    102     {
    103       perror("execve error ");
    104       exit(1);
    105     }
    106   }
    107   return EXIT_SUCCESS;
    108 }

    运行结果:

    1------------execl------------
    .  ..  .deps  exec  exec.o  .libs  Makefile
    2------------execv------------
    .  ..  .deps  exec  exec.o  .libs  Makefile
    3------------execlp------------
    .  ..  .deps  exec  exec.o  .libs  Makefile
    4------------execvp------------
    .  ..  .deps  exec  exec.o  .libs  Makefile
    5------------execle------------
    .  ..  .deps  .libs  Makefile  exec  exec.o
    6------------execve-----------
    .  ..  .deps  .libs  Makefile  exec  exec.o

    整理于百度百科 & https://blog.csdn.net/zjwson/article/details/53337212

  • 相关阅读:
    Java实现第九届蓝桥杯螺旋折线
    Java实现第九届蓝桥杯递增三元组
    强大的Mockito测试框架
    搭建eclipse环境下 Nutch+Mysql 二次开发环境
    mysql操作查询结果case when then else end用法举例
    查看mysql数据库及表编码格式
    mysql如何更改character-set-server默认为latin1
    (原创)Linux下MySQL 5.5/5.6的修改字符集编码为UTF8(彻底解决中文乱码问题)
    Ubuntu14连接MySql报错“can't connect to local mysql server through socket '/var/run/mysqld/mysqld.sock'”
    inux下设置mysql数据库字符集utf8
  • 原文地址:https://www.cnblogs.com/xuelisheng/p/10072362.html
Copyright © 2020-2023  润新知