【C++高并发服务器WebServer】-1:Linux中父子进程fork创建及关系、GDB多进程调试

在这里插入图片描述

本文目录

  • 一、进程创建
  • 二、GDB多进程调试

一、进程创建

在Linux中输入man 2 fork可以查看man文档中的fork的相关函数信息。

fork的作用就是创建一个子进程。

在这里插入图片描述

通过fork我们可以知道,创建子进程的时候,复制父进程的信息。

我们看看翻译的man文档信息:

  • 子进程和父进程在独立的内存空间中运行。在调用 fork() 时,两个内存空间的内容是相同的。其中一个进程进行的写操作、文件映射以及取消映射不会影响另一个进程。

首先我们来说说,在 fork() 调用时,子进程和父进程的内存空间内容是相同的,这是因为 fork() 的实现机制是通过“写时复制”(Copy-On-Write,简称 COW)来实现的。

  • 内存空间的复制

首先,fork() 的目标是创建一个与父进程几乎完全相同的子进程。为此,它需要复制父进程的内存空间,包括代码段、数据段、堆和栈等。在调用 fork() 的瞬间,子进程的内存空间内容与父进程完全一致。当然,上面两句话指的是虚拟地址空间。

在 fork() 后,父子进程会有相同的虚拟地址空间映射,父进程的数据和堆栈的内容在物理内存中不会立即复制。实际上,操作系统使用了页表(paging)来标记这些内存页面为只读,且标记为共享的。

  • 写时复制(Copy-On-Write)机制

虽然内存空间的内容在 fork() 时是相同的,但实际的物理内存并没有完全复制。操作系统采用了“写时复制”技术来优化资源使用。

在 fork() 调用后,父进程和子进程共享相同的物理内存页面。只有当父进程或子进程中的任意一个对内存进行写操作时,操作系统才会真正复制该页面的内容到一个新的物理页面,并将修改后的页面分配给进行写操作的进程。如果父进程和子进程都没有对内存进行写操作,它们将继续共享相同的物理页面。

当父进程或子进程尝试修改某一共享的内存页时,操作系统才会进行实际的物理内存复制。这意味着只有在进程写入某个页面时,内核才会为该页面分配新的物理内存,并将原页面的内容复制到新的物理内存位置,从而保证父子进程的内存内容互不影响。

所以由于 fork() 的目标是创建一个与父进程完全相同的子进程,因此在调用 fork() 的瞬间,子进程的内存空间内容必须与父进程一致。这是通过逻辑上的“复制”实现的(虚拟地址空间),而物理内存的实际复制则通过写时复制机制延迟到真正需要修改内存时才进行。

  • “运行在不同的内存空间”(The child process and the parent process run in separate memory spaces)

这句话的核心在于“内存空间”(memory space)的概念。内存空间指的是进程的虚拟内存空间(virtual memory space),而不是物理内存(physical memory)。每个进程都有自己的虚拟内存空间,这是操作系统为进程分配的逻辑地址空间。虚拟内存空间是独立的,每个进程都无法直接访问其他进程的虚拟内存空间。这种隔离是现代操作系统实现进程隔离和保护的基础。

当 fork() 创建子进程时,子进程会获得一个全新的虚拟内存空间。虽然这个虚拟内存空间的内容在创建时与父进程的虚拟内存空间内容相同,但它们是完全独立的。子进程无法直接访问父进程的虚拟内存空间,反之亦然。

虽然父进程和子进程运行在不同的虚拟内存空间中,但它们的内存内容在 fork() 调用时是相同的。一是因为逻辑上的复制:在 fork() 调用时,子进程的虚拟内存空间被初始化为与父进程的虚拟内存空间内容一致。这意味着子进程的代码段、数据段、堆和栈等在逻辑上与父进程相同。

二是因为写时复制(Copy-On-Write):操作系统通过写时复制技术优化了内存的使用。虽然虚拟内存空间的内容在逻辑上是相同的,但物理内存页面并不会立即复制。只有当父进程或子进程对某个页面进行写操作时,操作系统才会真正复制该页面的内容到新的物理内存中。所以只需要明确:在 fork() 调用时,子进程的虚拟内存空间的内容与父进程相同,但这种相同是逻辑上的,物理内存的复制是通过写时复制机制实现的。

在linux中新建c++文件,运行下面代码,gcc fork.c -o fork./fork

/*
    pid_t fork(void):
    作用:创建子进程。
    返回值;
        fork会返回两次:
        一次在父进程中,一次在子进程中。
        在父进程中,返回子进程的ID;
        在子进程中,返回0.
        如何区分父进程和子进程,就可以通过fork返回值。
        如果在父进程中返回-1,表示创建子进程失败,并且设置对应的error
        (当系统的进程数达到了系统上限或者内存不足的时候,就会失败)

*/


# include<sys/types.h>
# include<unistd.h>
# include<stdio.h>
int main(){


    pid_t pid = fork(); //这个pid是int类型的
    //因为fork会返回两个,分别对应两个进程
    //判断是父进程还是子进程
    if (pid>0){
        printf("pid: %d\n", pid);
        printf("我是父进程,pid : %d,ppid: %d\n",getpid(),getppid());
    }else if(pid==0){
        printf("我是子进程,pid : %d,ppid: %d\n",getpid(),getppid());
    }

    for(int i=0;i<5;i++){
        printf("i : %d, pid : %d \n",i,getpid());
        sleep(1);
    }
    return 0;
}

输出如下:

当前的pid是fork程序33228,ppid是32535(这个是我的连接linux终端bash的进程id,然后子进程id是33228,能够对应上。)

pid: 33228
我是父进程,pid : 33227,ppid: 32535
i : 0, pid : 33227 
我是子进程,pid : 33228,ppid: 33227
i : 0, pid : 33228 
i : 1, pid : 33227 
i : 1, pid : 33228 
i : 2, pid : 33227 
i : 2, pid : 33228 
i : 3, pid : 33227 
i : 3, pid : 33228 
i : 4, pid : 33228 
i : 4, pid : 33227 

从上面也可以看出,是父进程和子进程交替在运行for循环,从这一点也可以看出cpu是时间片分给父子进程交替运行。

并且父子进程都在运行for,也可以看出父子进程共享了代码。

下图是父子进程虚拟地址空间示意图,fork以后,子进程的用户区数据和父进程一样(因为是拷贝过来的),内核区也会拷贝过来,但是内核区的pid不一样。

父进程执行 pid_t pid = fork() 得到的返回值,是在栈空间,得到的返回值是子进程的pid(比如33228),但是子进程clone得到的虚拟地址空间中的栈空间所得到的pid是0,但是linux内核中子进程的pid是33228(注意返回的id和自身的pid)。
在这里插入图片描述

虚拟地址空间(Virtual Address Space)是现代计算机操作系统中一个非常重要的概念,它是操作系统为每个进程分配的逻辑地址空间。简单来说,虚拟地址空间是一个进程能够访问的内存地址范围,但这些地址并不是直接映射到物理内存(实际的硬件内存)的地址,而是通过操作系统和硬件的联合管理,映射到物理内存或其他存储资源的逻辑地址。
虚拟地址空间通常由以下几部分组成:
代码段(Code Segment):存放程序的可执行代码。
数据段(Data Segment):存放程序的全局变量和静态变量。
堆(Heap):用于动态分配内存(如通过 malloc() 或 new 分配的内存)。
栈(Stack):用于存储函数调用时的局部变量、函数参数和返回地址等。
共享库(Shared Libraries):存放程序运行时加载的动态链接库(如 .so 文件)。
未映射区域(Unmapped Regions):未分配给任何用途的内存区域,通常用于未来扩展。
虚拟地址空间是现代操作系统实现内存管理、进程隔离和内存保护的核心机制。它使得每个进程都能在一个独立的、安全的环境中运行,同时允许操作系统灵活地管理物理内存资源。
简单来说,虚拟地址空间是一个进程的“私有内存世界”,它通过操作系统和硬件的支持,将逻辑地址与物理地址分离,从而实现高效的内存管理和进程隔离。

所以总结一下就是:内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间,只要需要写入的时候才会复制地址空间,从而使得各个进程拥有各自的地址空间,也就是说,资源的复制是在写入的时候才会进行(读时共享,写时拷贝)。fork产生的子进程与父进程相同的文件描述符指向相同的文件夹,且用户区的数据相同。

二、GDB多进程调试

使用GDB进行调试的时候, 默认只能跟踪一个进程,可以在fork函数调用之前,通过指令设置GDB调试工具跟踪父进程或者跟踪子进程,默认跟踪父进程。

set follow-fork-mode [parent(默认)|child] 命令是设置调试父进程或者子进程。

设置调试模式:`set detach-on-fork [on|off],默认为on,表示调试当前进程的时候其他进程继续运行,如果为off,则调试当前进程的时候,其他进程被GDB挂起。

查看调试的进程:info inferiors, 切换当前调试的进程:inferior id,使进程脱离GDB调试:detech inferiors id

# include<sys/types.h>
# include<unistd.h>
# include<stdio.h>
int main(){


    printf("begin\n");
    //判断是父进程还是子进程
    if (fork()>0){
        printf("我是父进程,pid : %d,ppid: %d\n",getpid(),getppid());
        int i;
        for(i=0;i<10;i++){
            printf("i=%d\n",i);
            sleep(1);
        }
    }else{
        printf("我是子进程,pid : %d,ppid: %d\n",getpid(),getppid());
        int j;
        for(j=0;j<10;j++){
            printf("i=%d\n",j);
            sleep(1);
        }
    }

    return 0;
}

输入命令进行编译调试:gcc hello.c -o hello -g,然后通过gdb hello进行调试。

在12行、19行插入断点。

在这里插入图片描述
在这里插入图片描述
然后输入r、n(next)、n,就会发现下面的调试过程,也就是gdb默认调试父进程,然后子进程的代码会走掉。

在这里插入图片描述
在gdb环境下,通过命令show follow-fork-mode可以查看现有的fork模式,我们通过set follow-fork-mode child来跟踪子进程。

这个时候再继续run,就会发现断点在19行停住了。
在这里插入图片描述
通过info inferiors可以查看调试进程,并且inferior 2切换调试的进程。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/961079.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

中科大:LLM检索偏好优化应对RAG知识冲突

&#x1f4d6;标题&#xff1a;RPO: Retrieval Preference Optimization for Robust Retrieval-Augmented Generation &#x1f310;来源&#xff1a;arXiv, 2501.13726 &#x1f31f;摘要 &#x1f538;虽然检索增强生成&#xff08;RAG&#xff09;在利用外部知识方面表现出…

Antd React Form使用Radio嵌套多个Select和Input的处理

使用Antd React Form使用Radio会遇到嵌套多个Select和Input的处理&#xff0c;需要多层嵌套和处理默认事件和冒泡&#xff0c;具体实现过程直接上代码。 实现效果布局如下图 代码 <Formname"basic"form{form}labelWrap{...formItemLayoutSpan(5, 19)}onFinish{on…

【deepseek】deepseek-r1本地部署-第一步:下载LM Studio

要下载LM Studio&#xff0c;可以按照以下步骤进行&#xff1a; 一、访问LM Studio官方网站 打开必应&#xff08;注意&#xff01;百度无法打开官网&#xff09;&#xff0c;输入LM Studio的官方网址&#xff1a;LM Studio - Discover, download, and run local LLMs。进入L…

爬虫基础之爬取某基金网站+数据分析

声明: 本案例仅供学习参考使用&#xff0c;任何不法的活动均与本作者无关 网站:天天基金网(1234567.com.cn) --首批独立基金销售机构-- 东方财富网旗下基金平台! 本案例所需要的模块: 1.requests 2.re(内置) 3.pandas 4.pyecharts 其他均需要 pip install 模块名 爬取步骤: …

Day27-【13003】短文,什么是栈?栈为何用在递归调用中?顺序栈和链式栈是什么?

文章目录 第三章栈和队列总览第一节栈概览栈的定义及其基本操作如何定义栈和栈的操作&#xff1f;合理的出栈序列个数如何计算&#xff1f;栈的两种存储方式及其实现&#xff1f;顺序栈及其实现&#xff0c;还有对应时间复杂度*、清空栈&#xff0c;初始化栈5、栈空&#xff0c…

Linux:多线程 [1]概念理解

char *str "hello bfr"; *str "H"; "hello bfr"这个字符串存储在虚拟地址空间的代码区中&#xff0c;令str指向它之后当要修改*str时&#xff0c;也就是修改代码区中"hello bfr"位置的值&#xff0c;再将它通过页表映射成物理内存时…

electron typescript运行并设置eslint检测

目录 一、初始化package.json 二、安装依赖 1、安装electron 2、安装typescript依赖 3、安装eslint 三、项目结构 四、配置启动项 一、初始化package.json 我的&#xff1a;这里的"main"没太大影响&#xff0c;看后面的步骤。 {"name": "xlo…

国内优秀的FPGA设计公司主要分布在哪些城市?

近年来&#xff0c;国内FPGA行业发展迅速&#xff0c;随着5G通信、人工智能、大数据等新兴技术的崛起&#xff0c;FPGA设计企业的需求也迎来了爆发式增长。很多技术人才在求职时都会考虑城市的行业分布和发展潜力。因此&#xff0c;国内优秀的FPGA设计公司主要分布在哪些城市&a…

基于微信小程序的电子竞技信息交流平台设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

在亚马逊云科技上用Stable Diffusion 3.5 Large生成赛博朋克风图片(下)

背景介绍 在2024年的亚马逊云科技re:Invent大会上提前预告发布的Stable Diffusion 3.5 Large&#xff0c;现在已经在Amazon Bedrock上线了&#xff01;各位开发者们现在可以使用该模型&#xff0c;根据文本提示词文生图生成高质量的图片&#xff0c;并且支持多种图片风格生成&…

【自学嵌入式(6)天气时钟:软硬件准备、串口模块开发】

天气时钟&#xff1a;软硬件准备、串口模块开发 软硬件准备接线及模块划分ESP8266开发板引脚图软件准备 串口模块编写串口介绍Serial库介绍 近期跟着网上一些教学视频&#xff0c;编写了一个天气时钟&#xff0c;本篇及往后数篇都将围绕天气时钟的制作过程展开。本文先解决硬件…

初始JavaEE篇 —— Spring Web MVC入门(上)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaEE 目录 RequestMappingg 注解介绍 Postman的介绍与使用 PostMapping 与 GetMapping 注解 构造并接收请求 接收简单参数 接收对象…

浅谈Unity中Canvas的三种渲染模式

Overview UGUI通过 Canvas 组件渲染和管理UI元素。Canvas 是 UI 元素的容器&#xff0c;它决定了 UI 元素的渲染方式以及它们在屏幕上的显示效果。Canvas 有三种主要的渲染模式&#xff0c;每种模式有不同的用途和特点。本文将介绍这三种渲染模式 1. Screen Space - Overlay 模…

C++17 std::variant 详解:概念、用法和实现细节

文章目录 简介基本概念定义和使用std::variant与传统联合体union的区别 多类型值存储示例初始化修改判断variant中对应类型是否有值获取std::variant中的值获取当前使用的type在variant声明中的索引 访问std::variant中的值使用std::get使用std::get_if 错误处理和访问未初始化…

NoteGen:记录、写作与AI融合的跨端笔记应用

在信息爆炸的时代,如何高效地捕捉灵感、整理知识并进行创作成为了许多人关注的问题。为此,我们开发了 NoteGen,一款专注于记录和写作的跨端 AI 笔记应用。它基于 Tauri 开发,利用其强大的跨平台能力支持 Mac、Windows 和 Linux 系统,并计划未来扩展到 iOS 和 Android 平台…

SET alter system reload

目录标题 alter system 只是 写 auto 文件SET & alter system1. **会话级别参数&#xff08;Session-level parameters&#xff09;**2. **系统级别参数&#xff08;System-level parameters&#xff09;**3. **某些特定的超级用户参数**4. **修改时生效的参数**总结&#…

Ubuntu20.04 磁盘空间扩展教程

Ubuntu20.04 磁盘空间扩展教程_ubuntu20 gpart扩容-CSDN博客文章浏览阅读2w次&#xff0c;点赞38次&#xff0c;收藏119次。执行命令查看系统容量相关的数据&#xff1a;df -h当前容量为20G&#xff0c;已用18G&#xff08;96%&#xff09;&#xff0c;可用844M&#xff0c;可用…

无心剑七绝《除夕快乐》

七绝除夕快乐 除旧迎新瑞气扬 夕阳烂漫映红妆 快言美酒佳肴味 乐享天伦福满堂 2025年1月28日 平水韵七阳平韵 无心剑这首七绝以“除夕快乐”为题&#xff0c;巧妙地运用了藏头手法&#xff0c;将“除夕快乐”四字分别嵌入诗的每一句首字&#xff0c;构思精巧&#xff0c;富有新…

WebSocket 详解:全双工通信的实现与应用

目录 一、什么是 WebSocket&#xff1f;&#xff08;简介&#xff09; 二、为什么需要 WebSocket&#xff1f; 三、HTTP 与 WebSocket 的区别 WebSocket 的劣势 WebSocket 的常见应用场景 WebSocket 握手过程 WebSocket 事件处理和生命周期 一、什么是 WebSocket&#xf…

机器人抓取与操作概述(深蓝)——1

工业机器人&#xff1a;① “臂”的形态 ② “手”的形态 ③ 视觉&#xff0c;力和触觉 1 机器人的不同形态 “臂”的形态 “手”的形态 2 常见的操作任务 操作&#xff1a;插入、推和滑 抓取&#xff1a;两指&#xff08;平行夹爪&#xff09;抓取、灵巧手抓取 落地-产…