当前位置: 首页 > news >正文

【linux 多进程并发】0201 Linux进程fork内存空间,父子进程变量内存地址居然是一样的

0201 Linux进程fork方式详解

专栏内容

  • postgresql使用入门基础
  • 手写数据库toadb
  • 并发编程

个人主页:我的主页
管理社区:开源数据库
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.

文章目录

  • 0201 Linux进程fork方式详解
  • 一、概述
  • 二、创建进程
    • 2.1 测试程序
    • 2.2 查看进程信息
  • 三、进程内存空间
    • 3.2 变量空间用例
    • 3.2 验证分析
  • 四、总结
  • 结尾

一、概述


在Linux系统中提供了多个创建进程的API接口,以不同的方式来创建进程,

比如fork, vfork, exe系列接口,还有clone,使用之前博文《linux 进程创建的五种方法fork/vfork/execv/clone》之中已经介绍,

本文对于常用的fork方式,从进程的内存空间,生命周期,资源管理等方面详细分析,有更深的理解,在设计并发任务时有更加清晰的架构选择。

二、创建进程


通过fork创建进程,经过fork调用之后,就非常有意思了,程序被分叉为两个进程了,形成了父子两个进程,而fork会在父子进程中返回不同的值。

在这里插入图片描述

在父进程中返回子进程的pid,而在子进程中返回值为0。

2.1 测试程序

下面我们写一个测试程序,通过前面介绍的查看进程的方法来看一下进程的情况。

/** ex020101_fork.c*/
#include <stdio.h>#include <sys/types.h>
#include <unistd.h>#include <errno.h>
#include <string.h>int main(int argc ,char *argv[])
{int pid = -1;pid = fork();if(pid == 0){// in childprintf("here in child ,my pid is %d\n", getpid());sleep(10);}else if(pid > 0){// in parentprintf("here in parent, my pid %d, child pid is %d\n", getpid(), pid);sleep(10);}else{// errorprintf("fork error[%s]\n",strerror(errno));}return 0;
}

2.2 查看进程信息

事先打开两个终端,一个用于运行测试程序,一个用于查看进程信息。

  • 在终端一上运行程序。
[senllang@hatch ex_0201]$ gcc ex020101_fork.c -o ex020101
[senllang@hatch ex_0201]$ ./ex020101
here in parent, my pid 56657, child pid is 56658
here in child ,my pid is 56658

程序运行后,可以看到进入了父子进程中,分别打印了信息,之后为睡眠10秒的时间,如果来不及查看,可以调大时间。

  • 在终端二上查看
[senllang@hatch ex_0201]$ ps -ef|grep ex0201|grep -v grep
senllang   56657   55956  0 08:28 pts/0    00:00:00 ./ex020101
senllang   56658   56657  0 08:28 pts/0    00:00:00 ./ex020101

通过ps命令可以查看两个进程,第二列是PPID 父进程PID,第三列是当前进程的PID。

第一行是子进程,它的进程号为55956, 第二行为父进程,也就是主程序运行的进程,进程号为 56657, 它的父进程为当前的终端进程。

三、进程内存空间


内存空间在进程之间是完全独立的,通过fork创建的父子进程也是这样吗?

这里通过一组测试来看一下,在上面的例子中增加变量查看内存空间变化。

3.2 变量空间用例

在上一例子中,增加了全局变量和动态分配内存的变量,观察如下内容:

  • 分别在父子进程中查看它们的值和地址;
  • 修改变量的值,观察它们在父子进程中的变化;

3.2 验证分析

验证代码如下:

/** ex020102_forkvar.c*/
#include <stdio.h>#include <sys/types.h>
#include <unistd.h>#include <errno.h>
#include <string.h>
#include <stdlib.h>#define MAX_PROMPT_LEN 64int g_flag = 1;int main(int argc ,char *argv[])
{int pid = -1;char *prompt = NULL;prompt = (char*)malloc(MAX_PROMPT_LEN);if(NULL == prompt){printf("memory maybe not enogh.\n");return -1;}strncpy(prompt, "I am a starting process.", MAX_PROMPT_LEN);printf("g_flag:%d, addr:%p; prompt:%s , addr:%p\n", g_flag, &g_flag, prompt, prompt);pid = fork();if(pid == 0){// in childprintf("here in child ,my pid is %d\n", getpid());printf("[pid:%d] g_flag:%d, addr:%p; prompt:%s , addr:%p\n", getpid(), g_flag, &g_flag, prompt, prompt);g_flag = 2;free(prompt);prompt = NULL;sleep(10);}else if(pid > 0){// in parentprintf("here in parent, my pid %d, child pid is %d\n", getpid(), pid);printf("[pid:%d] g_flag:%d, addr:%p; prompt:%s , addr:%p\n", getpid(), g_flag, &g_flag, prompt, prompt);sleep(10);}else{// errorprintf("fork error[%s]\n",strerror(errno));}printf("[pid:%d] g_flag:%d, addr:%p; prompt:%s , addr:%p\n", getpid(), g_flag, &g_flag, prompt, prompt);if(NULL != prompt)free(prompt);return 0;
}
  • 运行结果

编译生成可执行文件

[senllang@hatch ex_0201]$ gcc ex020102_forkvar.c -o ex020102

运行测试程序

[senllang@hatch ex_0201]$ ./ex020102
g_flag:1, addr:0x60106c; prompt:I am a starting process. , addr:0x9372a0
here in parent, my pid 57198, child pid is 57199
[pid:57198] g_flag:1, addr:0x60106c; prompt:I am a starting process. , addr:0x9372a0
here in child ,my pid is 57199
[pid:57199] g_flag:1, addr:0x60106c; prompt:I am a starting process. , addr:0x9372a0
[pid:57198] g_flag:1, addr:0x60106c; prompt:I am a starting process. , addr:0x9372a0
[pid:57199] g_flag:2, addr:0x60106c; prompt:(null) , addr:(nil)

分析结果

在这里插入图片描述

从运行结果可以看到如下信息:

  • 父进程的PID为57198,而子进程的PID为 57199;
  • 全局变量 g_flag 的地址为 0x60106c, 而指针变量 prompt的地址为 0x9372a0;
  • 子进程中修改变量 g_flag 值为2 ,同时释放 prompt 指向的内存,并赋值为空;

可以看到一个有意思的现象,在子进程中,继承于父进程的变量地址,与父进程中的变量地址一模一样,值也一样;

而修改子进程中的变量值时,父进程并没有改变;

  • 子进程在创建时,会完全拷贝父进程的内存内容,所以它们有一模一样的内存部局;当然,这里为了加速创建性能,采用了写时拷贝的策略;
  • 我们看到的进程内存地址,其实是虚拟内存地址,它由基地址开始,所以每个进程的基址一样,使得父子进程中内存地址可能相同;
  • 虽然虚拟地址相同,实际对应的是不同的物理内存;
  • 在32位机器上,进程虚拟地址空间是4GB,而64位上就非常大了;

四、总结


在多任务并发编程中,使用多进程架构时,在使用fork创建的父子进程时,子进程得到与父进程相同的内存空间内容。

进程的内存空间由虚拟地址描述,在使用时会映射到物理地址。

结尾


非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!

作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。

注:未经同意,不得转载!


http://www.mrgr.cn/news/47711.html

相关文章:

  • 【Go】:图片上添加水印的全面指南——从基础到高级特性
  • 《探秘鸿蒙NEXT中的人工智能核心架构》
  • 数据库系统概论期末复习
  • SpringCloud微服务(十二)
  • asp.net core webapi中的数据注解与数据验证
  • 探索Python的异步编程:高效处理并发任务
  • Vue CLI 创建项目
  • 春意盎然:Spring Boot课程答疑系统
  • 20241011软考架构-------软考216-220答案解析
  • 如何获取商品详情:发送HTTP请求的指南
  • 删除 Word 空白页的 3 种方法总结
  • Mycat引领MySQL分布式部署新纪元:性能与扩展性的双重飞跃
  • 楼顶上的建筑奇迹:气膜体育馆的独特优势—轻空间
  • 基于STM32的智能门锁
  • 为什么人工智能用 Python?
  • JAVA学习-练习试用Java实现“二叉树的层序遍历”
  • 【靶点Talk】为什么联合用药喜欢用VEGF+VEGFR?
  • 基恩士PLC数据 转profinet IO项目案例
  • 【加密社】私钥碰撞器原理及工作展示
  • 如何看待诺贝尔物理学奖颁给了机器学习与神经网络?
  • electron-vite_4使用WebContentsView快速集成已有项目
  • 使用Windows 自带的 图片 改变图像的任意分辨率:
  • xxxxx51c嵌入式~mbedtls移植各MCU
  • 构建卓越企业架构:数字化转型中的TOGAF框架最佳实践解析
  • SSM(set、foreach、sql代码片段)
  • 数据结构之二叉搜索树(key模型与key_value模型)