linux下利用fork创建进程,进程运行内存说明,与同时创建多个进程的方法及分析
fork的应用及同时创建多个进程的分析1 进程及进程的创建1 进程2 利用fork创建进程2 利用fork同时创建多个进程3 创建多个进程的代码分析2级标题3级标题四级标题五级标题六级标题1 进程及进程的创建在linux编程中,用来创建用户进程的函数时fork。首先来说明什么是进程。1 进程什么是进程,引用百度百科的说明:进程(Process)是计算机中的程序关于某数据集合上的一次运行...
fork的应用及同时创建多个进程的分析
1 进程及进程的创建
在linux编程中,用来创建用户进程的函数时fork。首先来说明什么是进程。
1 进程
什么是进程,引用百度百科的说明:
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
通俗的说,一个程序在计算机中运行起来了,就是一个进程。
打个比方:程序好比郭德纲老师相声的剧本,而进程就好比郭德纲老师在台上表演。进程的生命周期就是郭德纲老师从上台开始表演,到表演结束。
2 fork函数
在创建进程之前,需要linux环境安装了gcc。
首先认识fork(),在linux中用man fork
查看说明,由于说明很多,这里就大概截取一点,具体的可以自行查阅man
说明文档
从图1的说明文档中可以知道,fork函数的功能是创建一个子进程,需要包含的头文件是#include<sys/types.h> #include <unistd.h>
,没有参数。还有很多描述,比如子进程拥有独立的进程ID,父子进程运行在独立的内存中等等,可以具体读下man
的说明文档。
从图2,可以看出返回值,如果是父进程返回值为创建的子进程的PID;如果是子进程,返回为0;如果创建失败,返回-1。我们可以通过这一点来判断当前进程是父还是子。
3 进程运行内存说明
在图1中我们看到,父子进程运行的内存是隔离的,也就父子进程运行在相互独立的内存中。
当子进程被创建出来的时候,相应的子进程的内存空间也被创建出来了,仿佛克隆了一份父进程的内存空间。
如果是运行在32位的机子上,那么fork后,内存如下图,
当fork()后,子进程仿佛copy一份父进程的内存空间,作为自己的内存空间。具体有以下内容:
全局变量,.text段,.rodata段,.data段,.bss段,heap,stack,env(环境变量),用户ID, 进程工作目录,信号处理方式等。
那我们计算机的内存都是固定的,比如4G/8G/16G等等。进程的内存如果是独立的,如果有很多进程,怎么够用呢?
哈哈,其实创建子进程的时候不是真正的copy了一份内存空间,而是遵循了读书共享写时复制
的原则,这样就可以节省内存开销了。太优秀了!
其实进程用到的虚拟内存到物理内存的映射关系,还涉及到MMU内存管理单元
,他实现了我们物理内存到虚拟内存的映射。这里边的关系,后面我单独写个博客来分析下,这里只需要知道父子进程之间内存是相互独立的,以及父子进程遵循读时共享写时复制
的原则。
4 利用fork()创建进程
这个很简单,直接上code
// creat_process.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
pid_t pid;
pid = fork();
// if creat fail
if (pid == -1) {
perror("creat child process fail!\n");
exit(1);
}
// 利用返回值来判单是父还是子进程
if (pid == 0){
printf("I am the child, pid:%d\n", getpid());
} else {
printf("I am the parent, pid:%d\n", getpid());
}
return 0;
}
直接利用fork函数来创建子进程。
通过返回值的不同来判断是父进程还是子进程。依据上面图2中返回值得说明。
用gcc编译
gcc creat_process.c -o creat_process
然后执行结果如下:
从结果看,有父进程也有子进程,函数功能ok。
2 利用fork同时创建多个进程
1 错误的方法及分析
如果要创建多个进程,我们很容易想到的就是,直接加个for循环,就可以了。那么很容易想到的写法:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
printf("this is a fork test code\n");
int i;
for (i = 0; i < 5; i++) {
pid = fork();
if (pid == 0) {
printf("i am %d child, pid = %u\n", i + 1, getpid());
} else {
printf("I am parent, pid = %u\n", getpid());
}
}
return 0;
}
我们编译执行下:
hxf@hxf-VirtualBox:thread$ gcc fork_test.c -o fork_test
hxf@hxf-VirtualBox:thread$ ./fork_test
this is a fork test code
I am parent, pid = 26654
I am parent, pid = 26654
I am parent, pid = 26654
I am parent, pid = 26654
I am parent, pid = 26654
i am 1 child, pid = 26655
I am parent, pid = 26655
I am parent, pid = 26655
hxf@hxf-VirtualBox:thread$ I am parent, pid = 26655
I am parent, pid = 26655
i am 2 child, pid = 26660
I am parent, pid = 26660
I am parent, pid = 26660
I am parent, pid = 26660
i am 3 child, pid = 26661
I am parent, pid = 26661
I am parent, pid = 26661
i am 4 child, pid = 26662
I am parent, pid = 26662
i am 2 child, pid = 26656
i am 5 child, pid = 26663
I am parent, pid = 26656
I am parent, pid = 26656
i am 5 child, pid = 26666
I am parent, pid = 26656
i am 4 child, pid = 26671
i am 3 child, pid = 26657
I am parent, pid = 26657
i am 5 child, pid = 26669
I am parent, pid = 26657
i am 5 child, pid = 26672
I am parent, pid = 26671
i am 4 child, pid = 26665
I am parent, pid = 26665
i am 3 child, pid = 26664
I am parent, pid = 26664
I am parent, pid = 26664
i am 5 child, pid = 26676
i am 4 child, pid = 26673
I am parent, pid = 26673
i am 5 child, pid = 26674
i am 4 child, pid = 26658
i am 4 child, pid = 26677
i am 5 child, pid = 26675
I am parent, pid = 26677
i am 5 child, pid = 26680
i am 5 child, pid = 26659
i am 3 child, pid = 26670
I am parent, pid = 26670
I am parent, pid = 26670
I am parent, pid = 26658
i am 5 child, pid = 26682
i am 5 child, pid = 26668
i am 4 child, pid = 26667
i am 5 child, pid = 26679
i am 5 child, pid = 26683
i am 5 child, pid = 26678
I am parent, pid = 26667
i am 4 child, pid = 26681
I am parent, pid = 26681
i am 5 child, pid = 26684
i am 5 child, pid = 26685
发现根本不对,出错在哪里呢?
首先我们想要的结果是,由父进程创建5个子进程,如下图:
哈哈,这里其实是没有理解创建子进程后,子进程和父进程是同时存在的,并且子进程是从创建的那一刻开始,拥有了父进程的执行程序的一切,比如全局变量,.text段,.rodata段,.data段,.bss段,heap,stack,env(环境变量) 等。所以其实当第一个子进程创建出来后,同样会继续执行fork(),所以子进程会创建出自己的子进程,这个时候子变成了父,生出了自己的子
。这样,子生孙,孙生重孙,无穷尽也。
分析上面的结果,我们发现如下规律:
i am 1 child 出现了`1`次 = (2^0)
i am 2 child 出现了`2`次 = (2^1)
i am 3 child 出现了`4`次 = (2^2)
i am 4 child 出现了`8`次 = (2^3)
i am 5 child 出现了`16`次 = (2^4)
I am parent 出现了`31`次 = (2^0) + (2^1) + (2^2) + (2^3) + (2^4) 所有子进程之和
所以每生成一个子进程,就会对应一个父进程。也就是子必有父(感觉像废话,哈哈)。
上面的代码其实是创建了 2 * (1 + 2 + 4 + 8 + ... + 2^(N-1))
个进程。并不是我们想要的N
个进程。
这里用N = 3(上面的例子N=5,5太多啦,这里展示下过程)作为例子画下上面的创建过程:
嗯,这个图,就一目了然了。
2 正确的方法
所以正确的做法是,不要让子再生子了,方法如下:
//fork.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
printf("this is a fork test code\n");
int i;
for (i = 0; i < 5; i++) {
pid = fork();
//不让子再生子了
if (pid == 0) {
break;
}
}
if (i < 5) {
sleep(i);
printf("i am %d child, pid = %u\n", i + 1, getpid());
} else {
sleep(i);
printf("I am parent, pid = %u\n", getpid());
}
return 0;
}
编译然后执行:
从结果看,可以达到我们想要的结果的。
功成!
更多推荐
所有评论(0)