[Linux]exec系列函数

exec系列函数

exec系列函数是用来进行程序的替换的。

替换原理

当使用fork函数创建子进程后,子进程会执行与父进程相同的代码(但也有可能执行不同的代码分支),子进程会通过调用exec函数来执行另一个程序。当调用exec函数时,会用新程序的代码和数据来覆盖当前进程的代码和数据(也就是写时拷贝,可以看看这一篇博客[Linux]进程地址空间 - 羡鱼OvO - 博客园)。由于调用exec函数并不是创建新的进程,所以调用前后该进程的pid并不会发生改变,这样从操作系统的角度看,仍然是同一个进程在运行,只是其执行的代码和数据发生了根本性的变化。

exec系列函数返回值问题

exec系列函数如果执行成功,则不会返回;如果执行失败,则会返回-1。失败返回-1好理解,但是为什么执行成功的话没有返回值呢?

那是因为,当在调用exec系列函数之后,原本的代码和数据都会被新程序的代码和数据所覆盖。也就是说,当执行完exec系列函数后,exec函数后的代码都不会被执行,所以它要返回值也没有用。

exec系列函数

  1. execl()

    • 函数原型:int execl(const char *path, const char *arg, ...)

    • 功能:通过指定程序的路径path和一系列以空指针结尾的命令行参数来执行新程序。

    • 示例:execl("/usr/bin/ls", "ls", "-l", "-a", NULL);执行ls -la命令。

      #include <stdio.h>
      #include <unistd.h>
      #include <assert.h>
      
      int main()
      {
          printf("process is running...\n");
          execl("/usr/bin/ls", "ls", "--color=auto", "-a", "-l", NULL);
          printf("process running done......\n");//程序被替换,不执行该语句
          return 0;
      }
      

      如上,就实现了运行我们自己的程序,来执行其他程序的方法。

      当然,也可以使用子进程的方式来执行程序替换。

      #include <stdio.h>
      #include <unistd.h>
      #include <assert.h>
      #include <sys/wait.h>
      #include <sys/types.h>
      #include <stdlib.h>
      int main()
      {
          printf("process is running...\n");
          pid_t id = fork();
          assert(id != -1);
          if (id == 0)
          {
              //子进程
              execl("/usr/bin/ls", "ls", "--color=auto", "-a", "-l", NULL);
              exit(1);
          }
          //父进程
          int status = 0;
          pid_t ret = waitpid(id, &status, 0);//等待子进程
          if (ret > 0) printf("wait success: sig: %d, exit code: %d\n", WEXITSTATUS(status), WIFEXITED(status) );
          printf("process running done......\n");
          return 0;
      }
      
      
  2. execlp()

    • 函数原型:int execlp(const char *file, const char *arg, ...)

    • 功能:会在环境变量PATH中自动搜索指定的file,并执行它。其他参数用法与execl()相同。

    • 示例:execlp("ls", "ls", "-l", "-a", NULL);PATH中查找ls命令并执行。

      示例给的有两个ls,这并不重复,第一个是告诉系统我要执行谁,第二个是告诉系统我想如何执行。

  3. execv()

    • 函数原型:int execv(const char *path, char *const argv[])

    • 功能:与execl()类似,但命令行参数以字符串数组的形式传递。

    • 示例:char *const argv[] = {"ls", "-l", "-a", NULL}; execv("/usr/bin/ls", argv);

      #include <stdio.h>
      #include <unistd.h>
      #include <assert.h>
      #include <sys/wait.h>
      #include <sys/types.h>
      #include <stdlib.h>
      int main()
      {
          printf("process is running...\n");
          pid_t id = fork();
          assert(id != -1);
          if (id == 0)
          {
              char *const argv_[] = {"ls", "--color=auto", "-a", "-l", NULL}; 
              execv("/usr/bin/ls", argv_); //通过字符数组传递参数
          }
          int status = 0;
          pid_t ret = waitpid(id, &status, 0);//等待子进程
          if (ret > 0) printf("wait success: sig: %d, exit code: %d\n", WEXITSTATUS(status), WIFEXITED(status) );
          printf("process running done......\n");
          return 0;
      }
      
      

      运行结果与上面的同。

  4. execvp()

    • 函数原型:int execvp(const char *file, char *const argv[])
    • 功能:结合了execv()execlp()的特点,在PATH中搜索指定的可执行文件,并以字符串数组的形式传递命令行参数。
    • 示例:char *const argv[] = {"ls", "-l", "-a", NULL}; execvp("ls", argv);
  5. execle()

    • 函数原型:int execle(const char *path, const char *arg, ..., char *const envp[])

    • 功能:除了指定程序路径和命令行参数外,还可以显式指定新程序的环境变量数组 envp

    • 示例:execle("/bin/ls", "ls", "-l", NULL, my_env); 其中 my_env 是自定义的环境变量数组。

      //mybin.c
      #include <stdio.h>
      #include <stdlib.h>
      int main() 
      {
          // 系统就有
          printf("PATH:%s\n", getenv("PATH"));
          printf("PWD:%s\n", getenv("PWD"));
          // 自定义
          printf("MYENV:%s\n", getenv("MYENV"));
      
          printf("另一个程序\n");
          printf("另一个程序\n");
          printf("另一个程序\n");
          printf("另一个程序\n");
          printf("另一个程序\n");
          printf("另一个程序\n");
          printf("另一个程序\n");
          return 0;
      }
      //test.c
      #include <stdio.h>
      #include <unistd.h>
      #include <assert.h>
      #include <sys/wait.h>
      #include <sys/types.h>
      #include <stdlib.h>
      int main()
      {
          printf("process is running...\n");
          pid_t id = fork();
          assert(id != -1);
          if (id == 0)
          {
              char *const envp_[] = { (char*)"MYENV=123456789", NULL};
              putenv((char*)"MYENV=987654321");//将指定环境变量导入到系统中
              //execle("./mybin", "mybin", NULL, envp_);//自定义环境变量
              extern char **environ;
              execle("./mybin", "mybin", NULL, environ);
          }
          int status = 0;
          pid_t ret = waitpid(id, &status, 0);//等待子进程
          if (ret > 0) printf("wait success: sig: %d, exit code: %d\n", WEXITSTATUS(status), WIFEXITED(status) );
          printf("process running done......\n");
          return 0;
      }
      
      

      当使用自定义的环境变量时,子进程只会接收你传的环境变量数组。如下

      当传入系统的环境变量时,自定义的环境变量又传不过去,所以想要同时满足将系统环境变量传过去的同时也将自定义环境变量传过去,此时就可以使用putenv()函数。

综上得出如下规律:

  • l(list):表示参数采用列表
  • v(vector):参数用数组
  • p(path):有p表示自动搜索环境变量PATH
  • e(environ):表示自己维护环境变量

事实上除了上述5个函数之外,还有一个execve()函数,它与上述函数不同的是execve()是一个系统调用,其它五个函数最终都会调用execve()

posted @   羡鱼OvO  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示