【linux 多进程并发】0202 Linux进程fork之后父子进程间的文件操作有着相同的偏移记录,多进程操作文件的方法
0202 Linux进程资源
专栏内容:
- postgresql使用入门基础
- 手写数据库toadb
- 并发编程
个人主页:我的主页
管理社区:开源数据库
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
文章目录
- 0202 Linux进程资源
- 一、概述
- 二、资源创建场景
- 三、资源在子进程中创建
- 3.1 示例代码
- 3.2 进程独立验证
- 四、程序启动时资源创建
- 4.1 示例代码
- 4.2 共用内核对象
- 总结
- 结尾
一、概述
进程启动之后,除了占用内存资源之外,在进程中还会打开文件,共享内存,网络套接字等与内核对象关联的资源,
这些资源在子进程中如何处理呢?
本节以文件为例,分析父子进程间对于与内核相关联的资源的处理情况,并使用代码实例进行演示。
二、资源创建场景
在使用fork创建子进程的时刻,对资源的使用大致分为两类情况:
- 也可能是运行了一段程序,初始化很多资源;然后再创建多进程任务;
此时,子进程会继承父进程创建的资源;
同时,要在子进程中关闭和释放不再使用的资源,否则会产生资源泄漏;
- 也可能是刚开始运行,先创建多任务,然后根据不同任务的分工不同,再初始化各自的资源;
此时,各子进程中没有继承的资源;
在第一类场景下,对于关联内核对象的资源,继承后的特性也会与单进程使用有一定区别,下面通过案例来分析一下。
三、资源在子进程中创建
资源在fork之后创建,也就是在子进程中创建。
这种情况与我们常规编写一个main函数中创建类似,也就是单进程程序一样,各子进程间互相独立,没有联系。
3.1 示例代码
- 下面通过一段测试代码来演示一下:
/** ex020104_forkafter.c*/
#include <stdio.h>#include <sys/types.h>
#include <unistd.h>#include <errno.h>
#include <string.h>typedef struct Info
{int pid;int seg;
}Info;FILE *fp = NULL;
int segno = 0;void OpenFile(char *path);
void CloseFile();
void ReadFile();
void WriteFile();int main(int argc ,char *argv[])
{int pid = -1;pid = fork();if(pid == 0){OpenFile("test");// in child printf("here in child ,my pid is %d\n", getpid());WriteFile();sleep(5);WriteFile();sleep(5);WriteFile();}else if(pid > 0){OpenFile("test");// in parentprintf("here in parent, my pid %d, child pid is %d\n", getpid(), pid);sleep(3);ReadFile();sleep(5);ReadFile();sleep(5);ReadFile();}else{// errorprintf("fork error[%s]\n",strerror(errno));}CloseFile();return 0;
}
在创建子进程之后,父子进程中同时打开test
文件,进行以下步骤:
- 子进程从文件头写入结构体数据;
- 父进程从文件头读一个结构体大小的数据;
- 然后子进程在上次偏移位置继续写入结构体大小的数据;
- 接着父进程从自己的偏移位置处读入结构体数据;
- 继续一次步骤3和4;
- 在进程结束时,关闭文件。
公共的文件操作函数
为了方便多次测试,将打开文件,关闭文件,读写文件编写为公共函数。
void OpenFile(char *path)
{if(NULL == path)return;fp = fopen(path, "w+");if(NULL == fp){printf("open file %s error!\n", path);}return;
}void CloseFile()
{if(NULL != fp)fclose(fp);
}void ReadFile()
{Info rinfo = {0};int pos = 0;if(NULL == fp)return ;/* data read from current position record by the fp. */pos = ftell(fp);fread(&rinfo, sizeof(rinfo), 1, fp); printf("[%d] read from %d, context(%d, %d) \n", getpid(), pos, rinfo.pid, rinfo.seg);
}void WriteFile()
{Info winfo = {0};int pos = 0;if(NULL == fp)return ;winfo.seg = segno++;winfo.pid = getpid();/* data read from current position record by the fp. */pos = ftell(fp);fwrite(&winfo, sizeof(winfo), 1, fp); fflush(fp);printf("[%d] write to %d, context(%d, %d) \n", getpid(), pos, winfo.pid, winfo.seg);
}
3.2 进程独立验证
- 编译运行:
[senllang@hatch ex_0201]$ gcc ex020104_forkafter.c -o extest
[senllang@hatch ex_0201]$ ./extest
here in parent, my pid 802470, child pid is 802471
here in child ,my pid is 802471
[802471] write to 0, context(802471, 0)
[802470] read from 0, context(802471, 0)
[802471] write to 8, context(802471, 1)
[802470] read from 8, context(802471, 1)
[802471] write to 16, context(802471, 2)
[802470] read from 16, context(802471, 2)
- 结果分析
- 可以看到进程
802471
从文件偏移为0处开始写入数据;- 而父进程
802470
也是从偏移为0处读数据;- 两个进程的偏移都是各自计数,互相独立;文件偏移记录在内核文件表项中;
四、程序启动时资源创建
如果在程序启动时创建文件,也就是在fork之前创建,子进程会继承之前创建的文件句柄。
在这种情况下,会出现什么样的情况呢?
4.1 示例代码
/** ex020103_forkresource.c*/
#include <stdio.h>#include <sys/types.h>
#include <unistd.h>#include <errno.h>
#include <string.h>typedef struct Info
{int pid;int seg;
}Info;FILE *fp = NULL;
int segno = 0;void OpenFile(char *path);
void CloseFile();
void ReadFile();
void WriteFile();int main(int argc ,char *argv[])
{int pid = -1;OpenFile("test");pid = fork();if(pid == 0){// in child printf("here in child ,my pid is %d\n", getpid());WriteFile();sleep(5);WriteFile();sleep(5);WriteFile();}else if(pid > 0){// in parentprintf("here in parent, my pid %d, child pid is %d\n", getpid(), pid);sleep(3);ReadFile();sleep(5);ReadFile();sleep(5);ReadFile();}else{// errorprintf("fork error[%s]\n",strerror(errno));}CloseFile();return 0;
}
这段代码与前例类似,只是将创建文件放在了fork之前,
也就是FILE *fp 在主程序中先被初始化了,然后创建了子进程,在子进程中引用了一模一样的内容,类似于拷备了一份。
4.2 共用内核对象
- 编译运行
[senllang@hatch ex_0201]$ gcc ex020103_forkresource.c -o extest1
[senllang@hatch ex_0201]$ ./extest1
here in parent, my pid 803877, child pid is 803878
here in child ,my pid is 803878
[803878] write to 0, context(803878, 0)
[803877] read from 8, context(0, 0)
[803878] write to 8, context(803878, 1)
[803877] read from 16, context(0, 0)
[803878] write to 16, context(803878, 2)
[803877] read from 24, context(0, 0)
可以看到很有意思的现象:
- 在子进程
803878
中,在文件偏移0处写入,之后偏移变为8; - 而之后父进程
803877
开始读时,文件偏移为8,并没有从0开始; - 接着子进程
803878
又从偏移8处写入数据,之后偏移变为16; - 而父进程
803877
读偏移也变成了16; - 后面一次,也是父进程中文件偏移,与子进程中文件偏移相互联系;
总结
好了,到这里,子进程是父进程的拷贝有了更加深入的理解,这里像编程语言中的深拷贝与浅拷贝的关系。
而子进程其实是做了一些浅拷贝,引用的内核文件表项还是一份,这就会引起两个进程共同操作的问题。
在这种情况下,每次操作需要加锁,同时要指定操作的位置和大小。
结尾
非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!
相关文章:

【linux 多进程并发】0202 Linux进程fork之后父子进程间的文件操作有着相同的偏移记录,多进程操作文件的方法
0202 Linux进程资源 专栏内容: postgresql使用入门基础手写数据库toadb并发编程 个人主页:我的主页 管理社区:开源数据库 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物. 文章目录 020…...
SQLite在安卓中的应用
在 Android 应用程序中,SQLite 是默认的嵌入式数据库解决方案,Android 系统为开发者提供了相应的 API 来管理 SQLite 数据库。通过使用 SQLiteOpenHelper 类和 SQLiteDatabase 类,开发者可以方便地创建、查询、更新和删除数据库中的数据。 以…...

Python数据库操作
前面的章节中学习了使用 Python 读写文件的方法,大家可以用文件方式来存放数据,不过使用文件方式时不容易管理,同时还容易丢失,会带来许多问题。目前主流的方法都是采用数据库软件,通过数据库软件来组织和存放数据&…...
交叉熵损失函数为代表的两层神经网络的反向传播量化求导计算公式
反向传播(back propagation,BP)算法也称误差逆传播,是神经网络训练的核心算法。我们通常说的 BP 神经网络是指应用反向传播算法进行训练的神经网络模型。反向传播算法的工作机制究竟是怎样的呢?我们以一个两层…...
数据结构——八大排序(上)
数据结构中的八大排序算法是计算机科学领域经典的排序方法,它们各自具有不同的特点和适用场景。以下是这八大排序算法的详细介绍: 一、插入排序(Insertion Sort) 核心思想:将数组中的所有元素依次跟前面已经排好的元…...
vxe-table 导入导出功能全解析
一、vxe-table 导入导出功能概述 vxe-table 的导入导出功能在数据处理中具有至关重要的作用。在现代数据管理和处理的场景中,高效地导入和导出数据是提高工作效率的关键。 对于导入功能而言,它允许用户将外部的表格数据,如 Excel 文件&…...
常用STL的操作以及特点
C 标准模板库(STL)提供了很多常用的数据结构和算法,极大简化了开发工作。STL 包括容器(如 vector、list、map 等)、算法(如排序、查找等)以及迭代器。以下是一些常用 STL 容器的操作以及它们的特…...
025 elasticsearch索引管理-Java原生客户端
文章目录 pom.xml1创建索引2.创建索引并设置settings信息3.创建索引并设置mapping信息4.删除索引库5.给未设置mapping的索引设置mapping elasticsearch版本7.10.2,要求java客户端与之相匹配,推荐Springboot版本是2.3以上版本 依赖配置使用的是JUnit 5&am…...

Gin框架操作指南10:服务器与高级功能
官方文档地址(中文):https://gin-gonic.com/zh-cn/docs/ 注:本教程采用工作区机制,所以一个项目下载了Gin框架,其余项目就无需重复下载,想了解的读者可阅读第一节:Gin操作指南&#…...

AIGC技术的学习 系列一
文章目录 前言一、AIGC技术演进1.1 图像视频生成1.2. 文本生成1.3. 多模态生成1.4. 小结二、CAD&CAE软件介绍2.1. CAD软件2.2. CAE软件2.3. 小结三、AIGC技术与CAD&CAE软件的集成案例3.1. 土建设计领域3.2. 机械设计领域四、结语五、参考文献总结前言 在全球智能制造的…...

Milvus×Dify半小时轻松构建RAG系统
最近,检索增强生成(RAG)技术在AI界引起了广泛关注。作为一种将知识库与生成模型结合的新型架构,RAG大大提升了AI应用的实际表现。而在构建RAG系统时,Milvus作为业界领先的开源向量数据库,扮演着关键角色。本…...

wireshark 解密浏览器https数据包
一、导出浏览器证书有两种方法 1、在浏览器快捷方式追加启动参数: --ssl-key-log-file"d:\log\2.log" C:\Users\Administrator\AppData\Local\Google\Chrome\Application\chrome.exe --ssl-key-log-file"d:\log\2.log" 2、环境变量中新建用…...

【HTML】构建网页的基石
我的主页:2的n次方_ HTML 是一种超文本标记语言,不仅有文本,还能包含图片,音频等 1. HTML 的文件基本结构 html 标签是整个 html 文件的最顶层标签,head 标签中写页面的属性,body 标签是页面中显示的…...
rust不允许在全局区定义普通变量!
文章目录 C 中的全局变量Rust 中的全局变量设计哲学的体现 在 C 和 Rust 中,全局变量的处理方式体现了这两种语言设计哲学上的一些根本性差异: C 中的全局变量 C 允许在全局作用域中定义变量。这些变量在程序的整个生命周期内都存在,从程序开…...
量化投资中的数据驱动决策:大数据如何改变金融市场
随着科技的进步,金融行业迎来了前所未有的变革,量化投资作为其中的代表,逐渐成为投资市场的主流。量化投资是基于数学模型、数据分析以及算法策略的投资方式,与传统依赖经验和直觉的投资方法相比,它的核心优势在于能够…...

MySQL 设计数据表
一个数据表主要包含信息有 : 表名、主键、字段、数据类型、索引,本节主要介绍表的命名规范、字段命名、字段的数据类型选择。 新建的表都是新建在 “item_name” 数据库中的,新建 “item_name” 数据库命令如下 : CREATE DATABASE item_name;新建数据库…...

【大数据技术基础 | 实验一】配置SSH免密登录
文章目录 一、实验目的二、实验要求三、实验原理(一)大数据实验一体机(二)SSH免密认证 四、实验环境五、实验内容和步骤(一)搭建集群服务器(二)添加域名映射(三ÿ…...

地级市碳排放效率测算2006-2021年
为了测算碳排放效率,研究者们采用了多种方法,其中超效率SBM(Slack-Based Measure)和超效率CCR(Charnes, Cooper and Rhodes)模型是常用的两种方法。这些模型可以有效地评估决策单元的相对有效性,…...

周易解读:四象
四 象 在前面呢,我是讲完了太极与两仪的知识。这一节,我们来讲解四象的内容。 关于四象的知识,它在正式的周易的经文里面,它并没有多少用处。但是呢,在基础知识的学习里面,四象的知识,大家是…...

Java设计模式梳理:行为型模式(策略,观察者等)
行为型模式 行为型模式关注的是各个类之间的相互作用,将职责划分清楚,使得我们的代码更加地清晰。 策略模式 策略模式太常用了,所以把它放到最前面进行介绍。它比较简单,我就不废话,直接用代码说事吧。 下面设计的…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...

ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...