前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Linux】从 fork() 到 exec():理解 Linux 进程程序替换的魔法

【Linux】从 fork() 到 exec():理解 Linux 进程程序替换的魔法

作者头像
Yui_
发布2024-10-21 08:32:44
140
发布2024-10-21 08:32:44
举报
文章被收录于专栏:Yui编程知识

1.前言

进程程序替换是指一个进程用另一个新的可执行程序来替换当前正在执行的程序,这个过程通过通过exec系列函数完成。在Linux或UNIX系统中,进程程序替换通常发生在一个进程通过fork()创建了子进程之后,子进程用exec()函数加载和执行另一个程序。 也就是说,进程程序替换就是在不改变进程的PID(进程ID)的情况下,用一个全新的程序来替换当前的内存空间和执行内容。 当程序调用一种exec函数时,该进程的用户空间代码和数据完全被新的程序替换,从新程序的启动例程开始执行。

进程程序替换
进程程序替换

2.替换函数

exec函数是一个系列函数,负责替换当前进程的映像。常见的exec函数包括:

代码语言:javascript
复制
#include <unistd.h>
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[]);

2.1 函数解释

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不在返回。
  • 如果调用出错则返回-1。
  • 所以exec函数只有出错的返回值而没有成功的返回值。 使用execl测试
代码语言:javascript
复制
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
int main()
{
	pid_t id = fork();
	if(id < 0)
	{
		perror("fork failed");
		exit(1);
	}
	if(id == 0)
	{
		//child
		printf("i am a child process,replacing myself with /bin/ls\n");
		execl("/bin/ls","ls",NULL);//使用exec替换为ls程序
		//如果execl执行成功,下行将不会被执行。
		perror("execl failed");
		exit(1);
	}
	else 
	{
		//father
		wait(NULL);//等待子进程结束
		printf("child process finished\n");
	}
	return 0;
}

执行结果:

代码语言:javascript
复制
i am a child process,replacing myself with /bin/ls
a.out  pReplaceTest.c
child process finished

在这个例子中:

  • 父进程创建了一个子进程。
  • 子进程使用execl()来替换自己,将当前的进程映像替换为/bin/ls程序。
  • 替换成功后,子进程开始执行ls程序的代码,将不在执行原理的代码。
  • 父进程调用wait()等待子进程结束,执行ls命令将结果输出到控制台。 在程序的替换函数,通常不会让父进程去执行,而是交给子进程去执行,因为父进程还需要进行完成它的时,同时也防止替换的程序造成程序崩溃。由于进程间的独立性,即使子进程去执行execl函数时候,替换的也是子进程的代码和数据,而父进程的代码和数据是不会被影响的。 提问:发生了子进程的程序替换,此时:父进程的代码还是共享的吗? 回答:

不是,此时发生了写时拷贝,也就是会拷贝一份代码和数据出来,然后子进程再被它的execl还是进行程序替换。

2.2 命名理解

这些函数的名称还是太相似了,为了方便记忆,可以按一下规律记忆一下。

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

函数名

参数格式

是否带路径

是否使用当前环境变量

execl

列表

不是

execlp

列表

execle

列表

不是

不是,需要自己组装环境变量

execv

数组

不是

execvp

数组

execve

数组

不是

不是,需要自己组装环境变量

2.3 exec系列函数介绍

execl函数在上文已经介绍,这里就跳过了。

2.3.1 execlp函数

execlp函数和execl函数的区别在于,execlp在第一个参数时候,不需要全路径,只需要写上执行命令的文件名即可,表示你需要执行谁,往后也就是和execl的参数一样。

代码语言:javascript
复制
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        execlp("ls","ls","-a","-l",NULL);
        exit(1);
    }
    else if(id>0)
    {
        int ret = wait(NULL);
        if(ret<0)
        {
            perror("wait failed");
        }
        else
        {
            printf("wait success\n");
        }
    }
    else
    {
        perror("fork failed");
        exit(1);
    }

	return 0;
}

执行结果

代码语言:javascript
复制
drwxrwxr-x  2 ubuntu ubuntu  4096 Oct 20 14:18 .
drwxr-x--- 11 ubuntu ubuntu  4096 Oct 20 14:18 ..
-rwxrwxr-x  1 ubuntu ubuntu 16168 Oct 20 12:39 a.out
-rwxrwxr-x  1 ubuntu ubuntu 16168 Oct 20 14:18 execlp
-rw-rw-r--  1 ubuntu ubuntu   519 Oct 20 14:18 execlpTest.c
-rw-rw-r--  1 ubuntu ubuntu   552 Oct 20 12:39 pReplaceTest.c
-rw-rw-r--  1 ubuntu ubuntu     1 Oct 20 14:11 test.c
wait success
2.3.2 execle函数

execle函数比execl函数多一个e,按照上的设定。需要在最后一个参数需要给execle传入自定义的环境变量数组。 它的使用情况:如果你需要给你执行的一个新的程序,加载一些自定义的环境变量给新的程序时候,以可以使用该函数。 exeTest.c文件:打印环境变量的值,这个文件假如自己执行自己的话那么会打印默认的环境变量。假如其他文件使用execle传参给exeTest.c的话,exeTest.c就会执行该execle传递过来的环境变量。

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    extern char** environ;
    int i = 0;
    for(;environ[i];i++)
    {
        printf("%s\n",environ[i]);
    }
    return 0;
}

execle测试

代码语言:javascript
复制
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        char* const myEnv[] = {
            "MYVAL1 = 123",
            "MYVAL2 = 456",
            "MYVAL3 = 789",
            NULL
        };
        execle("./myexe","./myexe",NULL,myEnv);
        exit(1);
    }
    else if(id>0)
    {
        int ret = wait(NULL);
        if(ret>0)
        {
            printf("wait success\n");
        }
        else
        {
            perror("wait failed\n");
        }
    }
    else 
    {
        perror("fork failed");
        exit(1);
    }

    return 0;
}

结果:

代码语言:javascript
复制
MYVAL1 = 123
MYVAL2 = 456
MYVAL3 = 789
wait success
2.3.3 execv函数

execv和execl函数没什么区别。execv函数把execl的以列形式的传参,变成了以数组的形式的传参。

代码语言:javascript
复制
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    pid_t id = fork();
	if(id < 0)
	{
		perror("fork failed");
		exit(1);
	}
	if(id == 0)
	{
		//child
        char* const argv[]={
            "ls",
            "-a",
            "-l",
            NULL
        };
        execv("/bin/ls",argv);
		//如果execv执行成功,下行将不会被执行。
		perror("execl failed");
		exit(1);
	}
	else
	{
		//father
		wait(NULL);//等待子进程结束
		printf("child process finished\n");
	}
    return 0;
}

执行结果:

代码语言:javascript
复制
drwxrwxr-x  2 ubuntu ubuntu  4096 Oct 20 14:54 .
drwxr-x--- 11 ubuntu ubuntu  4096 Oct 20 14:54 ..
-rwxrwxr-x  1 ubuntu ubuntu 16216 Oct 20 14:54 a.out
-rwxrwxr-x  1 ubuntu ubuntu 16216 Oct 20 14:41 execle
-rw-rw-r--  1 ubuntu ubuntu   672 Oct 20 14:38 execleTest.c
-rwxrwxr-x  1 ubuntu ubuntu 16168 Oct 20 14:18 execlp
-rw-rw-r--  1 ubuntu ubuntu   519 Oct 20 14:18 execlpTest.c
-rw-rw-r--  1 ubuntu ubuntu   575 Oct 20 14:54 execvTest.c
-rw-rw-r--  1 ubuntu ubuntu   162 Oct 20 14:40 exeTest.c
-rwxrwxr-x  1 ubuntu ubuntu 16024 Oct 20 14:40 myexe
-rw-rw-r--  1 ubuntu ubuntu   552 Oct 20 12:39 pReplaceTest.c
-rw-rw-r--  1 ubuntu ubuntu     1 Oct 20 14:11 test.c
child process finished

如此一来,我们就讲完了:l v p e的各个结果。其他函数只需要在此基础上进行配合就是了。

exec函数
exec函数

4. 总结

  • 进程程序替换是指用一个新的可执行程序替换当前进程的内存空间和执行内容,但进程ID不变。
  • 常用的替换函数是 exec 系列函数(如 execl()execvp())。
  • 它常用于父进程通过 fork() 创建子进程后,子进程用 exec() 替换为新的程序来执行指定任务。
  • 替换后的进程将完全抛弃原来的代码和数据,并开始执行新加载的程序。

如果我文章对你有所帮助的话,点个关注吧~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-10-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.前言
  • 2.替换函数
    • 2.1 函数解释
      • 2.2 命名理解
        • 2.3 exec系列函数介绍
          • 2.3.1 execlp函数
          • 2.3.2 execle函数
          • 2.3.3 execv函数
      • 4. 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档