Linux下进程及其进程地址空间以及一些进程的控制函数

news2024/12/26 23:46:56

目录

    • 什么是进程?
    • 进程的状态
        • Linux下进程的状态
    • 进程地址空间
      • 什么是进程地址空间
        • 为什么需要进程地址空间?
    • 进程控制
      • 进程控制函数
        • fork
        • wait/waitpid 进程等待
        • 进程替换,进程替换函数exe

今天我们来分享一下Linux下的进程和进程地址空间以及一些进程的控制函数。

什么是进程?

进程是操作系统分配资源的基本单位,一个进程是一个程序的一次执行过程。每启动一个进程,操作系统就会为它分配一块独立的内存空间,用于存储PCB、数据段、程序段等资源。每个进程占有一块独立的内存空间。简单来说,它就是可执行程序 + 该进程对应的内核的各种数据结构。

问:操作系统是怎么管理进程的呢?

再回答这个问题之前,我想问问学校是怎么管理学生的呢?,总不能一个一个盯着吧;高效的做法就是将学生的信息用一个类似结构体的东西给记录起来,然后用一个类似链表的结构给串起来,那么学校管理学生,其实就是对这个数据结构的增删查改的。
那么操作系统肯定也是类似的,将进程信息放进一个叫做进程控制块的数据结构中,其实就是个结构体,称为PCB,Linux操作系统下,这个数据结构名字叫做task_struct;它会被装载到RAM(内存)里并且包含着进程的信息。然后把一个一个的task_struct用链表组织起来进行管理。

进程的状态

什么叫做进程的状态呢?举个例子吧,学校的学生有在读,退学,停学,毕业等等,这些状态;那么为什么要标识这些状态呢?当然就是为了好管理啊;所以进程的状态也类似,也就是为了方便操作系统根据进程的状态进行管理。
但是不同的平台下,进程的状态也有所不同,但是无非也就是运行态,终止态,阻塞态,挂起态啊这些;在讲Linux操作系统下进程的状态之前,我们先来认识上面举例的这几个吧。

  • 运行态:进程的PCB在运行队列就叫做运行态(代表进程条件已经就绪,随时可以被调度)
  • 终止态:进程还在,只是不会再次运行了,随时等待被释放
  • 阻塞态:当进程在等待外部资源时,该进程条件不就绪,代码不会执行,进入对应设备的等待队列,此时进入阻塞状态
  • 挂起态:短期内不会被调度(该进程所等待的资源,短期内不会就绪)的进程;如果让它的代码和数据依旧在内存中,那就是浪费内存空间,OS就会把该进程的代码和数据置换到磁盘上(置换到磁盘的swap分区)。这个过程就叫做进程挂起

对于上面的解释,可能大家还不是很理解,那就跟着我解决几个问题,就能理解了
为什么需要终止态,既然这个进程都不会在运行了,直接退出,回收资源不行吗?

答:你进程执行的结果,执行是否成功,退出的信息,是不是要告诉OS或者是父进程呢?而且如果但是操作系统很忙,没空去释放你这个进程呢?所以就要先进入终止态,等待操作系统空闲了,再去读取进程的退出信息,释放进程。
怎么理解外部资源不就绪,进入阻塞状态呢?
一个进程在使用资源的时候,可不只是在申请CPU资源,可能还会申请其他资源,比如说磁盘啊,网卡啊,显示器啊等等;那么如果有多个进程其他资源已经就绪,但是要申请CPU资源,暂时无法满足,那就需要排队——运行队列。所以如果当进程访问某些资源时,如果该资源没有准备好或者正在给其他进程提供服务,此时当前进程就要从运行队列中移除;将当前进程的task_struct放入对应设备的描述结构体中的等待队列
在这里插入图片描述
在这里插入图片描述

Linux下进程的状态

我们直接来看看,Linux描述进程状态的源码

static const char *task_state_array[]={
	"R (running)",
	"S (sleeping)",
	"D (disk sleep)",
	"T (stopped)",
	"T (tracing stop)",
	"Z (zombie)",
	"X (dead)"
};
  • R(running):运行状态
  • S(sleeping):阻塞状态,(这是浅度的睡眠,也叫做可中断睡眠,意思就是这个进程在等待资源,但是操作系统可以随时唤醒它,你也可以终止它)
  • D(disk sleep):也是一种阻塞状态(一般来说,在Linux中,如果我们等待的是磁盘资源,我们进程阻塞所处的状态就是D);这个叫深度睡眠,也叫做不可被中断睡眠,这种状态下,除非关机重启,不然这个进程不会被杀死。
  • Z(zombie):僵尸状态,在Linux系统下,进程退出时,一般不会直接进行X状态(死亡,资源可以立马回收),而是进入Z状态
  • X(dead):死亡状态,表示进程可以被释放,随时等待被释放,这个状态只是一个返回状态,你不会在任务列表里看到这个状态
  • T(stopped):停止状态,暂停;可以通过发送信号来暂停进程,暂停后进程就进入这个状态,也可以发送信号让这个进程继续执行
  • T(tracing stop):这个也是一种暂停状态,gdb在调试程序时,gdb进程处于S状态等待输入,但是我们发现我们自己的进程所处的状态就是T(tracing stop)状态。
    为什么呢要先进入Z状态呢?

首先明确进程为什么被创建出来,一定是因为要有任务让这个进程执行,当该进程退出时,我们怎么知道,这个进程把任务给完成了呢?所以一般需要将进程的执行结果告知给父进程和OS,所以进程进入Z状态,就是为了维护退出信息,可以让父进程或者os读取的.

长时间的僵尸状态会有什么问题呢?

如果没有人回收子进程的僵尸,该状态会一直维护,该进程的相关资源(task_struct)不会被释放,内存泄
露,一般必须要求父进程进行回收

这里在补充一个概念“孤儿进程”

什么叫做孤儿进程呢?

如果父进程提前退出,子进程还在运行,就叫孤儿进程,子进程就会被操作系统领养,有操作系统负责回收。

进程地址空间

进程地址空间,我们需要搞懂两个问题即可。第一个什么是进程地址空间?第二个就是为什么需要进程地址空间?

什么是进程地址空间

所谓进程地址空间,是操作系统上的概念,物理上并不真实存在。

每一个进程在启动时,都会让操作系统给他创建一个地址空间,该地址空间就是进程地址空间。

其实进程地址空间就是内核的一个数据结构叫做,struct mm_struct。通过这个数据结构,维护进程需要的资源的区域,比如堆,栈等,这些区域记录的都是虚拟地址,然后可以通过页表将虚拟地址映射到物理内存地址

有了进程地址空间可以很好的保证进程的独立性(多进程运行,需要独享各种资源,多进程运行期间互不干扰);
这样干讲可能不太懂,我们来看图
在这里插入图片描述
那我们来讲讲进程地址空间也就是mm_struct里面的内容大概是什么吧,其实就是mm_struct中维护了一个链表,然后这个链表的结点都是每段空间的划分,比如 (栈区 start 和 end)-> (堆区 start 和 end) -> (正文代码 start 和 end),如果要执行这个进程的某段代码,就去 mm_struct 中维护的链表中找 (正文代码)对应的区域,然后通过页表可以得到相应的内存地址,将程序的代码和数据读取到 CPU 中进行执行而程序内部的地址通过加载的时候早已转化成了虚拟地址。

为什么需要进程地址空间?

  1. 保护内存

有了虚拟地址空间,相当于给访问内存添加了一层保护层,可以对转换过程进行审核,对于非法的访问,就直接拦截了

  1. 通过地址空间,进行功能模块的解耦

(1)如果没有虚拟内存空间,那么如果一个进程正在malloc来申请内存空间,就会去调用malloc的底层代码,进行内存管理要进行的工作
(2)而现在,如果malloc来申请空间,那么操作系统会将进程中虚拟空间中对应的堆区扩大相应的大小,允许用户访问这段空间,但是不会马上进行内存的分配。而是等到用户真正要使用这块内存空间时,才会触发内存管理,申请物理空间,建立映射关系,就可以进行两者的解耦。
(3)如果没有进程地址空间,进程直接访问物理内存,当进程退出时,内存管理需要尽快将该进程回收,在这个过程中必须保证内存管理得知道某个进程退出了,并且内存管理也得知道某个进程开始了,这样才能给他们及时的分配和回收资源,这就意味着内存管理和进程管理模块是强耦合的。
(4)如果有了进程地址空间,当一个进程需要资源的时候,通过页表映射去要就可以了,内存管理就只需要知道哪些内存区域(配置)是无效的,哪些是有效的(被页表映射的就是有效的,没有被页表映射的就是无效的),当一个进程退出时,它的映射关系也就没有了,此时没有了映射关系,物理内存这里就将该进程的数据设置为无效.

就相当于,你存了1万块钱,你银行卡就有余额,银行管理你的1万块钱,你不用关心你1万块钱被拿去干嘛,你只需要在需要时,取就行了

  1. 让进程或者程序可以以统一的视角看待内存(方便一统一的方式来编译和加载所有的可执行程序,也就是说,无论是怎样的代码,都认为我们需要什么区域,都到各种的区域去,比如代码区,永远都在虚拟地址空间的那个区,而如果直接加载到内存,那么今天这个可执行程序的代码区可能加载到内存的这个位置,明天又可能是这个位置)
  2. .内存共享:虚拟地址空间允许多个进程访问同一块物理内存,这种机制可以提高进程间通信的效率和灵活性
  3. 进程间独立:每个进程拥有自己独立的地址空间,使得进程之间相互独立,避免了进程之间的影响和冲突。

进程控制

进程控制函数

fork

它有两个返回值,创建子进程成功返回给子进程0,失败返回-1;创建成功返回子进程Id给父进程,失败返回-1。
问:fork之后,操作系统会做什么?

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程的task_struct到系统进程列表当中
  • fork返回,开始调度器调度
  • 数据以写时拷贝的方式,来进行进程共享或独立
    那什么是写时拷贝呢?
    通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本,重新建立了映射关系
    在这里插入图片描述
    在这里插入图片描述
    这里我们就能解释为什么fork有两个返回值,pid_t id,同一个变量,怎么会有两个不同的值呢?
    当id=fork()的时候,谁先返回,谁就要发生写时拷贝,所以,同一个变量,会有不同的内容值,本质就是因为虚拟地址是一样的,但是映射到物理地址的时候,就不一样了。

wait/waitpid 进程等待

为什么要进程等待?

  • 子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
  • 父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,执行结果怎么样啊, 是否正常退出啊。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

wait方法:pid_t wait(int* status):

返回值:成功返回被等待进程pid,失败返回-1。
参数:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

wait()的方案可以解决回收子进程z状态,让子进程进入x

wait作用:等待任意一个退出的子进程

waitpid方法:pid_ t waitpid(pid_t pid, int *status, int options);

返回值:当正常返回的时候waitpid返回收集到的子进程的进程ID;如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:

  • pid:Pid=-1,等待任一个子进程。与wait等效,Pid>0.等待其进程ID与pid相等的子进程。
  • status:WIFEXITED(status)(等价于(status)&0x7F): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出);WEXITSTATUS(status)(等价于(status>>8)&0xFF): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
  • options:WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程id;0表示阻塞等待

进程替换,进程替换函数exe

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

#include <unistd.h>`
 
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

为什么需要进程替换?

我们想让创建出来的子进程,执行全新的程序,这个时候就需要程序的程序替换

进程替换的原理

  1. 将磁盘中的程序,加载入内存结构
  1. 重新建立页表映射,那个进程执行程序替换,就重新建立映射(子进程)
    效果:让我们的父进程和子进程彻底分离,并让子进程执行一个全新的程序

需要注意的是,进程替换之后,原来共享的父进程代码就不会再执行了
** 子进程执行程序替换,会不会影响父进程呢,如果不影响,是怎么做到的呢?**

不会(进程具有独立性),数据层面发生写时拷贝,当程序替换的时候,我们可以理解成代码和数据都发生了写时拷贝完成父子分离。

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

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

相关文章

进出口跨境电商软件平台系统开发,源码技术架构

一、进出口跨境电商软件平台系统开发需做好相应的前期准备&#xff0c;如确定市场、了解政策、推广宣传等。 欢迎名片沟通探讨 确定目标市场&#xff1a;选择合适的目标市场。需要了解目标市场的消费习惯、政策法规以及竞争情况。 了解海关相关政策&#xff1a;针对不同国家或…

python之函数(参数,匿名函数,局部变量和全局变量)

文章目录 前言一、函数的参数 1、形参和实参2、必传参数&#xff08;也叫&#xff1a;必须参数&#xff09;3、关键字传参4.、默认参数5、不定长参数6、传参的顺序二、匿名函数&#xff08;lambda函数&#xff09; 1. 定义及特点语法格式2. lambda函数的特点三、函数返回值retu…

微信小程序商城开发

随着移动互联网的发展&#xff0c;小程序商城逐渐成为了电商领域的新宠。小程序商城具有便捷、快速、安全等优点&#xff0c;为用户提供了更加优质的购物体验。下面我们来介绍小程序商城的功能和优点。 一、商品展示 小程序商城提供了丰富的商品展示&#xff0c;包括商品分类…

llama_index中query_engine的response_mode详解

文章目录 0. 前言1. ResponseMode: tree_summarize &#xff08;总结摘要-最优&#xff09;2. ResponseMode: generation3. ResponseMode: no_text4. ResponseMode: simple_summarize &#xff08;最省token&#xff09;5. ResponseMode: refine &#xff08;基于关键词询问-最…

ROS:坐标管理系统

目录 一、机器人中的坐标变换二、TF功能包2.2TF功能包简介2.2TF坐标变换实现2.3TF案例 三、小海龟跟随实验3.1打开小程序3.2查看当前的TF树3.3坐标相对位置关系可视化1&#xff08;tf_echo&#xff09;3.4坐标相对位置关系可视化2&#xff08;rviz&#xff09; 一、机器人中的坐…

二、电压源、电流源、受控源

点我回到目录 目录 理想电压源 理想电流源 受控源 电流源做功问题 电压源做功问题 理想电压源 •定义&#xff1a;两端电压保持定值或一定的时间函数&#xff0c;且电压值与流过它的电流i无关 •特点&#xff1a;理想电压源两端的电压由本身决定&#xff0c;与外电路无关…

ChatGPT2论文解读《Language Models are Unsupervised Multitask Learners》(2019)

论文总结 以下是我阅读完整篇论文做的个人总结&#xff0c;包含了ChatGPT-2文章的主要内容&#xff0c;可以仅看【论文总结】章节。 数据集 自制了一个网页爬虫&#xff0c;被抓取的网页部分来自于社交平台&#xff0c;这些网页由人工进行过滤。最终生成WebText数据集&#…

多种工厂模式的运用

文章目录 多种工厂模式的运用一、简单工厂模式&#xff08;非23种设计模式&#xff09;1.1 结构2.2 实现2.2.1 简单工厂类图2.2.2 代码2.2.3 优缺点 二、静态工厂模式&#xff08;非23种设计模式&#xff09;3.1 代码 三、工厂模式3.1 结构 3.2 实现3.2.1 工厂模式类图3.2.2 代…

Rust教程初识

Rust 教程 Rust 语言是一种高效、可靠的通用高级语言。其高效不仅限于开发效率&#xff0c;它的执行效率也是令人称赞的&#xff0c;是一种少有的兼顾开发效率和执行效率的语言。 Rust 语言由 Mozilla 开发&#xff0c;最早发布于 2014 年 9 月。Rust 的编译器是在 MIT Licens…

bmp图片怎么转jpg格式?思路提供

BMP和JPG是两种常见的图片格式。BMP文件相对较大&#xff0c;无损压缩&#xff0c;而JPG文件则相对较小&#xff0c;有损压缩。当我们需要在保持图片质量的同时减小文件大小时&#xff0c;我们可以将BMP文件转换为JPG文件。在本文中&#xff0c;我们将介绍如何将BMP文件转换为J…

短视频矩阵源码技术开发

短视频矩阵是一种常见的视频编码标准&#xff0c;它通过将视频分成多个小块并对每个小块进行压缩来实现高效的视频传输。在本文中&#xff0c;我们将介绍短视频矩阵的原理和实现&#xff0c;并提供示例代码。 $where_time array(); // 时间 $where_time[] array(name>fbr…

第5章:SpringMVC的视图

一、SpringMVC的视图 SpringMVC中的视图是View接口&#xff0c;视图的作用渲染数据&#xff0c;将模型的Model中的数据展现给用户SpringMVC视图种类很多&#xff0c;默认有转发视图和重定向视图当工程引入jstl依赖&#xff0c;转发视图自动转换为JstlView若使用视图技术是Thym…

【新版】系统架构设计师 - 新老教材对比分析

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 新老教材比较新版教材章节分析 新老教材比较 提示&#xff1a;请自行购买并浏览新版系统架构设计师教材 原教材&#xff1a;2009年出版&#xff0c;共21章&#xff0c;572页。新教材&#xff1a;2022年出…

紧急防勒索病毒的防御方案

一、适用目标&#xff08;校园网、企业网&#xff0c;windows系列的操作系统&#xff09;&#xff1a; 所有在校园内运行windows系统的电脑&#xff0c;并非只感染服务器操作系统&#xff0c;单机照样感染。会将你电脑中的所有文件全部加密&#xff0c;部分已感染案例有2个共同…

Yolov8轻量级:Next-vit,用于现实工业场景的下一代视觉 Transformer

1.Next-vit介绍 论文:https://arxiv.org/pdf/2207.05501.pdf 由于复杂的注意力机制和模型设计,大多数现有的视觉 Transformer(ViT)在现实的工业部署场景中不能像卷积神经网络(CNN)那样高效地执行。这就带来了一个问题:视觉神经网络能否像 CNN 一样快速推断并像 ViT 一样…

DVPP媒体数据处理图片解码问题案例

DVPP&#xff08;Digital Vision Pre-Processing&#xff09;是昇腾AI处理器内置的图像处理单元&#xff0c;通过AscendCL媒体数据处理接口提供强大的媒体处理硬加速能力&#xff0c;主要功能包括图像编解码、视频编解码、图像抠图缩放等。 本期就分享几个关于DVPP图片解码问题…

Web的基本漏洞--越权漏洞

目录 一、越权漏洞介绍 1.越权漏洞的原理 2.越权漏洞的分类 3.越权漏洞产生的原因 一、越权漏洞介绍 越权漏洞指的是应用在检查授权时存在纰漏&#xff0c;可以让攻击者获得低权限用户账户后&#xff0c;利用一些方式绕过权限检查&#xff0c;可以访问或者操作其他用户或者…

如何选择振动传感器?实现设备健康监测和预测性维护的第一步

随着工业设备的日益复杂和关键性的增加&#xff0c;设备健康监测和预测性维护成为保障生产效率和设备可靠性的关键要素。而振动传感器作为常用的监测工具&#xff0c;在设备健康监测中扮演着重要角色。 图.设备监测&#xff08;iStock&#xff09; 本文将介绍如何选择振动传感…

聊聊公司技术上的奇葩规定——计算机使用

首先说明下公司的技术架构。 MySQL PHP Java&#xff0c;纯互联网应用&#xff0c;Docker 容器部署微服务在 AWS 上面。 数据上没有过多敏感性&#xff0c;不涉及到很多敏感数据&#xff0c;身份识别信息通常使用的是第三方平台&#xff0c;我们本地不存储用户身份信息。 电…

跟着LearnOpenGL学习6--变换

文章目录 一、前言二、向量2.1、向量与标量运算2.2、向量取反2.3、向量加减2.4、长度2.5、向量相乘 三、矩阵3.1、矩阵的加减3.2、矩阵的数乘3.3、矩阵相乘 四、矩阵与向量相乘4.1、单位矩阵4.2、缩放4.3、位移4.4、旋转4.5、矩阵的组合 五、变换实战5.1、GLM配置5.2、GLM测试5…