进程概念(三)----- fork 初识

目录

  • 前言
  • 1. pid && ppid
  • 2. fork
    • a. 为什么 fork 要给子进程返回 0, 给父进程返回子进程的 pid ?
    • b. 一个函数是如何做到两次的?
    • c. fork 函数在干什么?
    • d. 一个变量怎么做到拥有不同的内容的?
    • e. 拓展:fork()之后,父子进程谁先运行?

前言

该篇文章是继 添加链接描述 文章的后续,针对 linux 中的 task_struct 进程的 PID(也即标识符)介绍,和系统调用中的 fork 展开初步的认识。

task_ struct 内容分类:
标识符:  描述本进程的唯一标识符,用来区别其他进程。
状态:  任务状态,退出代码,退出信号等。
优先级:  相对于其他进程的优先级。
程序计数器:  程序中即将被执行的下一条指令的地址。(其作用就相当于进程运行了一段时间后,因为系统调度等原因,停止了对该进程的执行而后续回来继续执行的时候,需要知道上次执行到什么地方了。也好比我们看书,今天看完不想看了之后,会在此处做一下标记,方便后续继续向下观看)
内存指针:  包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针(比如记录了该进程所匹配的代码和数据的存储位置)
上下文数据:  进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
I/O状态信息:  包括显示的I/O请求,分配给进程的 I/O 设备和被进程使用的文件列表。
记账信息:  可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。(保证系统调度的公平等)
其他信息


1. pid && ppid

在前面的进程相关的文章,我们已经知道了,因为用户不擅长直接对操作系统进行访问,并且操作系统也不会相信用户,因此用户无法直接访问操作系统。而在上一篇文章的末尾,我们简单见过了进程的 PID,但是那是通过系统指令获取到的 PID,而作为用户在编程语言上,在无法访问操作系统拿到数据的前提下,如何获取进程的 PID 等进程信息呢?

// 测试demo
#include<iostream>
#include<unistd.h>
using namespace std;int main()
{while(1){cout << "I am a process, my pid is " << getpid() << ", my parentId is " << getppid() << '\n';sleep(1);}return 0;
}

在这里插入图片描述

左右对比,我们是可以得知,在我们通过 c / c++ 编写的程序,运行起来后,系统会自动会该进程创建一个 PID,并且在cpp 中预取这个 PID 和我们在系统中获取的 PID 是一致的。

所以,PID 有什么用呢?? ---- 既然 PID 是每个进程的唯一标识符,那么当我们对进程进行管理的时候,就可以通过其PID 对该进程进行操作,比如杀掉该进程 kill -9 20059 等操作。

在这里插入图片描述

而当我们结束上一次进程,重新运行我们的程序时,我们又会发现,它的 pid 不一样了,这也是正常的现象。但是我们可以发现,进程自己的 pid 会变,但是他的父进程的 pid 确不会变!一直是 2742,好奇心驱动我们去查看这个父进程,它到底是谁!

在这里插入图片描述

没错!2742 它就是我们的 bash,这个 bash 进程是由 xshell 为我们创建的一个命令行的进程!所以同理,当我们断开对 linux 的链接之后,再一次链接 linux,进程的 ppid 也会随之改变,即我们每一次通过xshell 远程连接我们的服务器时,xshell 都会为我们重新分配一个 bash 进程,用来给我们提供命令行服务!


2. fork

但是上述的这些进程,可都是操作系统给我们创建出来的。那么现在我要手动创建进程,该怎么做呢? ---- 调用 fork 系统函数。那么修改后的 demo 代码如下:

int main()
{cout << "before the fork!\n";fork();cout << "after the fork!\n";sleep(1)return 0;
}

在这里插入图片描述

很明显,在执行完 fork函数之后,fork 之后的代码,被执行了 两次!为什么?最简单的回答就是,因为 fork 是创建进程的系统函数,因此在执行完 fork 之后,会有两个进程,同时执行这一份代码,所以代码一共被执行了两次!

但是上述仅仅是我们的猜测,为了更清楚的了解 fork 函数干了什么,我们需要通过 man 手册进一步了解 fork 函数!

在这里插入图片描述

以上是 fork 函数介绍中的一段信息,但是有一点奇怪的是!这个函数怎么可以返回两个值呢??据 man 手册的介绍,如果进程创建成功,返回这个进程的 pid 给它的父进程,返回 0 给自己;创建失败,则返回 -1 给父进程。这显然是我们无非理解的,在 C/C++ 语言当中,函数的返回值一直都只能有一个啊!而现在,这个 fork 函数,它告诉我有两个返回值!

多的不说,我们再来做一个 demo 测试,到底是不是真的这样,这个函数有两个返回值。

int main()
{cout << "I am a process, pid: " << getpid() << ", ppid: " << getppid() << '\n';pid_t pid = fork();if(pid == 0){// 子进程部分while(1){cout << "I am a child process, pid: " << getpid() << ", ppid: " << getppid() << "\n";sleep(1);}}else if(pid > 0){// 父进程部分while(1){cout << "I am a parent process, pid: " << getpid() << ", ppid: " << getppid() << "\n";sleep(1);}}else{cout << "error\n";}return 0;
}

在这里插入图片描述

没错!就是这么神奇,如果站在语言上的认知,这是根本无法理解的现象,每个 if 分支里面都是一个死循环,但是,运行起来确出现了两个 if 分支里的内容!所以这可以进一步的说明了,fork 之后,会多一个进程,而这个进程是由原来的进程所创建出来的,它的 ppid (即父进程)就是创建它的进程的 pid !而 fork 作为系统调用接口,也会为创建出来的进程进行属性初始化(分配 pid 等操作)。所以站在系统的角度看待这一段代码的话,那么就能说明原本的进程,作为父进程,在执行完 fork 函数之后,接收到的是创建出来的子进程的 pid,因此会执行第一个 if 分支,而子进程自己接收到 0 的返回值,所以执行第二个 if 分支的内容。

一般而言,fork 之后的代码,是父进程与子进程共享的!所以返回值的不同,恰恰是为了区分,让不同的执行流执行不同的代码块!

接下来,我们要回答几个关于 fork 的问题。

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

你知道的,父亲永远只有一个,而孩子可以有很多个,一个孩子也不可能同时有两个父亲。因此在操作系统中同理,一个父进程可以有多个子进程,但是不可能存在一个子进程有多个父进程!所以把子进程的 pid 给父进程,是为了让父进程可以明确的找到它的子进程,而子进程永远只有一个父进程,就不用谈找不到这件事了。假设今天父进程有10个子进程,但是它并不知道这个进程跟那个进程的 pid,也就无法明确指定操作其中某一个子进程了!再者,父进程在创建时,父进程有自己的内核 pcb 数据结构,也有自己的代码和数据,那现在父进程创建出一个子进程之后,系统会为子进程其分配一个 pcb,这没问题,但是子进程应该执行什么样的代码和访问什么样的数据呢? 而开始创建子进程是时候,子进程是没有自己的代码和数据的,所以子进程只能与父进程共享一样的代码(数据另谈)!那问题又来了,当 cpu 在调度的时候,父进程在执行这一份代码,子进程也是执行的这一份代码,这也是上面实验时,fork 之后的代码会重复执行两遍的原因。那这有什么意义呢?或者说,同样的代码为什么要执行两遍呢?所以问题就回归到了 我们为什么要创建这个子进程?! 所以一定是为了让父进程和子进程执行不同的代码,完成不同的工作!所以就需要让 fork 具有不同的返回值,才能达到区别不同的执行流!

b. 一个函数是如何做到两次的?

既然 fork 是用于创建进程的一个系统调用函数,而站在系统层面上,创建进程就要为其创建一个 pcb,并且每个进程需要有与其匹配的代码和数据,这是系统在创建进程时需要做的工作。那么我们就不难猜测,fork 在干什么。

pid_t fork(void)
{创建子进程填充 PCB 对应的内容让父子进程共享同一份代码到这一步,父子进程都用拥有了自己独立的 task_struct(即 PCB),可以被 cpu 调度运行了......return ret;
}

我们知道的是,子进程被创建出来之后,父子进程会共享代码,因此 fork 之后的代码才会被执行两次。现在的问题就是那么 return ret 是不是一条代码? ---- 它是代码是不争的事实!又因为在 return 之前子进程就已经被创建了,并且也完成了其 pcb 的各种初始化工作,同父进程一样拥有独立的 task_struct,所以既然在 return 语句的时候,子进程就已经完全存在了,那么 return 语句就自然是会被父子进程都执行!所以父进程返回一次,子进程返回一次,这个函数一共就返回了两次!

c. fork 函数在干什么?

其实这个问题在上面的介绍中,就已经不难得知了。fork 就是在创建一个进程,并且用其父进程对应的字段来初始化子进程,而因为子进程刚创建出来,没有自己的代码和数据,所以就需要和父进程共享同一份代码(数据另谈)。我们都知道,之所以可以共享代码,是因为代码存储在系统的常量区,它是不能够被修改的,因此父进程并不会因为与子进程进行代码共享而受到影响。但是数据就不一定,数据是可以被修改的!所以假如父进程和子进程共享同一份数据,然后子进程需要对其中的数据进行修改,这个数据又恰恰是父进程的某一个条件判断所需的数据,这就势必会影响到父进程的正常运行!那怎么办呢??所以子进程可以将父进程的数据拷贝一份给自己独立使用,自己怎么修改都不会影响到父进程。但是问题又来了,假设父进程当中有100个全局变量数据,而将来子进程只需要用到其中一个,其它的 99 个甚至更多都不需要修改,这样就会导致系统资源变少,利用率也变低。而实际上,被创建出来的子进程,需要修改到的数据其实是不大的,基本不可能有子进程需要修改父进程的全部数据。因此在面对数据方面,子进程则是采用了 写时拷贝 的策略,当子进程与父进程进行数据共享之后,子进程需要修改数据,系统就会为子进程开辟一块属于子进程自己的空间,并且将要修改的数据拷贝一份,供子进程修改和使用,子进程需要多少空间,系统就开辟多少,而后续子进程访问的也是自己的数据,这样父子进程就不会互相影响(因为我们需要保证让进程之间相互独立,互不影响。总不能是今天我csdn网页的进程奔溃了,连同我的音乐进程也奔溃了吧)。

d. 一个变量怎么做到拥有不同的内容的?

这个问题对 b 问题的一个延续,在上面的问答中,我们已经能够得知,一个函数之所以可以返回两个值,是因为在 return 的时候,子进程就已经被创建出来了,并且开始与父进程共享代码;在数据层面上,子进程并不是完全与父进程进行数据共享,而是采用了 写时拷贝 的策略。所以现在需要确定的是,return 是不是一种对数据的修改,或者是算不算数据的写入? ---- return 将数据进行返回,所以有数据,就必须要接收,也即数据的写入,而写入就是修改的范畴,所以当系统检测到子进程要对父进程的数据进行修改的时候,就会为其开辟一块自己的空间供子进程使用。所以站在系统层面上,这个变量数据在内存中存在两份,一份是父进程的,一份是子进程的,所以当我们站在语言的角度上,才看到了一个变量拥有两个不一样的值。

e. 拓展:fork()之后,父子进程谁先运行?

如果大家有这方面的疑惑的话,就需要深入了解在系统调度器方面的知识,因为谁先运行,这是调度器决定的!在系统层面上,谁先运行,取决于调度器决定先将哪个进程提携给 cpu 执行。而每个系统的调度原理都不太一样,所以这方面又是一门足以压死人的学问,小篇也是无能为力。


进程介绍到这里的时候,还远远没有结束,我们现在只是弄清楚进程是什么,以及与进程相关的系统调用 fork,但是进程还会有所谓的状态,比如进程等待,堵塞等等。但是由于篇幅问题,关于进程的状态等方面的信息,小篇会在后续文章中一一介绍。

如果感觉该篇文章给你带来了收获,可以 点赞👍 + 收藏⭐️ + 关注➕ 支持一下!

感谢各位观看!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小红书电商首提“生活方式电商”定义,个性化需求也能做成好生意

近日&#xff0c;小红书发布COO柯南与经济学者薛兆丰的对谈视频。对谈中柯南首次对外定义&#xff0c;小红书电商是“生活方式电商”。 柯南表示&#xff0c;生活方式电商是让用户在小红书买到的&#xff0c;不仅是好产品&#xff0c;也是一种向往的生活。 随着生活方式的多元…

【初阶数据结构】9.二叉树(4)

文章目录 5.二叉树算法题5.1 单值二叉树5.2 相同的树5.3 另一棵树的子树5.4 二叉树遍历5.5 二叉树的构建及遍历 6.二叉树选择题 5.二叉树算法题 5.1 单值二叉树 点击链接做题 代码&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* …

PID 控制实验 - 整定实验

Arduino PID Arduino-PID-LibraryArduino-PID-AutoTune-Library PID控制实验 – 制作测试台 PID Control Experiment – Making the Testing Rig PID (Proportional, Integral, Derivative) control is a classic control algorithm that I have used for a few projects,…

Java面试还看传统八股文?快来看看这个场景题合集吧【附PDF】

以下就是这份面试场景文档↓ 这里有什么&#xff1f; ↓↓ 1.针对 2024 年面试行情的变化设计的面试场景题以及回答思路 2. 如何快速通过面试的详细攻略 3. 简历优化技巧 1.知己知彼才能百战百胜&#xff0c;如何做好面试前的准备工作 场景题答案以及更多场景题八股文一线大…

java学习--枚举

问题引入&#xff1a; 当需要解决一个季节类的问题&#xff0c;我们使用学到的类与对象&#xff0c;创建一个季节的类然后添加构造器在进行分装就可以实现&#xff0c;但问题也随之而来&#xff0c;这样不仅可以有正常的四季还可以添加其他不存在的四季以及可以更改四季的属性…

【Leetcode】十九、贪心算法:玩筹码 + 跳跃游戏

文章目录 1、贪心算法2、leetcode1217&#xff1a;玩筹码3、leetcode55&#xff1a;跳跃游戏 1、贪心算法 关于贪心算法中&#xff0c;“每一步都是最好的选择"的理解”。以零钱兑换为例&#xff0c;现在有1分、2分、5分的硬币&#xff0c;现在要凑出11分&#xff0c;且要…

masscan 端口扫描——(Golang 简单使用总结)

1. 前言 最近要做一个扫描 ip 端口的功能 扫描的工具有很多&#xff0c;但是如何做到短时间扫描大量的 ip 是个相对困难的事情。 市场上比较出名的工具有 masscan和nmap masscan 支持异步扫描&#xff0c;对多线程的利用很好&#xff0c;同时仅仅支持 syn 半开扫描&#xff…

采用先进的人工智能视觉分析技术,能够精确识别和分析,提供科学、精准的数据支持的明厨亮灶开源了。

明厨亮灶视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。AI技术可以24小时…

SEO与数据中心代理IP的结合能带来哪些便利?

本文将探讨将SEO与数据中心代理IP结合所带来的好处&#xff0c;以及如何利用这种组合来提升网站在搜索引擎中的排名和可见性。 1. 数据中心代理IP的作用和优势 数据中心代理IP指的是由数据中心提供的IP地址&#xff0c;用于隐藏真实服务器的位置和身份。与其他类型的代理IP相…

【Java基础系列】RBAC:介绍与原理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【C++】:红黑树的应用 --- 封装map和set

点击跳转至文章&#xff1a;【C】&#xff1a;红黑树深度剖析 — 手撕红黑树&#xff01; 目录 前言一&#xff0c;红黑树的改造1. 红黑树的主体框架2. 对红黑树节点结构的改造3. 红黑树的迭代器3.1 迭代器类3.2 Begin() 和 End() 四&#xff0c;红黑树相关接口的改造4.1 Find…

Java OpenCV 图像处理41 图形图像 图片缩放

Java OpenCV 图像处理41 图形图像 图片缩放 1 图片缩放2 仿射变换3 透视变换 1 图片缩放 Java OpenCV 代码 OpenCV 提供的主要图像缩放函数&#xff0c;可以指定缩放比例或者目标尺寸。 Imgproc.resize(src, dst, new Size(width, height), fx, fy, interpolation);Imgproc.r…

科学又省力 宠物浮毛怎么去掉便捷高效?除毛秘籍养宠空气净化器

上次和朋友逛完街去她家&#xff0c;她家的猫哈基米一开门就飞奔过来&#xff0c;朋友直接抱起它狂亲。结果&#xff0c;猫毛和汗水粘得到处都是&#xff0c;手臂上、脸上都是&#xff0c;看得我这鼻炎星人直起鸡皮疙瘩。很多养宠物的朋友都说&#xff0c;天天给猫狗梳毛&#…

ProcessExplorer免费且功能强大的进程管理软件

ProcessExplorer是一款功能强大的进程管理软件&#xff0c;由Sysinternals开发&#xff0c;并被微软收购。它不仅可以管理和监控系统中的进程&#xff0c;还提供了许多实用的功能&#xff0c;如CPU和内存使用情况的曲线图表、DLL和句柄查看、进程冻结等。 安装ProcessExplorer…

微服务安全——OAuth2.1详解、授权码模式、SpringAuthorizationServer实战、SSO单点登录、Gateway整合OAuth2

文章目录 Spring Authorization Server介绍OAuth2.0协议介绍角色OAuth2.0协议的运行流程应用场景授权模式详解客户端模式密码模式授权码模式简化模式token刷新模式 OAuth 2.1 协议介绍授权码模式PKCE扩展设备授权码模式拓展授权模式 OpenID Connect 1.0协议Spring Authorizatio…

Axious的请求与响应

Axious的请求与响应 1.什么是Axious Axious是一个开源的可以用在浏览器和Node.js的异步通信框架&#xff0c;它的主要作用就是实现AJAX异步通信&#xff0c;其功能特点如下&#xff1a; 从浏览器中创建XMLHttpRequests ~从node.js创建Http请求 支持PromiseAPI 拦截请求和…

电信应用的振荡器基础知识

数字通信的最基本组成部分是同步。同步有很多方面。在数字传输中&#xff0c;同步是通过管理跨节点的平均传输和接收速率来管理无错误的传输和接收。在蜂窝通信中&#xff0c;同步使用户设备在移动中以及从一个小区移动到另一个小区时能够可靠地工作。在 5G 等先进网络中&#…

为什么w 和 b成同比例变化对超平面没有影响

文章目录 解释可视化证明数乘角度进行解释可视化代码领取 解释 在机器学习中&#xff0c;特别是支持向量机&#xff08;SVM&#xff09;和线性回归等模型中&#xff0c;参数 w w w和 b b b分别代表权重向量和偏置项。当说 w w w和 b b b成规模变化对超平面没有影响时&#xff…

pikachu靶场之目录遍历、敏感信息泄露

一、目录遍历 漏洞概述 在web功能设计中,很多时候我们会要将需要访问的文件定义成变量&#xff0c;从而让前端的功能便的更加灵活。 当用户发起一个前端的请求时&#xff0c;便会将请求的这个文件的值(比如文件名称)传递到后台&#xff0c;后台再执行其对应的文件。 在这个过…

邮件攻击案例系列二:冒充合作伙伴伪造发票商务邮件诈骗

案例描述 2023 年 11 月下旬&#xff0c;某知名外贸公司财务人员收到一封来自境外合作伙伴的邮件&#xff0c;说明有一张发票即将于 11 月 29 日到期的&#xff0c;希望该外贸公司能尽快付款。 该邮件有两个附件&#xff0c;一个附件是即将到期发票的电子版&#xff0c;一个附…