当前位置: 首页> 教育> 就业 > Linux:进程替换

Linux:进程替换

时间:2025/7/11 12:05:36来源:https://blog.csdn.net/Au_ust/article/details/141339276 浏览次数:0次

什么是进程替换?

我们的可执行程序,在运行起来的时候就上一个进程

一个进程就会有他的内核数据结构+代码和数据

把一个已经成型的进程的代码和数据替换掉,这就叫进程替换

也就是可以通过系统调用把当前进程替换位我们需要的进程

那么替换后,会创建一个新进程吗?

不会,只是在旧进程的壳子执行新进程;替换进程后,之前的代码不会执行,因为已经被替换了

进程怎样替换?

进程替换是需要接口函数的,总不能什么都没有直接替换吧

来看看都有什么:

上图的【int execl(const char *pfin,“const char *arg, ...);】这种后面的省略号,是指参数可变

来举个栗子捏:

test1.c

#include<stdio.h>
#include<unistd.h>int main()
{printf("test1.c ... begin!\n");execl("/usr/bin/ls", "ls", "-l", "-a", NULL);printf("test1.c ... end!\n");return 0;
}

makefile:

test1.out:test1.cgcc -o $@ $^
.PHONY:clean
clean:rm -f test1.out

解释一下:就是把我们本来的代码通过execl来执行execl引用的程序,成为替换的新进程

替换的过程?

而execl的返回值我们并不关心,因为一旦替换成功,就不会向后继续运行旧进程了

而只要继续运行旧进程,那就一定是替换失败了

所以我们的替换接口函数只有失败返回值,没有成功返回值

替换的过程本质上是把这个新程序加载到内存上

一个程序怎样加载到内存上呢? 

别忘了exec*!它类似一种Linux上的加载函数,做的是从外设拷贝到内存是操作系统的活
 

多进程版的进程替换

将代码改成多进程版就是fork创建子进程,让子进程自己替换,父进程只需要等待就好了

也就是说这里被替换的是子进程捏,父进程还在等待

 test1.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){printf("exec begin\n");//进程替换execl("/usr/bin/ls", "ls", "-al", NULL);printf("exec end\n");exit(0);}int rid = waitpid(id, NULL, WUNTRACED);if(rid > 0){printf("wait success\n");}return 0;
}

makefile:

test1.out:test1.cgcc -o $@ $^
.PHONY:clean
clean:rm -f test1.out

解释一下:我们在if(rid==0)分支里,先打印了一下,然后进入我们的接口,接口里是我们的ls命令,我们ls命令也是文件,也是C语言写的,我们平时执行命令也是执行该程序,所以可以在接口里替换为ls命令

这样就是替换成功了

我们刚刚说了替换失败会继续执行原进程,来看看吧:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{printf("test1.c ... begin!\n");pid_t id = fork();if (id == 0){sleep(2);execl("/usr/bin/lsss", "lsss", "-l", "-a", NULL);printf("替换失败捏");exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));}printf("test1.c ... end!\n");return 0;
}

你看,没执行execl里面的程序

以前创建子进程让它完成任务,是让子进程执行父进程代码的一部分,现如今可以让子进程执行一个全新的程序

进程有对应的数据和代码,创建子进程会有新的地址空间和页表;替换本身就是覆盖,子进程在数据部分进行写时拷贝,当执行新程序的时候新程序也要进行写时拷贝,所以此后父子间几乎完全独立(本来数据和代码是继承父进程的,现在是全新的了)

来仔细介绍一下我们的替换接口函数:

这些函数的前四个字母都是exec,后面跟着的:

 p: 表示自动搜索路径e: 表示自己维护环境变量l:表示采用列表传参v:表示采用数组传参

execl

int execl(const char *path, const char *arg, ...);

l就是list(列表),传参数列表

剩下的...带选项,在命令行中怎样写命令的参数,就怎么写(下图有例子捏)结束时传NULL

路径表明我们想要执行谁后面的选项表示我们想要怎样执行

execv

v是vector的意思,动态数组

int execv(const char *path, char *const argv[]);

前面的参数是路径,后面的参数是指针数组,execl的是可变参数,execv的传参需要传指针数组指针数组是自己写的

 execvp
带p的意思就是用户可以不用传要执行文件的路径(但要传文件名)

char *const argv[] = {"ps", "-ef", NULL};
execvp("ps", argv);

那它是怎么做到的呢?

别忘了p是可以自己搜路径,在环境变量里搜路径

execlp
就是采用列表传参,但是不用输路径

execlp("ps", "ps", "-ef", NULL);
execvpe

execvpe
带e的需要自己组装环境变量

int execvp(const char *file, char *const argv[], char *const envp[]);

以替换  "ls -al" 为例,示例一下剩余接口:

//l代表传参数列表,分成一个个字符串传
execl("/usr/bin/ls", "ls", "-al", NULL);//p表示系统会去PATH环境变量中找
execlp("ls", "ls", "-al", NULL);//v代表传数组,把命令分成字符串,放进字符串数组里,一起传
char* argv[] = {"ls", "-al", NULL};
execv("/usr/bin/ls", argv);

 替换自己写的进程

刚刚我们引用了系统的进程,我们也可以引用我们自己写的程序

下面就来替换段C++代码(文件后缀: .cpp , .cc ,.cxx)

来先写一个自己的程序:

#include<iostream>using namespace std;int main()
{cout << "hello C++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;return 0;
}

然后再也一个父进程:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{printf("test1.c ...begin!\n");pid_t id = fork();if (id == 0){sleep(2);execl("./mypragma", "mypragma", NULL);exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){printf("father wait success,child exit code:%d\n", WEXITSTATUS(status));}printf("test1.c ... end!\n");return 0;
}

tips:别忘了makefile默认是从上到下形成可执行程序,所以第一个要是主程序,其他的依赖程序依次往下排哦

.PHONY:all
all:testexec mypragmamypragma:mypragma.ccg++ -o $@ $^ -std=c++11testexec:testexec.cgcc -o $@ $^
.PHONY:clean
clean:rm -f testexec mypragma

来看看:

程序替换并未形成新进程,我们来验证一下:

test1.c:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{printf("test1.c ...begin!\n");pid_t id = fork();if (id == 0){ printf("i am a process,pid:%d\n",getpid());sleep(2);execl("./mypragma", "mypragma", NULL);exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){printf("father wait success,child exit code:%d\n", WEXITSTATUS(status));}printf("test1.c ... end!\n");return 0;
}

mypragma.cpp

#include<iostream>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;int main()
{printf("i am a new process,pid:%d\n",getpid());cout << "hello C++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;return 0;
}

看,这两个进程pid是一样的

自己组装环境变量

我们刚刚说环境变量可以自己组装,来引用

看看吧:

test1.c:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{printf("test1.c ...begin!\n");pid_t id = fork();if (id == 0){sleep(2);char *const argv[] = { (char*)"mypragma",NULL };char* const envp[] = { (char*)"LIKE=521",(char*)"LOVE=1314",NULL };execvpe("./mypragma", argv, envp);exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){printf("father wait success,child exit code:%d\n", WEXITSTATUS(status));}printf("test1.c ... end!\n");	return 0;
}

mypragma.cpp:

#include<iostream>using namespace std;int main(int argc,char *argv[],char *env[])
{int i = 0;for (; argv[i]; i++){printf("argv[%d]: %s\n", i, argv[i]);}printf("-------------------------------\n");for (i = 0; env[i]; i++){printf("env[%d]: %s\n", i, env[i]);}printf("-------------------------------\n");cout << "hello C++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;return 0;
}

哦?

父进程本身自己的环境变量,来自于父进程的父进程:bash

你也可以用全新的环境变量,也可以用修改后的环境变量:使用putenv()

往进程加环境变量: putenv() ,程序替换不会替换环境变量,我们可以通过带e的接口函数(例如execpe())设置新的环境变量,这会覆盖原本的环境变量。

用法:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(){char *p;if((p = getenv("USER")))printf("USER =%s\n", p);putenv("USER=test");printf("USER+5s\n", getenv("USER"));}

这些都是:

#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[]);

tips:只有execve是真正的系统调用,其它五个函数最终都调用 execve(execve在man手册第2节,其它函数在man手册第3节)

关键字:Linux:进程替换

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: