• 二十五、Linux 进程与信号---exec函数


    25.1 介绍

    • 在用 fork 函数创建子进程后,子进程往往要调用一种 exec 函数以执行另一个程序
    • 当进程调用一种 exec 函数时,该进程完全由新程序代换,替换原有进程的正文,而新程序则从其 main 函数开始执行。因为调用 exec 并不创建新进程,所以前后的进程 ID 并未改变。exec 只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。
    • exec 函数族也称为代码替换函数族

    25.1.1 函数说明

    1 #include <unistd.h>
    2 int execl(const char * path,const char * arg,....);
    3 int execv (const char * path, char * const argv[ ]);
    4 int execle(const char * path,const char * arg,....,char *const envp[]);
    5 int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
    6 int execvp(const char *file ,char * const argv []);
    7 int execlp(const char * file,const char * arg,……);
    • 函数功能:
      • execl()用来执行参数 path 字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
      • execv() 用来执行参数 path 字符串所代表的文件路径,与 execl() 不同的地方在于 execve() 只需两个参数,第二个参数利用数组指针来传递给执行文件。

      • execle() 是用来执行参数 path 字符串所代表的文件路径,并为新程序复制最后一个参数所指示的环境变量。接下来的参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
      • execve()用来执行参数 filename 字符串所代表的文件路径,第二个参数系利用数组指针来传递给执行文件,最后一个参数则为传递给执行文件的新环境变量数组。
      • execvp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件。
      • execlp() 会从 PATH 环境变量所指的目录中查找符合参 file 的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的 argv[0]、argv[1]……,最后一个参数必须用空指针 (NULL) 作结束。  
    • 返回值
      • 出错返回 -1,错误码存放,成功则不返回  
    • 错误码
      • EACCES
        • 欲执行的文件不具有用户可执行的权限。 
        • 欲执行的文件所属的文件系统是以 noexec 方式挂上。
        • 欲执行的文件或 script 翻译器非一般文件。
      • EPERM 
        • 进程处于被追踪模式,执行者并不具有 root 权限,欲执行的文件具有 SUID 或 SGID 位。
        • 欲执行的文件所属的文件系统是以 nosuid 方式挂上,欲执行的文件具有 SUID 或 SGID 位元,但执行者并不具有 root 权限。
      • E2BIG 参数数组过大
      • ENOEXEC 无法判断欲执行文件的执行文件格式,有可能是格式错误或无法在此平台执行。
      • EFAULT 参数 filename 所指的字符串地址超出可存取空间范围。
      • ENAMETOOLONG 参数 filename 所指的字符串太长。
      • ENOENT 参数 filename 字符串所指定的文件不存在。
      • ENOMEM 核心内存不足
      • ENOTDIR 参数 filename 字符串所包含的目录路径并非有效目录
      • EACCES 参数 filename 字符串所包含的目录路径无法存取,权限不足
      • ELOOP 过多的符号连接
      • ETXTBUSY 欲执行的文件已被其他进程打开而且正把数据写入该文件中
      • EIO I/O 存取错误
      • ENFILE 已达到系统所允许的打开文件总数。
      • EMFILE 已达到系统所允许单一进程所能打开的文件总数。
      • EINVAL 欲执行文件的ELF执行格式不只一个PT_INTERP节区
      • EISDIR ELF翻译器为一目录
      • ELIBBAD ELF翻译器有问题
    • 注意点
      • execve 函数为系统调用,其余为库函数。执行 execve 函数后面的代码不执行
      • execlp 和 execvp 函数中的 path,相对和绝对路径均可使用,其他四个函数中的 path 只能使用绝对路径。相对路径一定要在进程环境表对应的 PATH 中。
      • argv 参数为新程序执行 main 函数中传递的  argv 参数,最后一个元素为 NULL
      • envp 为进程环境表  
    • 六个函数都使以 "exec"四个字母开头的,后面的字母表示了其用法上的区别:
      • 带有字母 " l " 的函数,表明后面的参数列表是要传递给程序的参数列表,参数列表的第一个参数必须是 要执行的程序,最后一个参数必须是 NULL
      • 带有字母 “ p ”的函数,第一个参数可用是相对路径或程序名,如果无法立即找到要执行的程序,那么就在环境变量 PATH 指定的路径中搜索。其他函数的第一个参数必须是绝对路径
      • 带有字母 " v "的函数,表明程序的参数列表通过一个字符串数组来传递。这个数组和最后传递给程序的 main 函数的字符串数据 argv 完全一样。第一个参数必须是程序名,最后一个参数也必须是 NULL
      • 带有字母 " e " 的函数,用户可用自己设置程序接收一个设置环境变量的数组

      六个函数之间的关系如下:  

      

    25.2 例子

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/types.h>
     5 #include <sys/wait.h>
     6 
     7 char *cmd1 = "cat";    //相对路径
     8 char *cmd2 = "/bin/cat";//绝对路径
     9 char *argv1 = "/etc/passwd";
    10 char *argv2 = "/etc/group";
    11 
    12 
    13 int main(void)
    14 {
    15     pid_t pid;
    16     if((pid = fork()) < 0){
    17         perror("fork error");
    18         exit(1);
    19     } else if(pid == 0) {
    20         //子进程调用 exec 函数执行新的程序
    21         //execl 不带 p 需要用绝对路径的
    22         if(execl(cmd2, cmd1, argv1, argv2, NULL) < 0) {
    23             perror("execl error");
    24             exit(1);
    25         } else {
    26             printf("execl %s success
    ", cmd1);
    27         }
    28     }
    29 
    30     wait(NULL);
    31     printf("=================================
    ");
    32 
    33     if((pid = fork()) < 0){
    34         perror("fork error");
    35         exit(1);
    36     } else if(pid == 0) {
    37         
    38         char *argv[4] = {cmd1, argv1, argv2, NULL};
    39         if(execvp(cmd1, argv) < 0) {
    40             perror("execl error");
    41             exit(1);
    42         } else {
    43             printf("execvp %s success
    ", cmd1);
    44         }
    45     }
    46 
    47     wait(NULL);
    48     printf("=================================
    ");
    49     return 0;
    50 }

      

  • 相关阅读:
    建设Kubernetes生产环境的16条建议
    深度长文:深入理解Ceph存储架构
    10个最危险的Linux命令,希望你牢记在心
    完美排查入侵者的 10 个方法和 1 个解决思路
    基于Docker&Kubernetes构建PaaS平台基础知识梳理
    Linux入门进阶
    (七)服务接口调用-OpenFeign
    (六)服务调用负载均衡-Ribbon
    (五)Eureka替换方案-Consul
    (四)Eureka替换方案-Zookeeper
  • 原文地址:https://www.cnblogs.com/kele-dad/p/9157754.html
Copyright © 2020-2023  润新知