【Linux】fork入门级使用

目录

 一、前置准备

1、进程的基本概念

2、进程标识符PID、PPID

1)pid介绍

2)获取pid和ppid

二、fork函数

1、fork的基本介绍

1)fork(): 创建子进程

2)对于函数具体的描述

3)两个返回值

2、感受一下fork的使用

3、子进程的作用?-->实现不同于父进程的功能

4、创建的子进程如何存储,PCB都是如何确定的?-->子进程(PCB单独创建)和父进程共享代码和数据

5.为什么fork要有两个返回值?

6.为什么fork要给子进程返回0,给父进程返回子进程pid?

7.fork之后,父子进程谁先运行?

7.一个变量怎么会有不同的内容?-->地址空间


全文在Ubuntu 20.04.6 LTS的环境下使用C语言!

本篇文章需要大家具备一定的Linux的基础下,进行学习~

 一、前置准备

1、进程的基本概念

【Linux】进程周边:进程概念-CSDN博客

这篇文章中已经将进程的基础概念、进程控制块PCB、查看进程的基本信息、杀掉进程,进行了介绍~

稍后本篇也会提及到部分内容。

2、进程标识符PID、PPID

1)pid介绍

进程标识符:pid

  • task_struct里面的一个变量
  • 当前操作系统中每个进程的唯一性标识

PPID:当前进程的父进程的标识符

获取 进程(pid) 的函数:getpid();
获取 父进程(ppid) 的函数:getppid();

进程的pid都是独一无二的,每次运行程序,都相当于一个新的进程,所以每启动一次程序,该进程的pid就会变化,但是父进程的PID不会变。(也就是无论孩子怎么变化,他的父亲该是谁,就是还是谁!)
在命令行运行的命令或程序都是bash的子进程;
几乎所有的指令都是程序,运行起来都是进程;

2)获取pid和ppid

  • #include <sys/types.h>
  • #include <unistd.h>
  •  pid_t getpid(void);
  •  pid_t getppid(void);

通过man 手册查询getpid()的使用。

man getpid

获取 进程(pid) 的函数:getpid();
获取 父进程(ppid) 的函数:getppid();

二、fork函数

1、fork的基本介绍

我们先来看看Linux系统中对于fork函数是怎么介绍的!

使用man指令查询

man fork

可见:

1)fork(): 创建子进程

2)对于函数具体的描述

  • fork()通过复制调用进程来创建一个新进程。
  • 新进程被称为子进程。调用进程被称为父进程。
  • 子进程和父进程在单独的内存空间中运行。
  • 子进程几乎是父进程的完全拷贝(某些情况例外)

3)两个返回值

如果成功,则在父进程中返回子进程的PID,并在子进程中返回0。
如果失败,则在父进程中返回-1,不创建子进程,并适当设置errno

这就是fork的特性,具有两个返回值!

2、感受一下fork的使用

让我们根据以下代码,对于fork的使用进一步了解。

我们希望这段代码能够达到,

创建进程之后,

父进程可以输出自己的pid、以及自己对应的父进程的pid、还有fork子进程的pid

子进程可以输出自己的pid、以及自己对应的父进程的pid、还有返回值0

#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>int main()
{printf("创建子进程前,这只有一个进程,pid:%d,ppid:%d\n",getpid(),getppid());sleep(3);//休眠3秒//我们创建进程,感受父子进程printf("创建新的进程了\n");pid_t id = fork();//接收返回值//利用条件选择语句,区分父、子进程//id = 0 --> 当前进程是子进程//id = 子进程的pid  -->那么当前进程是父进程if(id == 0){//子进程while(1){printf("after fork, 我是子进程: I am a prcess, pid: %d, ppid: %d, return id: %d\n\n", getpid(), getppid(), id);sleep(1);//休眠1秒,避免刷新太快}}else{//父进程while(1){printf("after fork, 我是父进程: I am a prcess, pid: %d, ppid: %d, return id: %d\n\n", getpid(), getppid(), id);sleep(1);}}return 0;
}

具体的pid变化过程:

父子进程可以执行不同的任务

通过上面的代码,可以看到父、子进程可以利用条件选择if else同时执行不同的内容。

那么,子进程就可以做一些父进程不能做,又或是不敢做的事情,这样既不会危害到父进程,也能完成任务。


通过上文的代码实现,我们可以对fork又有了进一步的了解。那么对于fork,又会引发对应的问题.

首先,子进程究竟有什么作用?

以及创建的子进程的存储和进程信息都是如何确定的?

为什么fork要有两个返回值?

为什么fork要给子进程返回0,给父进程返回子进程pid?

父子进程怎么实现执行的不同的内容


3、子进程的作用?-->实现不同于父进程的功能

通过上面的代码和图文,可以发现fork之前的代码只有父进程执行
然而fork之后的代码父子进程都要执行

创建子进程的意义就是
为了让子进程执行和父进程不一样的代码,实现和父进程不一样的功能。

比如我们可以一边下载聊天,一边播放音乐,这两个过程就是不同的进程在执行!



那修改一下功能,

    #include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>int main()
{printf("创建子进程前,这只有一个进程,pid:%d,ppid:%d\n",getpid(),getppid());sleep(3);//休眠3秒//我们创建进程,感受父子进程printf("创建新的进程了\n");pid_t id = fork();//接收返回值//利用条件选择语句,区分父、子进程//id = 0 --> 当前进程是子进程//id = 子进程的pid  -->那么当前进程是父进程if(id == 0){//子进程while(1){printf("after fork, 我是子进程,正在播放视频: pid: %d, ppid: %d, return id: %d\n\n", getpid(), getppid(), id);sleep(1);//休眠1秒,避免刷新太快}}else{//父进程while(1){printf("after fork, 我是父进程,正在进行聊天: pid: %d, ppid: %d, return id: %d\n\n", getpid(), getppid(), id);sleep(1);}}return 0;
}

可以查看图片,确实父子进程分别在 实现不同的功能。

4、创建的子进程如何存储,PCB都是如何确定的?-->子进程(PCB单独创建)和父进程共享代码和数据

fork会创建子进程,系统中会多出一个子进程。

操作系统以父进程为模板,为子进程创建PCB,但是子进程中是没有代码和数据的,所以子进程和父进程共享代码和数据
所以fork之后,父子进程会执行一样的代码。

5.为什么fork要有两个返回值?

  • 返回不同的返回值,是为了 区分父、子
  • 为了让fork以后的if、while等条件判断语句进行区分,来让父子进程执行 不同 的代码片段
  • 两个进程可以执行不同的功能(进程之间是相互独立的)

最重要的也是因为,

fork执行过程中,

子进程被创建完毕,自然就会会执行父进程中的代码和数据,自然也会执行到return这一句。

因此,父子都会返回一个数值,产生了两个返回值。

以下,是fork函数执行的大概逻辑!!

可以发现,在fork函数return之前,
就已经创建了子进程,并且将子进程放入调度队列中运行了,

所以当子进程,在调度队列时,它和父进程就已经分流了


而不是真正在fork函数return之后才分流的

并且创建完子进程后代码是共享的,在fork以后的代码执行了两次
很明显return也是一句代码。

所以父子进程,都会执行return语句,fork函数有两个返回值

部分内容参考这个篇文章:【linux进程(二)】如何创建子进程?--fork函数深度剖析_linux系统用for循环创建三个子进程-CSDN博客

6.为什么fork要给子进程返回0,给父进程返回子进程pid?

  • 一个进程只能有一个父进程,但是可以拥有多个子进程。
  • 一个父进程可以创建很多个子进程,然而一个子进程只对应一个父进程,所以fork函数会返回子进程的pid给父进程,方便父进程对于子进程进行管理。

7.fork之后,父子进程谁先运行?

创建完成子进程后,这只是一个开始,系统的其他进程,父进程,子进程接下来会被调度执行!

问题是先调度谁?先创建就先调度吗?

答案明显不是!不能确定谁先执行。

在调度队列中,CPU会选择一个进程去运行它,谁先被调度,谁就先运行!

所以fork之后父子进程谁先运行,用户是不确定的,这是由各自进程PCB中的调度信息决定的,比如优先级,算法信息等等       

8.一个变量怎么会有不同的内容?-->地址空间

我们继续使用一开始的代码。

在这段代码中,可以看到,if  else两个都被执行了,也就是说,id这个变量满足两个不同的条件。-----> id这个变量有两个值。

#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>int main()
{printf("创建子进程前,这只有一个进程,pid:%d,ppid:%d\n",getpid(),getppid());sleep(3);//休眠3秒    printf("创建新的进程了\n");pid_t id = fork();//接收返回值if(id == 0){//子进程while(1){printf("after fork, 我是子进程: pid: %d, ppid: %d, return id: %d\n\n", getpid(), getppid(), id);sleep(1);}}else{//父进程while(1){printf("after fork, 我是父进程:pid: %d, ppid: %d, return id: %d\n\n", getpid(), getppid(), id);sleep(1);}}return 0;
}

但是同一个变量怎么可能有两个不同的值呢?

进程是有独立性的。
首先是表现在进程各自的PCB运行时不会相互影响。

很明显,代码本身只是可读的,所以不会影响代码。

但是对于数据来说,父子的数据是可能不同的(可能会被修改)

所以系统是怎样做到让数据在各个进程都自己私有一份的?

答案是写时拷贝(类似于学习类和对象时的深浅拷贝数据会在需要使用时,被写时拷贝到PCB。)
然而fork返回值赋值给变量时,本质也是写入,返回时也会发生写时拷贝,所以不同
的进程执行的代码中的变量id获取的值不同!

父、子进程空间分配的文章

linux多进/线程编程(2)—— fork函数和进程间“共享”数据 - 胖白白 - 博客园 (cnblogs.com)

进程专题02篇———进程共享(读时共享写时复制copy-on-write)原理详解——超经典-CSDN博客

深入学习fork的文章推荐:

【创建进程】fork函数与写时拷贝-阿里云开发者社区

fork()写时复制原理-阿里云开发者社区

参考文章:

【Linux系列】fork( )函数原理与应用详解——了解【父子进程及其特性】(代码演示,画图帮助理解,思维导图,精简)(11)_linux fork-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

yuque-dl-语雀知识库下载为本地markdown

之前有下载语雀的需求&#xff0c;发现了一个开源项目&#xff0c;用起来还是很顺手的 需要环境 需要前端的node环境 https://nodejs.cn/ Node.js 18.4 or later 插入 npm i -g yuque-dl 使用 $ yuque-dl --helpUsage:$ yuque-dl <url>Commands:<url> …

Python 如何读取和写入文件

Python 如何读取和写入文件 文件操作是编程中常见的任务&#xff0c;几乎所有编程语言都支持文件的读写功能。在 Python 中&#xff0c;文件操作简单直观&#xff0c;适用于从初学者到经验丰富的开发者。无论是处理小型文本文件&#xff0c;还是需要处理大型数据文件&#xff…

fo-dicom是如何实现DICOM 的网络通信功能

一、前言 前面的文章&#xff0c;我们介绍了fo-dicom是一个怎样的开源库等一些内容&#xff1a; fo-dicom&#xff0c;第一个基于.NET Standard 2.0 开发的DICOM开源库fo-dicom开源库是如何满足 DICOM标准的基本要求fo-dicom开发之DICOM数据解析&#xff1a;常见数据类型及处…

【Transformers基础入门篇2】基础组件之Pipeline

文章目录 一、什么是Pipeline二、查看PipeLine支持的任务类型三、Pipeline的创建和使用3.1 根据任务类型&#xff0c;直接创建Pipeline&#xff0c;默认是英文模型3.2 指定任务类型&#xff0c;再指定模型&#xff0c;创建基于指定模型的Pipeline3.3 预先加载模型&#xff0c;再…

pycharm恢复两边侧边栏常驻显示

问题&#xff1a; pycharm两边的侧边栏菜单默认不显示&#xff08;打开project还得用alt1快捷键&#xff09;&#xff0c;非常不方便&#xff0c;如下图&#xff1a; pycharm版本&#xff1a;2022.3 professional 勾选&#xff1a;setttngs -> Appearance -> tool Wind…

云原生虚拟化kubevirt安装

kubevirt 介绍 Kubevirt 是 Redhat 开源的一套以容器方式运行虚拟机的项目&#xff0c;通过 kubernetes 云原生方式来管理虚拟机生命周期。它通过使用自定义资源&#xff08;CRD&#xff09;和其它 Kubernetes 功能来无缝扩展现有的集群&#xff0c;以提供一组可用于管理虚拟机…

JavaScript的注释与常见输出方式

注释 源码中注释是不被引擎所解释的&#xff0c;它的作用是对代码进行解释。Javascript 提供两种注释的写法:一种是单行注释&#xff0c;用//起头;另一种是多行注释&#xff0c;放在/*和*/之间。 单行注释&#xff1a; //这是单行注释 多行注释&#xff1a; /*这是 多行 注…

远程升级,你成功了吗?

最近又遇到了远程升级失败的情况&#xff0c;而且是不明原因的多次接连失败。。。 事情是这样的&#xff1a;最近有客户反馈在乡村里频繁出现掉线的情况。通过换货、换SIM卡对比排查测试&#xff0c;发现只有去年5月22号采购的那批模块在客户环境附近会出现掉线的情况&#xf…

服务器操作系统【sar 命令】

sar 安装、语法参数说明以及示例 文章目录 功能概述一、功能介绍1.安装配置2. 配置3. 启动二、sar 语法及参数说明三、示例及释义1.汇报 io 传输速率信息2.内存分页信息3.块设备状态信息4.hugepages 利用率统计信息5.列长度和负载平均值6.内存利用率统计信息7.swap 交换空间利用…

Redis数据持久化总结笔记

Redis 是内存数据库&#xff0c;如果不将内存中的数据库状态保存到磁盘&#xff0c;那么一旦服务器进程退出&#xff0c;服务器中的数据库状态也会消失。所以 Redis 提供了持久化功能&#xff01; Redis 提供了 2 个不同形式的持久化方式 RDB&#xff08;Redis DataBase&#…

VS2019配置Open3Dv0.18.0版本库

文章目录 一、引言二、配置过程三、举个例子参考资料一、引言 现在如果直接使用vs2019对Open3D(v0.15.2)进行编译,会比较麻烦,一是需要科学上网,另一个就是容易出现错误,这里就仍然按照之前的思路来配置新版本的Open3D(VS2015(及以上版本)配置Open3Dv0.15.2版本库)。 二…

科研小白入门工具

三、科研绘图 1.流程图绘制工具&#xff1a;powerpoint、亿图图示、visio、draw.io 2.绘制标准&#xff1a;布局合理、色彩鲜明、字体大小、矢量输出 矢量图绘制推荐流程&#xff1a;亿图图示绘制--visio--word--pdf无损放大 3.文章插图&#xff1a;excel、origin、matlab、…

【JUC并发编程系列】深入理解Java并发机制:Volatile从底层原理解析到高级应用技巧(六、Volatile关键字、JMM、重排序、双重检验锁)

文章目录 【JUC并发编程系列】深入理解Java并发机制&#xff1a;Volatile从底层原理解析到高级应用技巧(六、Volatile关键字、JMM、重排序、双重检验锁)1. Volatile的特性2. Volatile的用法3. CPU多核硬件架构剖析4. JMM内存模型4.1 主要特性4.2 JMM 的工作原理4.3 实现机制 5.…

电商跨境电商商城系统/网上商城接口/电商数据接口详情

电商API接口背景&#xff1a;电商运营中&#xff0c;数据分析这项工作越来越重要&#xff0c;许多品牌方也越来越热衷去做电商数据分析。不过&#xff0c;全面的数据该如何获取呢&#xff0c;此时&#xff0c;电商数据接口的重要性便凸显出来了。 电商API数据接口主要有以下特…

ASP.NET Core8.0学习笔记(十九)——EF Core DbSet

一、DbSet概述 1.DbSet提供了通过DbContext对表进行查询操作的路径。DbSet对应的属性名称将默认映射为实体T的表名。 2.使用DbSet<T>进行查询的方法&#xff1a; (1)直接在DbContext中创建对应的DbSet<T>属性 (2)使用DbSet DbContext.Set<T>方法操作数据表。…

对c语言中的指针进行深入全面的解析

1.普通的指针: 实际上指针就是存放地址的变量&#xff0c;eg: int a10; int *p&a; 拆分一下int *中的*说明p是一个指针&#xff0c;int是它所指向的类型&#xff1b; 2.字符串指针和字符串数组 char*str1"abcd"; 先看这一个&#xff0c;这个就是一个字符串…

[vulnhub] Hackademic.RTB1

第一次打靶机&#xff0c;思路看的红队笔记 https://www.vulnhub.com/entry/hackademic-rtb1,17/ 环境&#xff1a;kali Linux - 192.168.75.131&#xff0c;靶机 - 192.168.75.132 主机发现和端口扫描 扫描整个网络有哪台机子在线&#xff0c;不进行端口扫描 nmap -sP 192.16…

关于API概念:连接数字世界的桥梁

在数字化时代&#xff0c;信息和数据的流动是构建现代应用程序的基础。API&#xff08;应用程序编程接口&#xff09;作为连接不同软件和服务的桥梁&#xff0c;正逐渐成为现代技术架构中不可或缺的一部分。本文将探讨API的概念、重要性以及它如何塑造我们的数字生活。 什么是A…

解决Echarts:宽度100%,渲染的宽度却是100px

为什么我们宽度设置了100%&#xff0c;结果变为了100px&#xff1f; 源码这里没有获取到clientWidth&#xff0c;会将设置的width:100%转换称100px 解决办法&#xff1a; <div ref"numberPieRef"></div>let numberPieRef ref(null); let myChart nu…

基于二自由度汽车模型的汽车质心侧偏角估计

一、质心侧偏角介绍 在车辆坐标系中&#xff0c;质心侧偏角通常定义为质心速度方向与车辆前进方向的夹角。如下图所示&#xff0c;u为车辆前进方向&#xff0c;v为质心速度方向&#xff0c;u和v之间的夹角便是质心侧偏角。 质心侧偏角的作用有如下三点&#xff1a; 1、稳定性…