Linux 子进程 -- fork函数

news2024/11/29 9:57:17

子进程

什么是子进程?

子进程指的是由一个已经存在的进程(称为父进程或父进程)创建的进程.

如: OS (操作系统) 就可以当作是一个进程, 用来管理软硬件资源, 当我点击浏览器, 想让浏览器运行起来时, 实际上是由 OS 接收指令, 然后 OS 帮我们将浏览器运行起来, 浏览器就是一个子进程, 是由 OS 创建出来的进程, 所以 OS 就是浏览器这个进程的父进程.
( OS 并不是一个单独的进程, 是由多个进程和线程组成的复杂结构, 这里为了方便理解, 将整个 OS 当作是一个进程)

如何创建子进程

在 Linux 中, 操作系统为我们提供了一个系统调用函数: fork()

fork() 函数可以在程序中, 创建一个子进程.

#include <sys/types.h>
#include <unistd.h>
// 以上为所需的库函数

// 函数声明
pid_t fork(void);

可以看到这个函数没有参数, 有一个 pid_t 类型的返回值.

成功后, 子进程的PID将在父进程中返回, 0将在子进程中返回.

失败时, -1在父进程中返回, 不创建子进程, 并且适当设置了errno.

#include <iostream>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    pid_t pid = fork();
    if(pid == -1)
    {
        std::cout << "fork error" << std::endl;
    }
    if(pid == 0)
    {
        std::cout << pid << ": this is child" << std::endl;
    }
    else
    {
        std::cout << "创建的子进程的pid: " << pid << std::endl;
    }
    return 0;
}

可以看到程序运行之后, 打印出了两条语句.
创建出来的子进程的 pid 是 598281.

但是我们只是看到了有进程被创建了, 怎么证明他们之间是父子关系?

先来介绍两个函数: 

#include <sys/types.h>
#include <unistd.h>

pid_t getpid(void); // 返回当前进程的 pid
pid_t getppid(void); // 返回当前进程的 父进程的 pid

然后将上面的代码修改一下

#include <iostream>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    pid_t pid = fork();
    if(pid == -1)
    {
        std::cout << "fork error" << std::endl;
    }
    if(pid == 0)
    {
        std::cout << "this is child ppid: " << getppid() << " pid: " << getpid() << std::endl;
    }
    else
    {
        std::cout << "this is parent pid: " << getpid() << std::endl;
    }
    return 0;
}

 

可以观察到子进程打印出来的父进程的pid, 和父进程的 pid 是相同的, 所以他们之间确实是父子关系.

fork 函数讲解 一

从上面的代码我们可以感知到, 子进程所执行的代码, 是 fork 函数之后的代码.
否者子进程也从头开始执行, 那么就形成了一个死循环, 子进程还回去创建自己的进程.
就会创建出无数的进程, 最后软件资源也就被分配完了, 电脑也可以关机重启了.

那么 fork 函数有什么作用呢?

#include <iostream>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    pid_t pid = fork();
    if(pid == -1)
    {
        std::cout << "fork error" << std::endl;
    }
    if(pid == 0)
    {
        // 执行子进程的专有代码
    }
    else
    {
        // 执行父进程的专有代码
    }
    return 0;
}

比如: 我在使用 QQ 聊天, 此时有人发了一个文件给我, 我点击接收文件, 然后 QQ 就创建了一个子进程去执行文件的下载, 这样在文件下载的时候, 我还是能聊天. 

#include <iostream>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    pid_t pid = fork();
    if (pid == -1)
    {
        std::cout << "fork error" << std::endl;
    }
    if (pid == 0)
    {
        while (1)
        {
            std::cout << "我在聊天" << std::endl;
        }
    }
    else
    {
        while (1)
        {
            std::cout << "我在下载东西" << std::endl;
        }
    }
    return 0;
}

fork 函数讲解 二

那么 fork 函数究竟是怎么创建出子进程的?

fork 函数创建的子进程, 实际上是一个父进程的副本, 子进程中没有代码和数据, 子进程和父进程共享代码和数据

所以 fork 之后父子进程执行的代码是一样的.

那么问题来了, 在上面创建子进程的代码中, 我们通过 fork 的返回值 pid 来区分父子进程.

#include <iostream>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    pid_t pid = fork();
    if(pid == -1)
    {
        std::cout << "fork error" << std::endl;
    }
    if(pid == 0)
    {
        // 执行子进程的专有代码
    }
    else
    {
        // 执行父进程的专有代码
    }
    return 0;
}

我们又说父子进程之间是共享代码和数据的, 那么 pid 这个变量也是两方共享的, pid 是一个变量, 一个变量怎么可能同时存在多个值.

fork 函数讲解 三

(1) 两个值从哪来

首先我们要知道, 这两个值是从哪里来的?

之前说了, 父子进程都会执行 fork 函数之后的内容. 
当 fork 函数执行到 return 时, fork 函数才算结束.

那么我们可以看到, 在 fork 函数执行 return 之前, 子进程已经被创建, 并且运行起来了.
之前所说的 子进程 执行 fork 函数之后的代码, 是不准确的,
在 fork 的内部还有一部分代码子进程是会执行的, 从放入调度队列之后.

那么也就是说, 父子进程都是执行 fork 的 return 函数, 那么 return 函数就被执行了两次, 那么也就出现了两个返回值.

(2) 一个变量怎么有多个值

我们已经知道了两个值是怎么来的.
那么下一个问题: 为什么 pid 会有两个值?

之前我们说过了, 父子进程共享数据, 那么父子进程访问同一个变量, 得到的值应该是相同的.

首先我们需要知道: 进程之间具有独立性, 进程有自己的堆栈, 数据 .......
在上面我们说了, 父子进程之间共享数据, 那么当父子进程运行之后, 可能就会修改数据
那么无论哪一方修改数据, 对另一个进程而言, 它的独立性都遭到了破坏. 这是不合理的.

所以, 为什么在创建子进程的时候, 父子进程共享代码和数据?

因为: 拷贝数据需要消耗资源, 而父子进程在运行时, 可能都不会发生修改数据的操作.

那么我就在父子进程修改数据之前, 让父子进程共享,

如果有进程发生了修改数据的操作, 此时我们在将父子进程的数据分离开来. (写时拷贝)

父子进程各自有一份独立的数据 ,各自维护

fork 函数的返回值, 赋值给 pid 时, 也就是发生了数据的修改, 那么此时就会发生写时拷贝, 将父子进程之间的资源分离开, 各自维护各自的数据.

所以当我们在父子进程使用同一个对象时, 获取的 值 不同也就可以理解了.
pid 看上去时一个变量, 实际上是在父子进程中的 两个不同的变量, 只是在父子进程中名称相同.

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

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

相关文章

DataLoade类与list ,iterator ,yield的用法

1 问题 探索DataLoader的属性&#xff0c;方法 Vscode中图标含意 list 与 iterator 的区别&#xff0c;尤其yield的用法 2 方法 知乎搜索DataLoader的属性&#xff0c;方法 pytorch基础的dataloader类是 from torch.utils.data.dataloader import Dataloader 其主要的参数如下&…

C++入门——“C++11-lambda”

引入 C11支持lambda表达式&#xff0c;lambda是一个匿名函数对象&#xff0c;它允许在函数体中直接定义。 一、初识lambda lambda的结构是&#xff1a;[ ] () -> 返回值类型 { }。从左到右依次是&#xff1a;捕捉列表 函数参数 -> 返回值类型 函数体。 以下是一段用lam…

【Linux网络编程】第二弹---Socket编程入门指南:从IP、端口号到传输层协议及编程接口全解析

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】 目录 1、Socket 编程预备 1.1、理解源 IP 和目的 IP 1.2、认识端口号 1.2.1、端口号范围划分 1.2.2、理解 &q…

《用Python实现3D动态旋转爱心模型》

简介 如果二维的爱心图案已经无法满足你的创意&#xff0c;那今天的内容一定适合你&#xff01;通过Python和matplotlib库&#xff0c;我们可以实现一个动态旋转的3D爱心模型&#xff0c;充满立体感和动感。# 实现代码&#xff08;完整代码底部名片私信&#xff09; 以下是完…

shell-函数调用进阶即重定向

shell-函数调用进阶 声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷…

【高等数学学习记录】微分中值定理

一、知识点 &#xff08;一&#xff09;罗尔定理 费马引理 设函数 f ( x ) f(x) f(x) 在点 x 0 x_0 x0​ 的某邻域 U ( x 0 ) U(x_0) U(x0​) 内有定义&#xff0c;并且在 x 0 x_0 x0​ 处可导&#xff0c;如果对任意的 x ∈ U ( x 0 ) x\in U(x_0) x∈U(x0​) &#xff0…

【vue-router】vue-router如何实现动态路由

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Web前端技术浅谈CooKieAG网址漏洞与XSS攻防策略

随着互联网技术的飞速发展,Web前端开发已经成为构建网站和应用程序的重要环节。然而,Web前端开发中存在许多安全问题,这些问题不仅会影响用户体验,还可能给企业和个人带来严重的经济损失。但是web前端安全方面技术包含的东西较多&#xff0c;我们这里着重聊一聊关于XSS 的危害与…

关于VNC连接时自动断联的问题

在服务器端打开VNC Server的选项设置对话框&#xff0c;点左边的“Expert”&#xff08;专家&#xff09;&#xff0c;然后找到“IdleTimeout”&#xff0c;将数值设置为0&#xff0c;点OK关闭对话框。搞定。 注意,服务端有两个vnc服务,这俩都要设置ide timeout为0才行 附件是v…

51c自动驾驶~合集35

我自己的原文哦~ https://blog.51cto.com/whaosoft/12206500 #纯视觉方案的智驾在大雾天还能用吗&#xff1f; 碰上大雾天气&#xff0c;纯视觉方案是如何识别车辆和障碍物的呢&#xff1f; 如果真的是纯纯的&#xff0c;特头铁的那种纯视觉方案的话。 可以简单粗暴的理解为…

计算分数的浮点数值

计算分数的浮点数值 C语言代码C 代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 两个整数a和b分别作为分子和分母&#xff0c;既分数 a/b &#xff0c;求它的浮点数值&#xff08;双精度浮点数&#xff0c;保留小数点…

戴尔电脑安装centos7系统遇到的问题

1&#xff0c;找不到启动盘&#xff08;Operation System Loader signature found in SecureBoot exclusion database(‘dbx’).All bootable devices failed secure Boot Verification&#xff09; 关闭 Secure Boot&#xff08;推荐&#xff09;&#xff1a; 进入 BIOS/UEFI…

简单获取json预览

data: JSON 数据。 collapsedNodeLength: 对象或数组的长度超过此阈值时会折叠 deep: json路径深度超过此值时会折叠 showLineNumber: 显示左侧行号 showIcon: 显示图标。 virtual: 使用虚拟滚动 height: 使用虚拟滚动时列表的高度 itemHeight: 使用虚拟滚动时节点的高…

ChatGPT/AI辅助网络安全运营之-数据解压缩

在网络安全的世界中&#xff0c;经常会遇到各种压缩的数据&#xff0c;比如zip压缩&#xff0c;比如bzip2压缩&#xff0c;gzip压缩&#xff0c;xz压缩&#xff0c;7z压缩等。网络安全运营中需要对这些不同的压缩数据进行解压缩&#xff0c;解读其本意&#xff0c;本文将探索一…

Cookie概念和API

Cookie概念 Cookie在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie就是一个键和一个值构成的&#xff0c;随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来&#xff0c;当下一次再访问服务器时把Cookie再发送给服务器。 Cookie是由服务器…

qt音频实战

一、Qt音频基础知识 1、QT multimedia 2、QMediaPlayer类&#xff1a;媒体播放器&#xff0c;主要用于播放歌曲、网络收音机等功能。 3、QMediaPlaylist类&#xff1a;专用于播放媒体内容的列表。 二、界面设计 三、代码 #include "mainwindow.h" #include "…

GDPU Android移动应用 数据存储

又是学到了数据持久化。 登录界面 题外话&#xff1a;有无动画大佬带带呀&#xff0c;前端移动端可免( •̀ .̫ •́ )&#xff0c;合作可私信哦。 1.用户登陆和“记住我”功能 该内容拥有两个Activity活动视图&#xff1a; &#xff08;1&#xff09;LoginActivity&#x…

Java算法OJ(11)双指针练习

目录 1.前言 2.正文 2.1存在重复数字 2.1.1题目 2.1.2解法一代码 解析&#xff1a; 2.1.3解法二代码 解析&#xff1a; 2.2存在重复数字plus 2.2.1题目 2.2.2代码 2.2.3解析 3.小结 1.前言 哈喽大家好吖&#xff0c;今天来给大家分享双指针算法的相关练习&…

天锐绿盾加密软件与Ping32联合打造企业级安全保护系统,确保敏感数据防泄密与加密管理

随着信息技术的飞速发展&#xff0c;企业在日常经营过程中产生和处理的大量敏感数据&#xff0c;面临着越来越复杂的安全威胁。尤其是在金融、医疗、法律等领域&#xff0c;数据泄漏不仅会造成企业巨大的经济损失&#xff0c;还可能破坏企业的信誉和客户信任。因此&#xff0c;…

Git上传本地项目到远程仓库(gitee/github)

目录 序言一、创建git本地版本库二、连接远程仓库&#xff08;以gitee为例&#xff09;三、将项目提交到git&#xff08;本地&#xff09;版本库1.由工作区添加到暂存区2.由暂存区添加到版本库 四、将代码由本地仓库上传到 gitee远程仓库1.获取远程库与本地同步2.把当前分支 ma…