Linux------------exec组函数学习

前言:

fork函数是用于创建一个子进程,该子进程几乎是父进程的副本,而有时我们希望子进程去执行另外的程序,exec函数族就提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。

什么情况下会使用exec组函数?
  • 当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec 函数族让自己重生。

  • 如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数使子进程重生。

函数原型以及语法

实际上,Linux中并没有exec函数,而是有6个以exec开头的函数组成exec函数族。
如下:

#include <unistd.h>
int execl(const char *path, const char *arg, ...)   常用
int execlp(const char *path, 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[])

简单来说,exec函数族都是以exec开头,后面跟的不同字母表示不同的涵义:

  • l 表示 list,指代的是命令行参数列表。
  • p 表示 path,指定搜索文件file时所使用的path变量。
  • v 表示vector,指代的是命令行参数数组。
  • e 表示 environment,指代的是环境变量数组。

函数参数

  • path 表示要执行的程序路径,可以是绝对路径或是相对路径。
    file 表示要执行的程序名称,如果该参数中包含/字符则视为路径名并直接执行,否则则视为单独的文件名,系统将会根据环境变量PATH中设置的路径顺序去搜索指定的文件。

  • argv 表示命令行参数的矢量数组

  • envp 表示带有该参数的exec函数可以在调用时指定一个环境变量数组,其他不带该参数的exec函数则使用调用进程的环境变量。

  • arg 表示程序的第0个参数,也就是程序名本身,相当于argv[0]。
    … 表示命令行参数列表,调用相应程序时有多少个命令行参数就需要有多少个输入参数项。

返回值
函数执行成功不会返回,若执行失败则返回-1,失败原因会记录在error中。

事实上,这6个函数中真正的系统调用只有execve函数,其它5个都是库函数,它们最终都会调用execve这个系统调用。
在这里插入图片描述

exec函数族使用注意点

在使用exec函数族时,一定要加上错误判断语句。因为exec很容易执行失败,其中最常见的原因有:

  • 找不到文件或路径,此时errno被设置为ENOENT。

  • 数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT。

  • 没有对应可执行文件的运行权限,此时errno被设置为EACCES。

exec后新进程保持原进程以下特征

环境变量(使用了execle、execve函数则不继承环境变量);

      进程ID和父进程ID;

      实际用户ID和实际组ID;

      附加组ID;

      进程组ID;

      会话ID;

      控制终端;

      当前工作目录;

      根目录;

      文件权限屏蔽字;

      文件锁;

      进程信号屏蔽;

      未决信号;

      资源限制;

      tms_utime、tms_stime、tms_cutime以及tms_ustime值。

对打开文件的处理与每个描述符的exec关闭标志值有关,进程中每个文件描述符有一个exec关闭标志(FD_CLOEXEC),若此标志设置,则在执行exec时关闭该描述符,否则该描述符仍打开。除非特地用fcntl设置了该标志,否则系统的默认操作是在exec后仍保持这种描述符打开,利用这一点可以实现I/O重定向。

示例:

这里重点讲讲execl函数和execlp函数,execv函数


execlp函数:
从PATH 环境变量中查找文件并执行

说明:

execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名, 找到后便执行该文件, 然后将第二个以后的参数当做该文件的argv[0]、argv[1]……, 最后一个参数必须用空指针(NULL)作结束。

execlp("ls", "ls", "-la", NULL);

int execl(const char *path, const char *arg, …)

函数说明
execl()其中后缀"l"代表list也就是参数列表的意思,第一参数path字符指针所指向要执行的文件路径, 接下来的参数代表执行该文件时传递的参数列表:argv[0],argv[1]… 最后一个参数须用空指针NULL作结束。

     #include <unistd.h>/*** File: execl.c**/
     #include <iostream>
     using namespace std;
     int main()
     {
    
    
       // 执行/bin目录下的ls, 第一参数为程序名ls, 第二个参数为"-al", 第三个参数为"/etc/passwd"
       if(execl("/bin/ls", "ls", "-al",  NULL) < 0)
       {
    
    
           cout<<"execl error"<<endl;
       }
        else
       {
    
    
          cout<<"success"<<endl;
       }
       return 0;
     }
$ vim execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    
    
    printf("parent process begin\n");
    pid_t pid = vfork();
    if(pid == -1) 
    {
    
       
        perror("vfork fail");
        exit(1);
    }   
    else if(pid == 0)
    {
    
       
        printf("child process begin\n");
        if(execl("/bin/ls", "ls", "-l", NULL) == -1) 
        {
    
    
            perror("execl fail");
            _exit(1);
        }
    }   
    sleep(1);
    printf("parent process end\n");
    return 0;
} 

$ gcc -o execl.out execl.c

$ ./execl.out
parent process begin
child process begin
总用量 16
-rw-r--r-- 1 jc jc  400 36 00:32 execl.c
-rwxr-xr-x 1 jc jc 8544 36 00:33 execl.out
parent process end


execle函数举例

利用函数execle,将环境变量添加到新建的子进程中去。

execle.c源代码如下:

#include <unistd.h>

#include <stdio.h>

int main()

{

/*命令参数列表,必须以 NULL 结尾*/

char *envp[]={"PATH=/tmp","USER=sun",NULL};

if(fork()==0){

    /*调用 execle 函数,注意这里也要指出 env 的完整路径*/

    if(execle("/usr/bin/env","env",NULL,envp)<0)

    {

        perror("execle error!");

        return -1 ;

    }

}

return 0 ;

}

编译:gcc execle.c –o execle。

执行./execle,执行结果如下:

PATH=/tmp

USER=sun

可见,使用execle和execve可以自己向执行进程传递环境变量,但不会继承Shell进程的环境变量,而其他四个exec函数则继承Shell进程的所有环境变量。


execve(执行文件)

相关函数
fork,execl,execle,execlp,execv,execvp
表头文件
#include<unistd.h>
定义函数
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
函数说明
execve()用来执行参数filename字符串所代表的文件路径,第二个参数系利用数组指针来传递给执行文件,
argv要传递给程序的完整参数列表,包括argv[0],它一般是执行程序的名字;最后一个参数则为传递给执行文件的新环境变量数组。
返回值
如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno 中。
错误代码
EACCES

  1. 欲执行的文件不具有用户可执行的权限。
  2. 欲执行的文件所属的文件系统是以noexec 方式挂上。
    3.欲执行的文件或script翻译器非一般文件。
    EPERM
    1.进程处于被追踪模式,执行者并不具有root权限,欲执行的文件具有SUID 或SGID 位。
    2.欲执行的文件所属的文件系统是以nosuid方式挂上,欲执行的文件具有SUID 或SGID 位元,但执行者并不具有root权限
int main( void )  
{
    
      
    char* args[] = {
    
     "/bin/ls", NULL };  
  
    if ( -1 == (execve("/bin/ls", args, NULL)) )  
    {
    
      
        perror( "execve" );  
        exit( EXIT_FAILURE);  
    }  
      
    puts( "shouldn't get here" );  
    exit( EXIT_SUCCESS );  
}  

代码来源:请点击!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char *argv[]) {
    
    
	pid_t pid;

	pid = fork();
	if(pid == -1) {
    
    
		perror("fork");
		exit(1);
	}

	if(pid == 0) {
    
    
		// 子进程执行另外一个程序
#if 0
// ++++++++++++++++++++ execl ++++++++++++++++++++
/*
 * int execl(const char *path, const char *arg, ...);
 		函数描述:加载一个进程,通过路径 + 程序名来加载。
 		函数参数:
 			path:程序路径+程序名字
 			arg:可执行程序的相关参数,使用NULL结尾
 		返回值:
			成功:无返回
			失败:-1
 */
		// 子进程执行指定目录下的自己编写的程序,该程序参数
		printf("this is func +++++++++ execl +++++++++\n");
		execl("/home/robin/a.out", "a.out", NULL);
		// 如果执行成功没有返回值
		perror("execl");
		exit(1);
#endif

#if 0
// ++++++++++++++++++++ execlp ++++++++++++++++++++
/*
 * int execlp(const char *file, const char *arg, ...);
		函数描述:加载一个进程,借助PATH环境变量,该函数需要配合PATH环境变量来使用,
						  当PATH中所有目录搜索后没有参数1则出错返回
		函数参数:可执行程序的相关参数,使用NULL结尾
			file:可执行程序的名字
			arg:可执行程序的相关参数,使用NULL结尾
		返回值:
			成功:无返回
			失败:-1
 */
		printf("this is func +++++++++ execlp +++++++++\n");
		execlp("ls", "ls", "-la", NULL);
		// 如果执行成功没有返回值
		perror("execlp");
		exit(1);
#endif

#if 0
// ++++++++++++++++++++ execvp ++++++++++++++++++++
/*
 * int execvp(const char *file, char *const argv[]);
		用法同 execlp
 */
		printf("this is func +++++++++ execvp +++++++++\n");
		char* arg[] = {
    
    "ls", "-a", "-l", NULL};
		execvp("ls", arg);
		// 如果执行成功没有返回值
		perror("execvp");
		exit(1);
#endif

#if 0
// ++++++++++++++++++++ execle ++++++++++++++++++++
/*
 *	 int execle(const char *path, const char *arg, ..., char *const envp[]);
 *	 	从用户指定的目录中搜索指定的命令
 */
		printf("this is func +++++++++ execle +++++++++\n");
		char* env[] = {
    
    "/bin", "./", "/home/robin", NULL};	// 自己指定环境变量,对应PATH
		execle("/bin/ls", "ls", "-l", "-a", NULL, env);
		// 如果执行成功没有返回值
		perror("execle");
		exit(1);
#endif

#if 0
// ++++++++++++++++++++ execv ++++++++++++++++++++
/*
 *  int execv(const char *path, char *const argv[]);
 *  	用法同:execl
 */
		printf("this is func +++++++++ execve +++++++++\n");
		char* argv[] = {
    
    "a.out", NULL};
		execv("/home/robin/a.out", argv);
		// 如果执行成功没有返回值
		perror("execv");
		exit(1);
#endif

#if 1
// ++++++++++++++++++++ execve ++++++++++++++++++++
/*
 *  int execve(const char *path, char *const argv[], char *const envp[]);
 */
		printf("this is func +++++++++ execve +++++++++\n");
		char* argv[] = {
    
    "ps", "a", "u", "x", NULL};
		char* env[] = {
    
    "/bin", "/home/", "./", NULL};
		execve("/bin/ps", argv, env);
		// 如果执行成功没有返回值
		perror("execve");
		exit(1);
#endif
	}

	else if(pid > 0)
		printf("parent process +++++++++++++++++++\n");
	printf("*****************************\n");

	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43743711/article/details/108134866