大前端

前端学习之家-大前端

进程篇——进程的运行:exec()系列函数

说明
  本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
  QQ 群 号:513683159 【相互学习】
内容来源
  Linux网络编程

exec系列函数

  ①函数说明
    fork()函数建立新进程,但只可建立相同程序的副本,原进程保持存在直至用户显式退出。
    exec系列函数的系统调用都完成相同的功能,用新程序装入原有进程的内存空间,改变原有进程的执行代码,从新程序的入口开始执行,形成新进程,故新进程PID值不会改变,即:
    exec系列函数没有建立一个与调用进程并发的新进程,而是用新进程取代了原来的进程。
    exec系列 调用成功后,没有任何数据返回,这与fork()不同。
  ② exec 系列调用在 Linux 系统库中unistd.h中的函数声明:

/  执行PATH,所有参数在PATH之后,直到一个NULL指针和环境从'environment'  /
1. int execl( const char *path, const char *arg, ...);

/	执行FILE,搜索' PATH'环境变量,如果它不包含斜杠,所有参数在FILE后面,直到一个NULL指针和环境从'environment' 	/
2. int execlp( const char *file, const char *arg, ...);

/	执行PATH,所有参数都在PATH后面,直到一个NULL指针,之后的参数用于环境  	/
3. int execle( const char *path, const char *arg , ..., char* const envp[]);

/' environ'执行带有参数ARGV和environment的PATH。	/
4. int execv( const char *path, char *const argv[]);

/	执行FILE,搜索' PATH'环境变量,如果它不包含斜杠,参数ARGV和环境从' environment '	/
5. int execvp( const char *file, char *const argv[]);

/	替换当前进程,使用参数ARGV和环境ENVP执行PATH。ARGV和ENVP以NULL指针结束	/
6. int execve ( const char *filename, char *const argv[], char* const envp[] );

  ③函数名的解读
    (1)l 表示参数以列表方式提供。
    (2)v 表示参数以数组[向量]方式提供。
    (3)p 表示用户在PATH环境变量中寻找可执行文件。
(只需简单提供文件名,主要用于shell,因为shell所指向进程通常会从shell继承环境变量)
    (4)e 表示会提供给新进程以新的环境变量。
  ④exec系列函数没有一个同时可搜索路径和使用新环境变量的函数。

1️⃣execl()函数(l表示参数以列表方式提供)

一、函数介绍:

函数说明:

  将参数path所指路径的二进制文件程序映像载入内存,替换原先进程的地址空间,参数__arg, ...构成可变长参数列表,以NULL结束,额外参数均可填入其中,并开始运行。

项目 说明
函数原型 extern int execl (const char *__path, const char *__arg, ...) __THROW __nonnull ((1, 2));
头文件 unistd.h
参数说明 1.__path:被执行的程序所在的文件名
(必须为有效路径,且文件为可执行程序)
2.__arg, ...:其它参数一起组成了该程序执行时的参数表
返回值 成功不会返回,直接跳入新程序入口点作为结束
失败返回-1,且errno会被设置某错误返回值。
注意 参数1:不可运行 shell 命令组成的文件。系统检查文件的开头两字节,就知该文件是否为程序文件(程序文件开头两字节是系统规定的专用值)
参数2:参数表第一项是不带路径的程序文件名。被调用的程序可访问该参数表,相当于 shell 下的命令行参数。由于参数的个数是任意的,故须用一个 null 指针来标记参数表的结尾。

一些注意点:

  若execl()成功调用:
    ①改变地址空间和进程映像。
    ②改变进程一些的属性,如:1.任何挂起信号都会丢失。2.多数线程属性还原缺省。3.多数关于进程统计信息复位等。4.捕捉的任何信号会还原缺省处理方式(信号处理函数不存在)5.任何内存的锁定会丢失6.与进程内存相关任何数据都会丢失
    ③不改进程一些的属性,如:1.pid。2.ppid。3.优先级。4.所属用户和组。
  

二、示例实践:

源文件:execl.c:

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

int main (int argc,char *argv[])  
{  
    int ret = 0;                                //返回值
    printf("Executing ls\n"); 

    /**
     *          调用execl函数
     *  参数1:带路径文件名
     *  参数2:文件名
     *  最后参数:NULL 
     */
    ret = execl("/bin/ls","ls","-l",NULL);


    /**
     *  若execl()函数有返回,说明调用失败 
     */
    if(ret == -1){
        perror("execl failed to run ls");
    }
    
    exit(1);                                    //退出
} 

编译运行及结果:

  1.编译gcc execl.c -o execl
  2.运行:./execl
  3.结果:

-rwxrwxr-x  1 hhb hhb   16232 117 14:47  fork
-rw-rw-r--  1 hhb hhb     563 117 14:47  fork.c

结论:

  execl()调用后紧跟着 perror()的无条件调用。这是因为若调用程序还存在且 execl()调用返回,那么肯定是 execl()调用出错了。
  只要execl()其它exec 调用成功,就肯定清除了调用程序而代之以新的程序。

2️⃣execv()函数(v表示参数以数组[向量]方式提供)

execv()函数说明:

项目 说明
函数原型 extern int execv (const char *__path, char *const __argv[]) __THROW __nonnull ((1, 2));
头文件 unistd.h
参数说明 1.__path:指向被执行的程序文件的路径名
2.__argv[]:字符型指针的数组
返回值 成功返回0,失败返回-1
注意 参数1:不可运行一个 shell 命令组成的文件。系统只要检查文件的开头两个字节,就知该文件是否为程序文件(程序文件的开头两个字节是系统规定的专用值)
参数2:数组中第一个元素指向不带路径的程序文件名,剩下元素指向程序所用参数,由于参数的个数是任意的,故须用一个 null 指针来标记结尾。

源文件:execv.c

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

int main (int argc,char *argv[])  
{  
    char* av[] = {"ls","-1",NULL};
    execv("/bin/ls",av);
    perror("execl failed");
    exit(1);
} 

编译运行及结果

  1.编译gcc execv.c -o execv
  2.运行:./execv
  3.结果:

-rwxrwxr-x  1 hhb hhb   16232 117 14:47  fork
-rw-rw-r--  1 hhb hhb     563 117 14:47  fork.c

结论

  无。

3️⃣execlp()和 execvp()(p表示用户在PATH环境变量中寻找可执行文件)

  execlp()execvp()分别类似于系统调用 execl()execv(),主要区别是:
    函数名后多了一个p,多用于shell,因为shell所执行进程通常会从shell继承环境变量,表示:
    第一个参数指向的是文件名(不包含路径)。
    可通过检索 shell 环境变量 PATH指出的目录,来得到该文件名的路径前缀部分。如可在 shell 中用下述命令序列来设置环境变量 PATH:

$PATH=/bin;/usr/bin;/sbin
$export PATH

  即: execlp()execvp()执行时:先在目录/bin,然后在目录/usr/bin,最后在目录/sbin 中搜索程序文件。另外, execlp()execvp()还可以用于运行 shell 程序,而不只是普通的程序。

一、函数介绍

①execlp()函数说明:

  函数功能

项目 说明
函数原型 extern int execlp (const char *__file, const char *__arg, ...) __THROW __nonnull ((1, 2));
头文件 unistd.h
参数说明 1.__file:可执行文件名
2.__arg:参数列表
返回值 成功不会返回,直接跳入新程序入口点作为结束
失败返回-1,且errno会被设置某错误返回值。
注意

②execvp()函数说明:

  函数功能

项目 说明
函数原型 extern int execvp (const char *__file, char *const __argv[]) __THROW __nonnull ((1, 2));
头文件 unistd.h
参数说明 1.__file:可执行文件名
(无需带路径,会去环境变量中寻找)
2.__argv[]:参数列表
返回值 成功不会返回,直接跳入新程序入口点作为结束
失败返回-1,且errno会被设置某错误返回值。
注意

示例实践:

① 源文件:execvp .c

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

int main (int argc,char *argv[])  
{  
    int ret = 0;                                //返回值
    
    /**
     *          调用execvp函数
     *  参数1:可执行文件名
     *  参数2:参数
     *  最后参数:NULL 
     */
    char *const args[] = {"vi","./data.txt",NULL};
    ret = execvp("vi",args);

    /**
     *  若execvp()函数有返回,说明调用失败 
     */
    if(ret == -1){
        perror("execvp");
    }
    
    exit(1);                                    //退出
} 

4️⃣execle()和 execve()(e表示会提供给新进程以新的环境变量)

  execle()execve()分别类似于系统调用 execl()execv(),主要区别是:
    函数名后多了一个e,

①execle()函数说明:

  函数功能

项目 说明
函数原型 extern int execle (const char *__path, const char *__arg, ...) __THROW __nonnull ((1, 2));
头文件 unistd.h
参数说明 1.__path:
2.__arg, ...
返回值 成功不会返回,直接跳入新程序入口点作为结束
失败返回-1,且errno会被设置某错误返回值。
注意

②execve()函数说明:

  函数功能

项目 说明
函数原型 extern int execve (const char *__path, char *const __argv[], char *const __envp[]) __THROW __nonnull ((1, 2));
头文件 unistd.h
参数说明 1.__path:
2.__argv[]:参数列表
3.__envp[]:
返回值 成功不会返回,直接跳入新程序入口点作为结束
失败返回-1,且errno会被设置某错误返回值。
注意

示例实践:

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

int main (int argc,char *argv[])  
{  
    char *args[] = {"/bin/ls",NULL};

    printf("PID = %d\n",getpid());
    if(execve("/bin/ls",args,NULL) < 0)
    {
        perror("execve");
    }
    
    exit(1);                                   
} 

错误返回值

项目 说明
E2BIG 参数列表( arg)或者环境变量(envp)的长度过长。
EACCESS 没有在path 所指向的路径中的搜索权限; path所指向的文件不是一个普通文件;目标文件不是可执行的; path或文件所位于的文件系统以不可执行(noexec)的方式挂载。
EFAULT 给定的指针是无效的。
EIO 发生底层IO错误(这是很坏的情况)。
EISDIR 路径path 的最后一部分或者解释器为目录。
ELOOP 系统在解析path 时遇到了太多的符号连接。
EMFILE 调用进程打开的文件数达到了限制。
ENFILE 打开文件时遇到了系统级别(system-wide)的限制
ENOENT 目标路径或文件不存在,或者所需要的共享库不存在
ENOEXEC 目标文件不是一个有效的二进制可执行文件或者是其他体系结构上的可执行格式。
ENOMEM 内核没有足够的内存来执行新的程序
ENOTDIR path 中除最后一部分之外的某个部分不是目录
EPERM path或文件位于的文件系统是被挂载为nosuid,但用户不是root用户,且 path 或文件被设置了suid或sgid位
ETXTBSY 目标文件被其他进程以写入方式打开了

发表评论:

Copyright Your WebSite.Some Rights Reserved.