拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Linux系统篇,进程概念(二):进程本质的研究、进程PCB是什么、进程的查看、如何创建父子进程并清楚fork函数调用逻辑

系列文章目录第三章 进程概念文章目录系列文章目录前言一、进程的基本概念1.1 什么是进程1.2 进程与程序的区别1.3 进程的本质1.4 进程的加载二、PCB——进程控制块2.1 PCB的作用2.2 task_struct的主要内容2.3 进程的组织方式三、进程的查看3.1 ps命令3.2 top命令3.3 /proc文件系统四、进程的创建——fork函数4.1 fork函数的基本使用4.1.1 父子进程的变量存储4.1.2 函数存在多个返回值4.1.2 else 和 if else 为什么可以同时执行总结前言在上一篇文章中我们了解了计算机的硬件体系、操作系统的作用以及程序的加载过程。我们知道了操作系统是“搞管理”的软件管理的本质是“先描述再组织”。本节我们将在此基础上深入探讨操作系统的核心管理对象之一进程。进程是操作系统中最基本、最重要的概念之一。无论是我们日常使用的浏览器、编辑器还是服务器上运行的各种服务本质上都是一个或多个进程在背后默默工作。理解进程是理解操作系统如何管理计算机资源、如何实现多任务并发、如何保证系统稳定运行的关键一步。接下来我们将从进程的定义出发逐步揭开它的神秘面纱。一、进程的基本概念1.1 什么是进程我们平时在电脑上双击一个程序图标或者在终端输入一个命令这个程序就开始运行了。这个“运行中的程序”在操作系统中就被称为进程。举个例子当你双击打开一个文本编辑器时操作系统就会为这个编辑器创建一个进程。如果你同时打开两个文本编辑器窗口那么系统中就会存在两个独立的进程它们各自拥有自己的内存空间、运行状态和资源互不干扰。即使你关闭了其中一个窗口另一个窗口依然可以正常工作这正是因为它们是两个相互独立的进程。从操作系统的角度来看进程是程序的一次执行过程是系统进行资源分配和调度的基本单位。换句话说程序是存放在磁盘上的静态文件而进程是程序在内存中动态执行的过程。这里需要特别强调的是“动态”二字。程序本身只是一堆指令和数据的集合它静静地躺在硬盘上不占用任何 CPU 时间也不消耗内存资源。只有当用户启动它、操作系统把它加载到内存并开始执行时它才“活”起来成为一个进程。进程从创建到消亡经历了一系列状态变化如就绪、运行、阻塞等这些状态转换正是操作系统调度和管理的核心内容。1.2 进程与程序的区别对比项程序进程存在形式静态文件存储在磁盘动态执行运行在内存生命周期长期存在有创建、运行、消亡的过程资源占用不占用CPU和内存占用CPU、内存等系统资源数量关系一个程序可以对应多个进程每个进程对应一个程序实例从上表可以看出程序与进程有着本质的区别。程序是“死”的它只是存储在磁盘上的一堆指令和数据而进程是“活”的它是程序在内存中动态执行的过程拥有自己的生命周期和资源占用。一个典型的例子是同一个浏览器程序你可以同时打开多个窗口每个窗口都是一个独立的进程。它们共享同一个程序代码即磁盘上的浏览器可执行文件但各自拥有独立的内存空间和运行状态。这就是“一个程序可以对应多个进程”的直观体现。1.3 进程的本质进程即程序的调用是怎么样的呢为了保证进程的有序执行os必须对这些进程做管理那具体形式是什么结构体完美的承担了这个角色。在操作系统中每个进程都需要被“描述”和“记录”。操作系统需要知道这个进程叫什么名字它占用了多少内存它当前处于什么状态它运行到哪一条指令了这些信息如果散落在各处操作系统将无法统一管理。因此操作系统为每个进程都维护了一个专门的数据结构用来集中存放该进程的所有信息这个数据结构就是进程控制块PCB。在上一篇文章中我们提到操作系统管理的本质是“先描述再组织”。那么对于进程而言描述操作系统用一个数据结构来记录进程的所有信息这个结构就是PCBProcess Control Block进程控制块。组织操作系统将所有的PCB通过一定的数据结构组织起来以便高效地查找、调度和管理。因此我们可以说进程 程序代码 数据 PCB。1.4 进程的加载进程的本质是进程 程序代码 数据 PCB。程序平时存放在硬盘中运行时需要加载到内存。整个加载过程分为四步第一步在硬盘中找到可执行文件操作系统根据文件名或路径找到硬盘上的可执行文件如 .exe。它包含程序代码、数据、文件头等信息此时还只是一个静态文件。第二步加载到内存操作系统读取文件解析格式为程序分配内存空间代码段、数据段、堆、栈等然后把程序代码和数据从硬盘复制到内存中。第三步创建 PCB进程控制块操作系统为该进程创建一个 PCB记录进程 ID、状态、程序计数器、寄存器、优先级等信息。创建 PCB 后进程被放入就绪队列等待 CPU 调度。第四步CPU 运行进程CPU 调度器从就绪队列中选择该进程加载 PCB 中的寄存器等信息从内存中取指令执行。进程开始运行执行过程中不断切换保存/恢复 PCB。二、PCB——进程控制块2.1 PCB的作用PCB是操作系统感知进程存在的唯一标志。在Linux系统中PCB的具体实现是 task_struct 结构体。这个结构体中包含了操作系统管理进程所需的全部信息。2.2 task_struct的主要内容PCB作为结构体它的属性是管理着这个进程的运行我们需要认识的有标识符每个进程都有唯一的PID进程ID用于区分不同的进程。此外还有PPID父进程ID等。状态进程当前处于什么状态运行、就绪、阻塞等。优先级进程被调度执行的优先级别。程序计数器记录下一条要执行的指令地址。内存指针指向进程的代码、数据、堆栈等在内存中的位置。上下文数据进程切换时需要保存的CPU寄存器状态。I/O状态信息进程打开的文件列表、使用的I/O设备等。记账信息进程使用的CPU时间、内存大小等统计信息。2.3 进程的组织方式在Linux内核中所有进程的task_struct通过多种方式组织链表所有进程通过一个双向链表连接在一起方便遍历所有进程。树形结构通过父子关系组织形成一个进程树方便管理进程的创建和终止关系。哈希表通过PID快速定位到对应的进程。这种“描述组织”的方式正是操作系统管理进程的核心机制。操作系统通过对PCB的增删查改就实现了对进程的管理。三、进程的查看3.1 ps命令ps命令用于查看当前系统中的进程状态。常用的选项有bashpsaxj# 查看系统中所有进程的详细信息ps-ef# 以完整格式显示所有进程ps-l# 显示当前用户的进程当然这样直接的使用不方便我们的查看可以结合之前学过的“管道” 和 “grep过滤”知识基础过滤使用grep 文件名筛选特定进程。去除干扰加上grep -v grep过滤掉过滤指令本身的进程。保留标题栏使用head -1显示 ps 输出的首行属性说明。实时监控脚本使用 while 循环构建一个简单的自动化脚本while:;dopsaxj|head-1psaxj|grepmyprocess|grep-vgrep;sleep1;done3.2 top命令top命令可以动态地查看系统中进程的运行状态包括CPU占用率、内存占用率等信息类似于Windows的任务管理器。top3.3 /proc文件系统Linux提供了一个特殊的文件系统/proc它不是一个真实的磁盘文件系统而是内核数据结构的映射。每个进程在/proc下都有一个以PID命名的目录里面包含了该进程的各种信息。# 查看PID为1的进程信息ls/proc/1/用这样的指令查看进程的详细信息会发现两个特殊的文件格式cwd -/home/sxr/code/Socket/UDP exe -/home/sxr/code/Socket/UDP/udpservercwd这表示这个进程运行时的工作目录是/home/sxr/code/Socket/UDP这个信息记录在进程的 PCB 中是操作系统管理进程的重要上下文之一。exe这表示这个进程是从 /home/sxr/code/Socket/UDP/udpserver 这个文件加载到内存后运行起来的。在进程加载过程中操作系统会在硬盘上找到这个可执行文件读取它的代码和数据并创建 PCB。exe 这个符号链接就指向了那个原始的可执行文件四、进程的创建——fork函数4.1 fork函数的基本使用在Linux中创建新进程的主要方式是调用fork()系统调用。fork()函数会创建一个新的进程称为子进程原来的进程称为父进程。头文件#include unistd.hfork的作用就像是 “克隆”复制出与该进程一模一样的子进程有着这样的特点和你长得一样代码一样手里拿的东西一样数据一样站在同样的位置当前工作目录一样但你们是两个独立的个体之后可以各干各的活。给出代码进行讲解#include stdio.h#include unistd.hintmain(){pid_t pidfork();if(pid0){// 子进程执行 printf(我是子进程PID %d子进程PID %d\n, getpid(),getppid());}elseif(pid0){// 父进程执行 printf(我是父进程PID %d子进程PID %d\n, getpid(), pid);}else{// fork失败 perror(fork);}return0;}我们可知进程 PCB 代码数据作为子进程那么它必然具有属于自己的pcb和代码数据而该进程是由父进程修改后自己的两者后拷贝赋予的。最终子进程也就会执行父进程之后的代码相当于有两个进程同时执行代码。对于这样的代码无异于是超出了我们C/C 使用的常规语法的我们会有着这样的疑问pid 为什么存在不同的值函数为什么会存在两个返回值if语句中执行两者不同的执行逻辑4.1.1 父子进程的变量存储我们可以清楚的看到pid这个变量同时表达了两个值大于0和等于0。这个现象的产生需要我们了解fork这个调用的底层逻辑写实拷贝创建与共享 父进程调用fork函数后os不复制物理内存它将给子进程创建一个新的PCB并将父进程和子进程的页表同时指向父进程的内存空间页表即虚拟地址与物理内存的映射表操作在进行只读操作期间共享状态并不会改变而一旦发生修改操作就会立刻发生保护异常拷贝当父子进程某一方要执行修改操作时os就会给一方创建一个新的物理内存并将原有的代码数据等拷贝同时修改页表的映射关系实现两者代码修改的独立。因此父进程的pid和子进程的pid虽然名字相同但本质是两块不同的物理内存资源因此有不同的值。4.1.2 函数存在多个返回值要明白这个问题我们首先要清楚一件事函数要实现的目的是return语句执行前就已经完成了。而通过上面写实拷贝过程中的事情我们可知在fork函数返回前就已经创建好子进程了。那就意味着如果要赋值这类修改操作发生就会有不同的物理内存存储这个值因此存在多个返回值。4.1.2 else 和 if else 为什么可以同时执行我们要明确一个概念代码是共享的但执行流是独立的。fork形成的父子进程具有同样的执行代码只是两者存储的修改后值不同因此同时执行是我们肉眼看到的本质是有两个执行相同代码的进程同时进入到if判断中并作出相应的回应。本节的进程有关的知识就到这里了下一节我们将学习关于进程的更多执行过程中的状态变化。总结
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门