解析进程复制:父子进程内存地址的神秘之处

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 解析进程复制:父子进程内存地址的神秘之处

当涉及到进程复制时,有时会出现一个令人困惑的现象:在父进程和子进程中,某些变量的内存地址似乎是相同的,尽管它们实际上是独立的进程。下面我将简单解释这个现象以及背后的原因。

进程复制:父子进程的神秘关系

在多进程编程中,使用 `fork()` 函数可以创建一个新的子进程。这个子进程是父进程的几乎完美副本,包括代码、数据和资源。这就是为什么在子进程中有时会看到父进程的某些变量具有相同的内存地址的原因。

为什么内存地址相同?

这种现象的原因在于虚拟内存系统。虚拟内存允许操作系统为每个进程创建一个独立的虚拟地址空间,使得每个进程都认为它在使用整个计算机的内存。但实际上,物理内存被分割和共享。

当调用 `fork()` 创建子进程时,操作系统并不会复制整个物理内存。相反,它会为子进程创建一个新的页表,该页表将虚拟地址映射到相同的物理内存页面。这就是为什么子进程看起来拥有与父进程相同的内存地址。

数据独立性

虽然父子进程共享相同的物理内存页面,但它们的数据是独立的。这意味着当一个进程修改共享页面上的数据时,不会影响到另一个进程。因为每个进程都有自己的页表,操作系统会根据页表来决定虚拟地址如何映射到物理内存。

进程间通信

尽管父子进程拥有相同的内存页面,但它们通常需要使用进程间通信(IPC)机制来实现数据共享。这可以通过管道、共享内存、消息队列等方式来实现。IPC 机制允许这两个进程在独立的虚拟地址空间中交换数据,确保数据的一致性和安全性。

结论

进程复制是多进程编程的常见技术,它允许创建独立运行的子进程,但在虚拟内存背后,这两个进程共享相同的物理内存页面。这种现象可以解释为什么父进程和子进程有时会看到某些变量的内存地址是相同的,尽管它们实际上是独立的进程。要确保数据的正确性和安全性,需要使用适当的 IPC 机制来实现进程间通信。对于开发者来说,了解虚拟内存系统和进程复制机制是编写稳健多进程应用程序的关键。

demo案例

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) 
{
  int fd[2];
  int ret;
    int status;
  //创建了两个字符数组 buff1 和 buff2,用于存储从父进程到子进程和从子进程到父进程的信息。
  char buff1[1024];
  char buff2[1024];
  pid_t pd;
  //调用 pipe(fd) 创建了一个管道,其中 fd 是一个包含两个文件描述符的数组,fd[0] 用于读取数据,fd[1] 用于写入数据。
  ret = pipe(fd);
  if (ret !=0) {
    printf("create pipe failed!\n");
    exit(1);
  }
  pd = fork();//调用 fork() 创建了一个子进程。如果 fork() 失败,程序将打印错误消息并退出。
  if (pd == -1) {
    printf("fork error!\n");
    exit(1);
  } else if (pd == 0) {//在子进程中(当 pd == 0 时),首先清空了 buff2,然后从管道的读取端 fd[0] 读取数据到 buff2 中。接着,子进程将一条信息写入管道的写入端 fd[1] 中,然后打印发送的信息和缓冲区地址。
    bzero(buff2, sizeof(buff2));
    read(fd[0], buff2, sizeof(buff2));
    printf("process(%d) received information[child-buff2]:%s  [%p]\n", getpid(), buff2,buff2);
    strcpy(buff1,"Hello!parent!");
        write(fd[1],buff1,strlen(buff1));
    printf("process(%d) send information[child-buff1]:%s  [%p]\n", getpid(), buff1,buff1);
  } else {//在父进程中(当 pd > 0 时),首先将一条信息写入管道的写入端 fd[1] 中,然后睡眠了5秒钟。接着,从管道的读取端 fd[0] 读取数据到 buff2 中,然后打印接收的信息和缓冲区地址。
    strcpy(buff1, "Hello!child");
    write(fd[1], buff1, strlen(buff1)); 
    printf("process(%d) send information[parent-buff1]:%s [%p]\n", getpid(), buff1,buff1);
    sleep(5);
        read(fd[0],buff2,sizeof(buff2));
    printf("process(%d) received information[parent-buff2]:%s [%p]\n", getpid(), buff2,buff2);
  }
  if (pd > 0) {
    wait(&status);//父进程等待子进程的结束(通过 wait(&status)),以确保子进程在父进程之前结束。
  }
  return 0; 
}

输出结果:结果表明,子进程和父进程中相同变量名的内存地址相同,但所存储的内容却并不一样。

目录
相关文章
|
21天前
|
存储 缓存 安全
Java内存模型深度解析:从理论到实践####
【10月更文挑战第21天】 本文深入探讨了Java内存模型(JMM)的核心概念与底层机制,通过剖析其设计原理、内存可见性问题及其解决方案,结合具体代码示例,帮助读者构建对JMM的全面理解。不同于传统的摘要概述,我们将直接以故事化手法引入,让读者在轻松的情境中领略JMM的精髓。 ####
32 6
|
13天前
|
C语言 开发者 内存技术
探索操作系统核心:从进程管理到内存分配
本文将深入探讨操作系统的两大核心功能——进程管理和内存分配。通过直观的代码示例,我们将了解如何在操作系统中实现这些基本功能,以及它们如何影响系统性能和稳定性。文章旨在为读者提供一个清晰的操作系统内部工作机制视角,同时强调理解和掌握这些概念对于任何软件开发人员的重要性。
|
13天前
|
Linux 调度 C语言
深入理解操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅,从进程管理的基本概念出发,逐步探索到内存管理的高级技巧。我们将通过实际代码示例,揭示操作系统如何高效地调度和优化资源,确保系统稳定运行。无论你是初学者还是有一定基础的开发者,这篇文章都将为你打开一扇了解操作系统深层工作原理的大门。
|
15天前
|
调度 开发者
核心概念解析:进程与线程的对比分析
在操作系统和计算机编程领域,进程和线程是两个基本而核心的概念。它们是程序执行和资源管理的基础,但它们之间存在显著的差异。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
34 4
|
14天前
|
存储 算法 Java
Java内存管理深度解析####
本文深入探讨了Java虚拟机(JVM)中的内存分配与垃圾回收机制,揭示了其高效管理内存的奥秘。文章首先概述了JVM内存模型,随后详细阐述了堆、栈、方法区等关键区域的作用及管理策略。在垃圾回收部分,重点介绍了标记-清除、复制算法、标记-整理等多种回收算法的工作原理及其适用场景,并通过实际案例分析了不同GC策略对应用性能的影响。对于开发者而言,理解这些原理有助于编写出更加高效、稳定的Java应用程序。 ####
|
22天前
|
算法 调度 开发者
深入理解操作系统:从进程管理到内存分配
本文旨在为读者提供一个深入浅出的操作系统知识之旅,从进程管理的基础概念出发,探索内存分配的策略与技巧。我们将通过实际代码示例,揭示操作系统背后的逻辑与奥秘,帮助读者构建起对操作系统工作原理的直观理解。文章不仅涵盖理论知识,还提供实践操作的指导,使读者能够将抽象的概念转化为具体的技能。无论你是初学者还是有一定基础的开发者,都能在这篇文章中找到有价值的信息和启发。
|
26天前
|
算法 调度 C++
深入理解操作系统:从进程管理到内存分配
【10月更文挑战第42天】本文将带你进入操作系统的神秘世界,探索其核心概念和关键技术。我们将从进程管理开始,了解操作系统如何协调和管理多个程序的运行;然后,我们将深入研究内存分配,看看操作系统如何有效地分配和管理计算机的内存资源。通过这篇文章,你将获得对操作系统工作原理的深入理解,并学会如何编写高效的代码来利用这些原理。
|
27天前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
1月前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
70 4
|
23天前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
179 1

推荐镜像

更多