[Linux]fork与exec

写这篇文章的原因是看到了一段与 Linux 下进程复制有关的代码,感觉很神奇,不甚理解,所以找了一些相关的资料想要弄明白 Linux 的 fork、进程复制到底是怎么工作的,于是有了这篇文章。
那段代码是这样的:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <wait.h>
int main() {
	int pid, status;
	pid = fork();
	if (pid == 0) {
		// /bin/echo是 Linux 自带的 echo 命令的源代码所在的位置
		char *argv[] = {"/bin/echo", "THIS","MESSAGE", "IS", "FROM", "ECHO", NULL};
		execve("/bin/echo", argv, NULL);
		printf("exec failed!\n");
		exit(1);
	} else {
		printf("parent wating!\n");
		wait(&status);
		printf("the child exited with status %d \n", status);
	}
	exit(0);
}
(base) root@iZuf65cax8rsfekcyp3ytyZ:~/testfork# gcc tfork.c -o test
(base) root@iZuf65cax8rsfekcyp3ytyZ:~/testfork# ./test
parent wating!
THIS MESSAGE IS FROM ECHO
the child exited with status 0 

上面的代码是调用了系统的 echo 命令,如果我们把命令换成一个不存在的命令,那么就会报错,我们来修改一下代码生成 errorfork.c :

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <wait.h>
int main() {
	int pid, status;
	pid = fork();
	if (pid == 0) {
		// 当前目录下没有名为 echo 的文件,所以 execve 会执行失败
		char *argv[] = {"echo", "THIS","MESSAGE", "IS", "FROM", "ECHO", NULL};
		execve("echo", argv, NULL);
		printf("exec failed!\n");
		exit(1);
	} else {
		printf("parent wating!\n");
		wait(&status);
		printf("the child exited with status %d \n", status);
	}
	exit(0);
}
(base) root@iZuf65cax8rsfekcyp3ytyZ:~/testfork# gcc errorfork.c -o errorfork
(base) root@iZuf65cax8rsfekcyp3ytyZ:~/testfork# ./errorfork
parent wating!
exec failed!
the child exited with status 256 

大家可以用在线C语言编译器 来做一下这个实验试试看。
我当时看到这个实验觉得有两点很不能理解:

  • fork() 函数调用返回了两个值,if 和 else 子句都会被执行,超出认知了,感觉很神奇。
  • 如果 execve 函数执行成功了,这个函数后面的代码就不会再被执行了,只有失败了就会继续执行下去,超出认知了,为什么一个函数调用,如果成功了它就不会回到它的原始调用点了?

基于这两个疑惑我找了一些资料,了解了 fork 的运行机制,总结成了这篇文章。

初识 fork

fork 是叉子的意思,进程复制函数为什么要叫 fork 呢,这就要从 fork 的起源说起了
image
fork 的思想最初是 Conway 作为一种多处理器并行的方案提出来的,这个想法非常有意思。简而言之,fork 思想来源于流程图。看上面这张流程图,从 fork 点开始产生分叉,产生了一个新进程,可以和原来的进程并行执行,就像叉子一样。
先来解决第一个疑惑,为什么 fork 函数会有两个返回值, if 和 else 语句都会被执行到?因为 fork 函数是把父进程完全复制了一遍,所以复制出来的子进程它下一步应该执行的就是 if 语句, 父进程要执行的代码一样,只是他俩的 pid 不一样,所以进入了不同的选择分支,也就是说,新进程的代码段寄存器、数据段寄存器、堆栈寄存器和指令指针寄存器里的内容都和父进程一样,父进程的指令指针指向了 pid = fork() 这句代码,它接下来要执行这句代码下面的后续代码,同样,子进程的指令指针也指向了这句代码,只是 fork 的返回值不一样,所以子进程后续会进入 pid == 0 的分支,而父进程会进入另一个分支。
再来解决第二个疑惑,为什么执行 exec 函数成功之后就不回到原来的函数调用点了?因为 exec 把所要执行的新的函数代码加载到了这个子进程里,子进程原始的代码就无了,自然也不会回到调用 exec 的地方了。

posted @ 2023-02-01 15:14  2235854410  阅读(52)  评论(0编辑  收藏  举报