进程呼叫转移
进程不会消失,只会转移
把一个进程的温暖转移到另一个的胸膛~
让上个进程犯的错反省出梦想~
不扯皮了,来说进程替换
进程替换
现象
先来看看怎么个事吧:
有个参数:...,是表示参数可变的意思
写段代码来解释下:
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("testexec ... begin!\n");
execl("/usr/bin/ls", "ls", "-l", "-a", NULL);
printf("testexec ... end!\n");
return 0;
}
我们会发现明明是自己的程序在跑,为什么执行起ls命令嘞
因为之前提过Linux设计的理念:一切皆文件
而ls命令本身也是拿C写的,在那个目录下的一个可执行程序
我们(进程)调用了execl函数,而这个函数可以帮助我们执行新的程序
原理
那到底怎么回事呢?
我们自己的可执行程序,在./运行之后就会变成进程
进程 = 内核数据结构 + 代码和数据
而进程程序替换就是把一个进程的代码和数据换掉
那么在替换的过程中有没有创建新进程呢?
并没有哦,只是拿老进程的壳子执行新进程的代码罢了
而站在被替换进程的角度,本质是这个程序被加载到内存了
可是一个程序怎么加载到内存啊?
别忘了exec*!它类似一种Linux上的加载函数
从外设拷贝到内存是操作系统的活
我们还会发现一个现象,就是后面并没再打印结束的函数了
这正常吗?
正常耶
exec*系列函数执行完毕之后代码就被替换啦
而execl的返回值我们并不关心,因为一旦替换成功,就不会向后继续运行了
而只要继续运行了,那就一定是替换失败了
多进程
将代码改成多进程版就是fork创建紫禁城,让紫禁城自己替换,父进程只需要等待就好了
看看失败的:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
printf("testexec ... begin!\n");
pid_t id = fork();
if (id == 0)
{
sleep(2);
execl("/usr/bin/lsss", "lsss", "-l", "-a", 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("testexec ... end!\n");
return 0;
}
紫禁城退出成功,退出码为1,替换失败
成功的是这样的:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
printf("testexec ...begin!\n");
pid_t id = fork();
if (id == 0)
{
sleep(2);
execl("/usr/bin/ls", "ls", "-l", "-a", NULL);
exit(1);
}
//father
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("testexec ... end!\n");
return 0;
}
(写路径的时候不要忘记根目录,我就是落了个/查半天...)
以前创建紫禁城让它完成任务,是让紫禁城执行父进程代码的一部分,现如今可以让紫禁城执行一个全新的程序
程序有对应的数据和代码(从可执行程序来),创建紫禁城会有新的地址空间和页表
父子进程数据共享但是有独立性
替换本身是覆盖,紫禁城在数据部分进行写时拷贝,当执行新程序的时候新程序也要进行写时拷贝,所以此后父子间几乎完全独立(在数据结构和代码层面完全分离)
替换方法及函数参数含义
execl
上面说过一个替换方法:execl
int execl(const char *path, const char *arg, ...);
康康 l 嘛意思:
l类似list,列表,如果参数为path,就需要带路径(别忘根目录,使用函数的人需要填)
剩下的...带选项,在命令行中怎样传参(依次传参很像list捏)就怎么写,结束时传NULL
路径表明我们想要执行谁,后面的选项表示我们想要怎样执行
execv
v是什么意思。。。很明显是vector总共就那点容器
int execv(const char *path, char *const argv[]);
前面的是路径,后面的是指针数组,execl的是可变参数,execv的传参需要传指针数组(自己构建)
char *const argv[] = {"ps", "-ef", NULL};
execv("/bin/ps", argv);
execvp
带p的意思就是用户可以不用传要执行文件的路径(但要传文件名)
char *const argv[] = {"ps", "-ef", NULL};
execvp("ps", argv);
那它是怎么做到的呢?
p:查找这个程序,系统会自动在环境变量PATH中进行查找
execlp
和上面规律差不多,自己悟去
execlp("ps", "ps", "-ef", NULL);
execvpe
带e的需要自己组装环境变量
int execvp(const char *file, char *const argv[], char *const envp[]);
上面的程序替换,替换的就是系统命令,所以肯定也可以替换我们自己写的程序
下面就来替换段C嘎嘎代码(文件后缀: .cpp , .cc ,.cxx)
tips:别忘了makefile默认是从上到下形成可执行程序,所以第一个要是主程序,其他的依赖程序依次往下排哦
举个栗子,可以这样干:
.PHONY:all
all:testexec mypragma
mypragma:mypragma.cc
g++ -o $@ $^ -std=c++11
testexec:testexec.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f testexec mypragma
这是testexec.c:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
printf("testexec ...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("testexec ... end!\n");
return 0;
}
这是pragma:
#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;
}
程序替换并未形成新进程,可以通过这个验证下,但是懒得验证啊都会吧应该唉算了还是替换下
这是mypragma.cc:
#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{
printf("I am a process,pid:%d\n", getpid());
cout << "hello C++!" << endl;
cout << "hello C++!" << endl;
cout << "hello C++!" << endl;
cout << "hello C++!" << endl;
return 0;
}
这是testexec.c:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
printf("testexec ...begin!\n");
pid_t id = fork();
if (id == 0)
{
sleep(2);
printf("I am a process,pid:%d\n", getpid());
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("testexec ... end!\n");
return 0;
}
makefile和之前一样不多说咯
哦对替换不止可以c和cpp,Python啥的也行呢,会就写呢
我写了一篇大概的Python基础语法:
我与Python的一夜情-CSDN博客https://blog.csdn.net/chestnut_orenge/article/details/139599983这样运行(Python不是编译型语言):
python3 test.py
说过了Python3和Python2是运行环境,然后pycharm是集成环境
就写了段简单的代码然后可以运行这样子:
s1 = "hello"
s2 = "world"
print(s1 + s2)
Python虽然不用编译(其他脚本语言也差不多都这样,Java也是半解释语言),但是它有解释器,解释器本身使用C和C++写的:
然后刚不是说替换也可以替换成其他语言,看个Python的(因为我不会shell脚本编写):
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
printf("testexec ...begin!\n");
pid_t id = fork();
if (id == 0)
{
sleep(2);
printf("I am a process,pid:%d\n", getpid());
execl("/usr/bin/python3", "python3","test.py", 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("testexec ... end!\n");
return 0;
}
有一种,殊途同归,的感觉
不是等会不是说要说环境变量我怎么跑这么远
言归正传:
环境变量怎么传!这样传(先试试):
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
printf("testexec ...begin!\n");
pid_t id = fork();
if (id == 0)
{
sleep(2);
char* const argv[] = { (char*)"mypragma",NULL };
execvpe("./mypragma", argv, 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("testexec ... end!\n");
return 0;
}
咳咳传空不是传啊
看看真的:
testexec.c:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
printf("testexec ...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("testexec ... end!\n");
return 0;
}
mypragma.cc:
#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~
可以这样获取爷进程Bash的环境变量,直接传:
extern char**environ;
execvpe("./mypragma" , argv, environ);
传参如果传自定义的,那就整体替换环境变量嘞
传环境变量可以用全新的,还可以用老的,如果老的想要修改之后再给紫禁城就可以这样:
这是putenv,它能帮你改改再传
putenv("LOVE=1314"); //直接用就行,它自己会添加的
然后总结归类下 :
这是替换函数
#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[]);
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回
如果调用出错则返回-1 所以exec函数只有出错的返回值而没有成功的返回值
这是助记:
l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量
这是使用举例 :
#include <unistd.h>
int main()
{
char *const argv[] = {"ps", "-ef", NULL};
char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-ef", NULL);
// 带p的,可以使用环境变量PATH,无需写全路径
execlp("ps", "ps", "-ef", NULL);
// 带e的,需要自己组装环境变量
execle("ps", "ps", "-ef", NULL, envp);
execv("/bin/ps", argv);
// 带p的,可以使用环境变量PATH,无需写全路径
execvp("ps", argv);
// 带e的,需要自己组装环境变量
execve("/bin/ps", argv, envp);
exit(0);
}
tips:只有execve是真正的系统调用,其它五个函数最终都调用 execve(execve在man手册第2节,其它函数在man手册第3节)
拜拜~
UNO很好玩,串串很好吃,蛇蛇很好摸
原文地址:https://blog.csdn.net/chestnut_orenge/article/details/140567959
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!