Fork函数

fork()的基础知识
父进程通过调用fork函数来创建一个新的运行的子进程。
父进程和子进程之间最大的区别就是PID不同
1)在父进程中,fork返回新创建子进程的PID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值
fork()的特点
调用一次,返回两次
一次只在调用进程(父进程)中,fork返回子进程的PID。
一次是在新创建的子进程中,fork返回0。

并发执行
父进程和子进程是并发运行的独立进程。
内核能够以任意方式交替执行他们的逻辑控制流中的指令。

相同但是独立的地址空间
父进程和子进程会有相同的用户栈、相同的本地变量值、相同的堆、全局变量以及代码。
但是,父进程和子进程都是独立的进程,他们都有自己的私有地址空间。

共享文件
子进程可以读写父进程中打开的任何文件

有关fork()代码分析
在看代码之前我们要了解一下有关 进程图 的知识:

进程图是刻画程序语句偏序的一种简单的前驱图,每个顶点a对应于一条程序语句的执行
有向边a—>b代表a发生在语句b之前,边上可以标记一些信息,例如一个变量的当前值。

画出 进程图 方便我们理解fork调用程序的情况。

1.几个基本的嵌套循环的例子

void fork1()
{
   int x = 1;
   pid_t pid = fork();

   if (pid == 0) {
   printf("Child has x = %d\n", ++x);
   } 
   else {
   printf("Parent has x = %d\n", --x);
   }
   printf("Bye from process %d with x = %d\n", getpid(), x);
}

运行结果和流程图:
在这里插入图片描述
子进程将x+1输出,父进程将x-1输出,二者的值没有相互影响,这是为什么呢?这个时候要提及到两个概念,一个是逻辑地址(虚拟地址),一个是物理地址。

逻辑地址:CPU所生成的地址。
根据内存管理和分页机制,CPU产生的逻辑地址分为:页号,它包含每个页在物理内存中的基址,用来做页表的索引;页偏移,同基址相结合,用来确定送入内存设备的物理内存地址。

物理地址:内存单元中存储数据的实际地址。
用户程序看不到物理地址。用户程序只生成逻辑地址。逻辑地址与物理地址呈现一一映射的关系。

fork函数会产生一个和父进程完全相同的子进程,一般情况下,子进程会调用exec函数族去执行新的程序,这个时候子进程就会有新的栈、堆、数据段和代码段。Linux系统经过不断发展,从效率的角度出发,引入了写时复制技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。

写时复制:
在fork之后exec之前两个进程用的是相同的物理空间(内存区),
子进程的代码段、数据段、堆栈都是指向父进程的物理空间,
也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。
当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,
如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),
而代码段继续共享父进程的物理空间(两者的代码完全相同)。
而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。

子进程在执行过程中改变了x的值,这个时候系统发现了这个操作,就会给子进程分配新(新指物理地址上的新)的栈、堆和数据段,并且将父进程中fork之前的变量拷贝一份(拷贝的值和父进程中的值就没有任何关系了),然后子进程再对这些值进行操作,所以二者虽然用的是相同的变量名,但是在物理地址上已经完全不相同了。

/*
 * fork2 - Two consecutive forks
 * Both parent and child can continue forking
 * Ordering undetermined
 */
void fork2()
{
    printf("L0\n");
    fork();
    printf("L1\n");    
    fork();
    printf("Bye\n");
}
dida@dida-VirtualBox:~/code$ ./f
L0
L1
Bye
dida@dida-VirtualBox:~/code$ Bye
L1
Bye
Bye

在这里插入图片描述
.使用waitpid函数
在介绍waitpid函数之前,我们先来了解一下有关 回收子进程 的相关知识

回收子进程
当一个进程由于某种原因终止时,内核并不是立即把他从系统中清除。相反,进程被保持一种已终止状态中,直到被他的父进程回收。一个终止了但还未被回收的进程称为僵死进程。
即使僵死子进程没有运行,他们仍然消耗系统的内存资源。
如果父进程没有回收它的僵死进程就终止了,那么内核会安排init进程去回收他们。
但是,一些长时间运行的程序,例如shell或服务器,总应该回收他们的僵死子进程,不然会对系统造成不小的负担。
所以,如何使我们的父进程来等待它的子进程终止或者停止,来将僵死子进程所占用的内存资源释放掉呢?

这时就需要我们的waitpid函数登场了,首先我们先来看下他的函数结构

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *statusp, int options);
``
我们这里仅对第一个参数pid进行解释,另外两个请大家自行百度

如果pid>0,那么等待集合就是一个单独的子进程,它的进程ID等于pid。
如果pid=-1,那么等待集合就是由父进程所有的子进程组成的。
我们可以通过控制pid的值来指定子进程进行回收,这也是与wait函数的区别。大家可以自行运行一下代码中的fork10和fork11观察下输出有何不同。

.使用fflush清除缓存
我们先来看一下fflush的函数结构

#include<stdio.h>
int fflush(FILE * stream);

函数说明:

fflush()会强迫将缓冲区内的数据写回参数stream指定的文件中,如果参数stream为NULL,fflush()会将所有打开的文件数据更新。
返回值:成功返回0,失败返回EOF,错误代码存于errno中。
fflush()也可用于标准输入(stdin)和标准输出(stdout),用来清空标准输入输出缓冲区。
stdin是standard input的缩写,即标准输入,一般是指键盘;标准输入缓冲区即是用来暂存从键盘输入的内容的缓冲区。
stdout是standard output 的缩写,即标准输出,一般是指显示器;标准输出缓冲区即是用来暂存将要显示的内容的缓冲区。
我们在fork函数中要注意的也就是输入输出缓冲区。

include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int i;
for (i=0; i<2; ++i) {
fork();
printf(“A”);
//fflush(stdout);
//printf("\n");
}
return 0;


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值