IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 【Linux进程】四、printf函数的缓冲区刷新机制与父子进程间的“读共享写拷贝”问题 -> 正文阅读

[系统运维]【Linux进程】四、printf函数的缓冲区刷新机制与父子进程间的“读共享写拷贝”问题

在这里插入图片描述


在这里插入图片描述


1. printf函数缓冲区刷新与C语言的 ‘\n’ 字符

我们先看一个简单的程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char* argv[])
{
	printf("begin...");
	fork();
	printf("end...\n");
	return 0;
}

运行后发现打印了两次begin,而根据前面的学习,实际上应该打印一次才对

在这里插入图片描述

实际上这是printf()函数缓冲区的机制造成的,缓冲区我们在Linux系统调用专题中已经讲过了。在系统调用时,遇到 ‘/n’ 输出行缓冲,我们这里第一个printf()函数中没有 ‘\n’ 字符,所以第一个printf()函数执行的时候没有打印缓冲区的内容,当我们fork一个子进程的时候,我们既没有输出这个缓冲区的内容,也没有刷新缓冲区,所以这段内容恢复至到子进程中。等到父子进程都执行到第二个printf()函数的时候,遇到 ‘\n’ 打印缓冲区内容,就把上一次和这一次的内容一块打印出来了。这也是为什么fork在第一个printf()语句之后,子进程却能打印出一个printf()语句中内容的原因,因为缓冲区没有刷新,所以被赋值给了子进程。这也告诉我们Linux和Windows是有区别的,在Linux下用pintf()函数一定要加 ‘\n’ 。

在这里插入图片描述

所以我们只要在第一个printf()语句中加上 ‘\n’ 字符就可以了。

在这里插入图片描述

2. 父子进程空间共享问题

执行fork()函数后,子进程与父进程有相同的全局变量、.data段、.text段、栈、堆、环境变量、用户ID、宿主目录、进程工作目录、信号处理方式等;不同之处在于,进程自己的ID、父进程ID、fork()函数返回值、进程运行时间(父进程在fork之前就已经运行了,而子进程在fork之后才开始运行)、定时器、未决信号集等不同。但是,子进程并不是直接把父进程0到3G的用户空间全部复制,而是遵循一种读时共享、写时复制这样的原则,这样无论是子进程执行父进程的逻辑,还是执行自己的逻辑都能节省内存开销。也就是说,父子进程的虚拟地址空间中,比如说数据段,它们都是指向同一块物理地址空间的,如果子进程只是读取该空间,那么就没必要复制这块物理内存,即读时共享,如果子进程要修改这块物理空间,那么将会复制一块物理空间然后修改复制的空间,即写时复制。

在这里插入图片描述

这里要注意,即便是全局数据,也遵循读时共享写时复制的原则,也就是说全局变量在父子进程之间也不是共享的。下面我们通过一个例子演示这种读时共享写时复制的原则。

/************************************************************
  >File Name  : shared_test.c
  >Author     : Mindtechnist
  >Company    : Mindtechnist
  >Create Time: 2022年05月19日 星期四 16时25分27秒
************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int g_data = 10;

int main(int argc, char* argv[])
{
	pid_t pid = fork();
	if(pid == 0)
	{
		printf("child: g_data = %d\n", g_data);
		g_data = 11;
		printf("child: g_data = %d\n", g_data);
		sleep(2);
		g_data = 13;
		printf("child: g_data = %d\n", g_data);
	}
	if(pid > 0)
	{
		sleep(1); /*1.保证printf时子进程已经修改全局变量 2.防止父进程提前结束*/
		printf("call: g_data = %d\n", g_data);	
		g_data = 12;
		printf("call: g_data = %d\n", g_data);
		sleep(2);
	}
	return 0;
}

编译运行,我们可以在打印结果中看到,当子进程修改全局变量的时候,父进程和子进程的全局变量值就可以使不再一样了,这就是写时复制,这时候,父子进程都有自己的g_data,修改的时候也是修改的自己的g_data的值。
在这里插入图片描述


在这里插入图片描述

在这里插入图片描述


  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-06-14 22:57:06  更:2022-06-14 23:00:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 1:42:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码