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

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

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

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

在多进程编程中,使用 `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; 
}

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

目录
相关文章
|
5天前
|
存储 大数据 Unix
Python生成器 vs 迭代器:从内存到代码的深度解析
在Python中,处理大数据或无限序列时,迭代器与生成器可避免内存溢出。迭代器通过`__iter__`和`__next__`手动实现,控制灵活;生成器用`yield`自动实现,代码简洁、内存高效。生成器适合大文件读取、惰性计算等场景,是性能优化的关键工具。
69 2
|
1月前
|
弹性计算 前端开发 NoSQL
2025最新阿里云服务器配置选择攻略:CPU、内存、带宽与系统盘全解析
本文详解2025年阿里云服务器ECS配置选择策略,涵盖CPU、内存、带宽与系统盘推荐,助你根据业务需求精准选型,提升性能与性价比。
|
2月前
|
存储 弹性计算 固态存储
阿里云服务器配置费用整理,支持一万人CPU内存、公网带宽和存储IO性能全解析
要支撑1万人在线流量,需选择阿里云企业级ECS服务器,如通用型g系列、高主频型hf系列或通用算力型u1实例,配置如16核64G及以上,搭配高带宽与SSD/ESSD云盘,费用约数千元每月。
210 0
|
8月前
|
消息中间件 存储 网络协议
从零开始掌握进程间通信:管道、信号、消息队列、共享内存大揭秘
本文详细介绍了进程间通信(IPC)的六种主要方式:管道、信号、消息队列、共享内存、信号量和套接字。每种方式都有其特点和适用场景,如管道适用于父子进程间的通信,消息队列能传递结构化数据,共享内存提供高速数据交换,信号量用于同步控制,套接字支持跨网络通信。通过对比和分析,帮助读者理解并选择合适的IPC机制,以提高系统性能和可靠性。
1146 14
|
3月前
|
存储 缓存 数据挖掘
阿里云服务器实例选购指南:经济型、通用算力型、计算型、通用型、内存型性能与适用场景解析
当我们在通过阿里云的活动页面挑选云服务器时,相同配置的云服务器通常会有多种不同的实例供我们选择,并且它们之间的价格差异较为明显。这是因为不同实例规格所采用的处理器存在差异,其底层架构也各不相同,比如常见的X86计算架构和Arm计算架构。正因如此,不同实例的云服务器在性能表现以及适用场景方面都各有特点。为了帮助大家在众多实例中做出更合适的选择,本文将针对阿里云服务器的经济型、通用算力型、计算型、通用型和内存型实例,介绍它们的性能特性以及对应的使用场景,以供大家参考和选择。
|
5月前
|
域名解析 网络协议 安全
DNS服务器地址大全
DNS(域名系统)是互联网的“电话簿”,将域名解析为IP地址。选择优质DNS服务器可提升网络速度、降低延迟。以下是全球及中国各运营商的DNS服务器列表,包括公共DNS(如Google DNS、Cloudflare DNS)、中国电信、联通、移动等。根据地理位置、稳定性、安全性与隐私保护等因素选择适合的DNS服务器,优化上网体验。
14387 6
|
8月前
|
消息中间件 Linux
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
616 20
|
9月前
|
运维 监控 Ubuntu
【运维】如何在Ubuntu中设置一个内存守护进程来确保内存不会溢出
通过设置内存守护进程,可以有效监控和管理系统内存使用情况,防止内存溢出带来的系统崩溃和服务中断。本文介绍了如何在Ubuntu中编写和配置内存守护脚本,并将其设置为systemd服务。通过这种方式,可以在内存使用超过设定阈值时自动采取措施,确保系统稳定运行。
345 4
|
9月前
|
C语言 开发者 内存技术
探索操作系统核心:从进程管理到内存分配
本文将深入探讨操作系统的两大核心功能——进程管理和内存分配。通过直观的代码示例,我们将了解如何在操作系统中实现这些基本功能,以及它们如何影响系统性能和稳定性。文章旨在为读者提供一个清晰的操作系统内部工作机制视角,同时强调理解和掌握这些概念对于任何软件开发人员的重要性。

热门文章

最新文章

推荐镜像

更多
  • DNS