Linux进程——进程的创建(fork的原理)

前言:在上一篇文章中,我们已经会使用getpid/getppid函数来查看pid和ppid,本篇文章会介绍第二种查看进程的方法,以及如何创建子进程!


在这里插入图片描述


本篇主要内容:

  • 查看进程的第二种方法
  • 创建子进程
  • 系统调用函数fork

在这里插入图片描述


在开始前,我先来回顾一下如何获取pid,ppid
在这里插入图片描述

进程要想区分就一定会有唯一的标示符,而pid,ppid初始化后就变为内核中的数据,也就是操作系统里的数据,在我们自己开发时,操作系统不会将内部数据暴露出来,不能直接访问,所以通过系统调用接口直接获取pid,ppid。

目录

  • 1. 查看进程的第二种方法
  • 2. 创建子进程
    • 2.1 系统调用函数fork
    • 2.2 fork的一般写法
    • 2.3 fork的原理
  • 3. 总结


1. 查看进程的第二种方法

在Linux系统中,不只有ps能够查看进程,还存在着一个动态目录proc,该目录存放了所有存在的进程,目录的名称。它会随着进程的改变而随时更新它的内容!

查看所有进程:
指令:ls /proc/

在这里插入图片描述

查看指定pid的进程文件:
指令:ls /proc/进程pid

如果想只查看这个目录我们可以:
指令:ls /proc/进程pid -dl

proc查看进程

当我们结束这个进程时,文件也会从proc中被删除


误删可执行程序时

在看完这个视频后,我们发现当我们在程序运行时,误删了可执行程序,进程不会被终止,但是在proc目录中的exe被标红并注明delete

在自行创建的进程中,我们只需要掌握好两个文件cwdexe

  • cwd代表当前工作目录
  • exe指向可执行程序的位置

默认情况下,进程启动所处的路径,就是当前路径,pwd指令其实就是从cwd中找到当前路径的!

当前工作目录是可以通过系统调用进行修改的:
指令:chdir ( " 路径 " )

我们只需要在代码编写时,加入这条指令我们就能更改当前工作目录


2. 创建子进程

2.1 系统调用函数fork

在Linux中,进程的创建方式有两种:

  • 命令行中直接启动进程
  • 通过代码创建

而在用代码创建进程时,实则是进行了系统调用,这里我们就得在学习一个系统调用函数fork!

函数:fork

让我们来简单用man指令了解fork函数信息
在这里插入图片描述
fork的功能是创建一个子进程
让我们来简单实现以下fork
在这里插入图片描述

在这里插入图片描述
我们发现在fork之后函数printf调用了两次!!!

我们再来看看进程的ppid
在这里插入图片描述
在这里插入图片描述

  • 说明了一个情况:fork之后,会创建子进程,并且子进程会和父进程一起进入后面的函数并且分别执行一次

2.2 fork的一般写法

结合目前: 只有父进程执行fork之前的代码(一定),fork之后,父子进程都要执行后续的代码!

  • 因此我们推断fork函数不仅会帮我们创建子进程而且它还有两个返回值,fork成功的时候,会有两个不同的返回值,给子进程返回0,给父进程返回子进程的pid。

首先我们来思考以下问题:

那么我们为什么要创建子进程?子进程的作用是啥?

  • 我们想让子进程协作父进程完成一些工作,这些工作是单进程解决不了的,因此子进程的创建是为了协助父进程,因此父子进程做的是不一样的事情

我们怎么保证父子进程做的是不一样的事情呢?

  • 我们可以通过判断fork的返回值,判断谁是父,谁是子,然后让他们执行不同的代码片段

让我们来看一下fork的一般写法

  1 #include<stdio.h>2 #include<sys/types.h>3 #include<unistd.h>4 5 int main()6 {7     printf("i am a process, 我的pid: %d\n", getpid());8 9 10     pid_t id = fork();11                                                                               12     if(id < 0) return 1;13 14     else if(id == 0)15     {16         // child17         while(1)18         {19             printf("我是子进程: pid: %d, ppid: %d, ret: %d, 我正在执行下载任务    \n",getpid(),getppid(),id);20             sleep(1);21         }22     }23 24     else25     {26         // parent27         while(1)28         {29             printf("我是父进程: pid: %d, ppid: %d, ret: %d, 我正在执行播放任务    \n",getpid(),getppid(),id);30             sleep(1);31         }32     }33 }

在这里插入图片描述
我们可以看到明明我们的fork只使用了一个变量接收但是出现了两个返回值

2.3 fork的原理

关于fork这个函数的原理,我们依然抛出几个问题

  1. fork干了什么事情?
  2. 为什么fork会有两个返回值?
  3. 为什么fork的两个返回值,会给父进程返回子进程pid,给子进程返回0?
  4. fork之后父子进程谁先运行?
  5. 如何理解同一个变量会有不同的值?

fork干了什么事情?

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

  • 以父进程为模板,为子进程创建PCB
  • 但是你今天创建的子进程,是没有代码和数据的!!!目前和父进程共享代码和数据!!
  • 所以,fork之后,父子进程会执行一样的代码

在这里插入图片描述


为什么fork的两个返回值,会给父进程返回子进程pid,给子进程返回0?

在用进程中,一个父进程可能会有多个子进程,但是子进程永远都只有一个父进程,所以父 :子 只会是 1 :n,为了能够更好的管理这些子进程,就必须返回具有唯一性的pid。但是子进程会很容易找到父进程,所以返回0表示成功即可!!


fork之后父子进程到底谁先运行?

创建完成子进程,只是一个开始,创建完成子进程之后,系统的其他进程,父进程和子进程,接下来要被调度执行的,当父子进程的PCB都被创建并在运行队列中排队的时候,哪一个进程的PCB先被选择调度,那个进程就先运行!!

  • 但是PCB的选择调度是由操作系统自主决定(由各自PCB中的调度信息(时间片,优先级等)+调度器算法共同决定)
  • 所以我们不确定父子进程到底谁先运行

最后为什么fork会有两个返回值?

如果一个函数到达了return,那么他的核心工作是否完成?

  • 答案很显然是的,所以。。。

在这里插入图片描述
最后在fork之后代码共享,所以return也会被共享进入父子进程,并在父子进程中分别执行,所以在fork函数return之前,父子进程就已经分流,因此就会产生两个返回值!


如何理解同一个变量会有不同的值?

同一个函数有两个返回值是因为fork后两个进程都被调度了,但是同一个变量会有不同的值?该如何理解?

首先我们思考一下,如果我们杀掉子进程,父进程还会存在嘛?杀掉父进程呢?

分析父子进程是否独立

由此,我们可以得出结论:进程之间运行的时候,是具有独立性的,杀掉父进程不会影响子进程!反之也是!

  • 进程的独立性,首先是表现在有各自的PCB进行之间不会互相影响,代码本身是只读的,不会影响,数据父子是会修改的!代码共享,数据各个进程都会写时拷贝私有一份!
  • 变量id是父进程定义的变量,保存数据,返回的时候发生写时拷贝,不同
    的进程执行的代码中的变量id获取的值不同,所以id在父进程和子进程中值不同

3. 总结

fork函数的内容远不只有这么一点,但是理解这五个问题能快速帮助我们,简单理解这个函数,了解fork的原理!关于如何创建子进程我们就讲到这里!

谢谢大家支持本篇到这里就结束了
在这里插入图片描述

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

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

相关文章

一文了解双向链表

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、链表分类二、双向链表是什么&#xff1f;三、功能函数实现1.申请一个节点2.初始化3.尾插4.头插5.尾删6.头删7.在指定位置后插入8.删除指定位置数据9.查找10…

带环链表问题

带环链表就是字面意思带环的链表&#xff0c;例如以下这三种情况 练习题 1.给定一个链表&#xff0c;判断链表中是否带环. - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;快慢指针&#xff0c;慢指针走一步&#xff0c;快指针走两步&#xff0c;两个指针从链表的起…

【项目经验】Redis Sentinel从工程中下线并对业务迁移-(二)

在上篇文章【项目经验】Redis Sentinel从工程中下线并对业务迁移-进行中-CSDN博客有说到迁移的计划。最近一直按照计划进行迁移&#xff0c;期间遇到了不少问题。总结如下&#xff1a; 一、key未设置过期时间 redis基于内存存储&#xff0c;主要作用是缓存。当大量的key未设置…

nginx的前世今生(二)

书接上回&#xff1a; 上回书说到&#xff0c;nginx的前世今生&#xff0c;这回我们继续说 3.缓冲秘籍&#xff0c;洪流控水 Nginx的缓冲区是其处理数据传输和提高性能的关键设计之一&#xff0c;主要用于暂存和管理进出的数据流&#xff0c;以应对不同组件间速度不匹配的问题…

池化整合多元数据库,zData X 一体机助力证券公司IT基础架构革新

引言 近期&#xff0c;云和恩墨 zData X 多元数据库一体机&#xff08;以下简称 zData X&#xff09;在某证券公司的OA、短信和CRM业务系统中成功上线&#xff0c;标志着其IT基础架构完成从集中式存储向池化高性能分布式存储的转变。zData X 成功整合了该证券公司使用的达梦、O…

Windows php 安装 Memcached扩展、php缺失 Memcached扩展、Class ‘Memcached‘ not found

在Windows系统下如何安装 php Memcached 扩展 下载dll文件 pecl地址&#xff1a;https://pecl.php.net/package/memcached 根据版本进行选择 &#xff1a; 解压下载的文件后得到了这么样的文件结构&#xff1a; 配置 移动dll文件到相应文件位置 重点&#xff1a; libme…

FreeRTOS队列集(1-15)

队列集定义&#xff1a;def 队列集只允许任务间传递消息为同一种数据类型&#xff0c;如果需要在任务间传递不同数据类型的消息时&#xff0c;就可以使用队列集。 用于对多个信号量进行监听&#xff0c;其中不管哪一个消息到来&#xff0c;都可以让任务退出阻塞状态 假设&am…

如何利用MCU自动测量单元提高大坝安全监测效率

大坝作为重要的水利基础设施&#xff0c;其安全性直接关系到人民群众的生命财产安全和社会的稳定发展。因此&#xff0c;对大坝进行实时、准确的安全监测至关重要。近年来&#xff0c;随着微控制器单元(MCU)技术的不断发展&#xff0c;其在大坝安全监测领域的应用也越来越广泛。…

springboot配置WebMvcConfigurationSupport

一、在spring里有四个mvc配置类 1、mvc配置类 WebMvcConfigurer WebMvcConfigurerAdapter WebMvcConfigurationSupport WebMvcAutoConfiguration 2、WebMvcConfigurer为接口 3、WebMvcConfigurerAdapter是WebMvcConfigurer的实现类&#xff0c;且大部分为空方法&#xff0c;…

微服务架构面试题(二)

1. 请简述微服务中各组件的作用 &#xff1f; 微服务架构中的组件各自扮演着关键的角色&#xff0c;它们协同工作以构建高效、可扩展和灵活的系统。以下是一些核心组件及其作用&#xff1a; 服务注册与发现组件&#xff1a;如Eureka&#xff0c;负责让其他服务知道当前服务的…

使用 Flask、Gunicorn 与 Shell 脚本构建高效 Web 应用部署流程

在使用 Flask 作为 Web 应用框架&#xff0c;并使用 Gunicorn 作为 WSGI 容器&#xff0c;使用shell 脚本来管理应用的启动、重启和停止。 启动脚本 start.sh&#xff1a; #!/bin/bash# 设置应用名称和端口 APP_NAME"my_flask_app" PORT8000# 设置 Flask 应用的路径…

【PCL】教程 supervoxel_clustering执行超体聚类并可视化点云数据及其聚类结果

[done, 417.125 ms : 307200 points] Available dimensions: x y z rgba 源点云milk_cartoon_all_small_clorox.pcd > Loading point cloud... > Extracting supervoxels! Found 423 supervoxels > Getting supervoxel adjacency 这段代码主要是使用PCL&#xff08;Po…

【Linux】创建/扩容swap交换空间swap优化

一、当前交换空间大小 目前交换空间大小为2G 二、创建swap交换空间 #创建大小为2G的交换空间 [roothadoop01 data1]# dd if/dev/zero of/data1/swapfile bs1M count2048 #将文件设置为交换空间 [roothadoop01 data1]# mkswap /data1/swapfile #启用交换空间 [roothadoop01 da…

【Python】pandas.cut()函数的用法

pandas.cut() 函数是一个非常有用的工具&#xff0c;用于将数值型数据按照指定的分箱或区间进行分割&#xff0c;从而将连续的数值变量转换为离散的类别变量。这在数据分析和机器学习的特征工程中尤其有用&#xff0c;因为它可以帮助揭示不同区间内的数据分布特征&#xff0c;或…

【Qt问题】Qt Creator 如何链接第三方库

往期回顾 【QT问题】 Qt信号函数如果重名&#xff0c;调用怎么处理-CSDN博客 【Qt问题】Qt中文乱码问题解决方案&#xff08;详细汇总&#xff09;-CSDN博客 【Qt问题】Qt常用快捷键汇总-CSDN博客 【Qt问题】Qt Creator 如何链接第三方库 一、为什么会使用第三方库 在编写代码…

Java Web 开发 - 掌握拦截器和监听器

目录 深入了解Java Web的拦截器和监听器 拦截器&#xff08;Interceptor&#xff09; 拦截器的使用场景 拦截器实例 思维导图 ​编辑 监听器&#xff08;Listener&#xff09; 监听器的使用场景 监听器类型 监听器实例 思维导图​编辑 总结 深入了解Java Web的拦截器…

在UI界面中播放视频_unity基础开发教程

在UI界面中播放视频_unity基础开发教程 前言操作步骤结语 前言 之前我写过一篇在场景中播放视频的文章&#xff0c;但是在开发中有时候也会在UI的界面中播放视频&#xff0c;这期我们做一下在UI的界面中播放视频。 操作步骤 首先在场景中创建一个Raw Image&#xff0c;UI->…

DAO是什么?有什么用途?

DAO&#xff08;Decentralized Autonomous Organization&#xff0c;去中心化自治组织&#xff09;是一种基于区块链技术的组织形式&#xff0c;它没有中央管理层&#xff0c;而是通过智能合约和区块链上的代码来运作。DAO 的决策过程是透明的&#xff0c;通常由组织的成员通过…

java学习笔记11

20. 字符串类 字符串是指一连串的字符,它是由许多单个字符连接而成。字符串可以包含任意字符,这些字符必须包含在一对双引号""之内,例如:“abc”.java中封装了3个字符串类,分别是String类、StringBuffer类、StringBuilder类,都在java.lang包中。20.1 String类的…

孤独的城市公路(增加开始结束页面)

致敬 裴总 缺点 没有一个车没有背景音乐后期都会添加 为什么裴总良心 裴总真良心 还有音乐 还是3D 还有开始按钮 还能game 还有结尾 还有结尾提示 import arcadeSCREEN_WIDTH 700 SCREEN_HEIGHT 700 SCREEN_TITLE "孤独的城市公路" MOVEMENT_SPEED 5 …