名片型网站开发,中国加盟网,四川网站建设制作,做电影类网站什么是进程替换#xff1f;
我们的可执行程序#xff0c;在运行起来的时候就上一个进程
一个进程就会有他的内核数据结构代码和数据
把一个已经成型的进程的代码和数据替换掉#xff0c;这就叫进程替换
也就是可以通过系统调用把当前进程替换位我们需要的进程
那么替换…什么是进程替换
我们的可执行程序在运行起来的时候就上一个进程
一个进程就会有他的内核数据结构代码和数据
把一个已经成型的进程的代码和数据替换掉这就叫进程替换
也就是可以通过系统调用把当前进程替换位我们需要的进程
那么替换后会创建一个新进程吗
不会只是在旧进程的壳子执行新进程替换进程后之前的代码不会执行因为已经被替换了
进程怎样替换
进程替换是需要接口函数的总不能什么都没有直接替换吧
来看看都有什么 上图的【int execl(const char *pfin,“const char *arg, ...);】这种后面的省略号是指参数可变 来举个栗子捏
test1.c
#includestdio.h
#includeunistd.hint 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.hint 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(rid0)分支里先打印了一下然后进入我们的接口接口里是我们的ls命令我们ls命令也是文件也是C语言写的我们平时执行命令也是执行该程序所以可以在接口里替换为ls命令 这样就是替换成功了
我们刚刚说了替换失败会继续执行原进程来看看吧
#includestdio.h
#includeunistd.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hint 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
来先写一个自己的程序
#includeiostreamusing namespace std;int main()
{cout hello C! endl;cout hello C! endl;cout hello C! endl;cout hello C! endl;return 0;
}
然后再也一个父进程
#includestdio.h
#includeunistd.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hint 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 $ $^ -stdc11testexec:testexec.cgcc -o $ $^
.PHONY:clean
clean:rm -f testexec mypragma
来看看 程序替换并未形成新进程我们来验证一下
test1.c:
#includestdio.h
#includeunistd.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hint 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
#includeiostream
#includeunistd.h
#includestdlib.h
#includesys/types.h
#includesys/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:
#includestdio.h
#includeunistd.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hint 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*)LIKE521,(char*)LOVE1314,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:
#includeiostreamusing 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()设置新的环境变量这会覆盖原本的环境变量。
用法
#includestdio.h
#includeunistd.h
#includestdlib.h
#includesys/types.h
#includesys/wait.h
int main(){char *p;if((p getenv(USER)))printf(USER %s\n, p);putenv(USERtest);printf(USER5s\n, getenv(USER));}这些都是
#include unistd.hint 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是真正的系统调用,其它五个函数最终都调用 execveexecve在man手册第2节,其它函数在man手册第3节