操作系统之线程和进程

简介: 操作系统之线程和进程

一. 什么是操作系统

操作系统本质上是一个软件, 发挥的是管理作用, 可以管理软件和硬件, 让其有条不紊的运行和使用.


对于操作系统下层, 操作系统需要管理好硬件; 对于操作系统上层要给软件提供稳定的运行环境, 所以操作系统是软件, 硬件, 用户之间交互的媒介.

73d8c9be8b2a4960a39693770de0ac9a.png

硬件设备: 实体设备, 比如电脑后盖打开看到的都是硬件设备.


驱动程序: 硬件厂商在开发硬件的同时会提供驱动程序, 电脑安装了对应的驱动程序, 才能让系统正确的识别硬件设备.


操作系统内核: 是操作系统的核心功能, 承上启下.


系统调用: 操作系统给应用程序提供的一些API接口, 比如有个程序想操作一下硬件设备, 就需要先通过系统调用, 把操作命令告诉给系统内核, 内核调用驱动程序, 进一步的操作硬件设备.


应用程序:QQ , 所写的Java代码等都是应用程序.


常见的操作系统:


Windows:windows98, 2000, xp, vista,win7, win10, win11(最熟悉的操作系统).


Linux: 特别适合进行开发和部署(程序员必须掌握的系统).


Mac: 苹果电脑用的系统, 和Linux有一些相似之处.


还有一些手机上运行系统;Android ,IOS等.

二. 进程和线程

1. 进程

简单来说进程就是正在运行的程序, 通过双击一个可执行文件(.exe), 操作系统就会把这个可执行文件加载到内存上, 并在CPU上执行可执行文件中的指令, 此时这个exe就跑起来了, 就成为一个正在运行的程序, 也称作一个进程(process), 也叫做一个任务.


现在的很多电脑都是多进程模式的, 宏观上, 可以有很多个进程同时执行.

73d8c9be8b2a4960a39693770de0ac9a.png

上面一部分进程是我们自己去执行应用的可执行文件, 而另一部分是操作系统自动启动的进程.


2. 线程

线程是进程内部的一部分, 将进程类比作一个工厂, 那么线程就是工厂中的生产线, 所以一个进程可以包括很多个线程, 一个进程至少拥有一个线程.


我们日常所写的java代码, 其实最终都是通过java进程(jvm)跑起来的.


3. 进程的管理

进程是一个重要的"软件资源", 是由操作系统内核负责管理的, 进程中含有一个或者多个线程, 在进程中开启第一个线程时, 需要进行资源的分配, 之后进程中开启的其他线程复用的都是同一一份资源.


线程是通过结构体(C语言)来描述的(操作系统基本上都是通过C/C++来实现的), 这个结构体也被称为进程控制块(PCB), 这若干个结构体就描述了进程, 对进程进行描述后, 然后就是组织线程, 线程的本质上也是数据, 那么对数据的组织需要通过数据结构来进行组织, 在Linux系统中是通过双链表来组织线程的, 也就是通过一个双向链表将多个PBC给串到一起.


创建一个线程, 本质上就是创建一个PCB这样的结构体对象, 把它插入到链表中.


销毁一个线程, 本质上就是把链表上的 PCB节点删除掉.


任务管理器查看到进程列表, 本质上就是遍历这个PCB链表.


所以对线程的增删查改本质上就是对双链表进行增删查改, 当然这个双链表是复杂的双链表, 但是对线程的操作的基本思路和操作双链表是一样的.]


3.1 PBC中的一些属性

PCB里包含的属性是非常多的, 下面介绍的是一些核心属性.


进程id(pid): 进程的一个身份标识符, 是一个唯一的数字.

73d8c9be8b2a4960a39693770de0ac9a.png

内存指针: 指明了自己分配到的内存.


文件描述符表: 硬盘上的文件等其他资源.


一个进程一运行, 操作系统就会自动打开至少3个文件: 标准输入(System.in), 标准输出(System.out), 标准错误(System.err).


想要一个进程正常工作就需要为这个进程分配资源, 包括但不限于内存, 硬盘, CPU.

3.2 并发和并行

分配资源时, 对于其他资源都比较好分配, 但CPU资源是不太好分配的: 电脑中进程可能有几十上百个, 电脑的CPU(现在电脑大多是多核CPU)资源是很有限的, 远不够给这些进程同时分配.


但我们又希望进程都是同时运行的, 上面说过, 一个进程中可以有多个线程, 我们可以让多个线程去并发执行(分时复用), 那么什么是并发呢?

并行性和并发性是既相似又有区别的两个概念, 并行性是指两个或多个事件在同一时刻发生, 而并发性是指两个或多个事件在同一时间间隔内发生.


从宏观上来讲, 并行与并发没有区别, 或者说是我们肉体凡胎感知不到.

从微观上来讲, 并行表示两个CPU核心同时运行两个任务的代码, 并发表示一个CPU核心先运行任务A的代码,再运行任务B的代码, 也就是多个任务能够在CPU核心上快速的切换运行; 只要运行得足够快, 就可以认为多个任务在同时运行.


3.3 进程的调度

线程是系统调度执行的最基本单位, 为了方便描述进程的调度, 这里假定此处的进程只有一个线程, 这样就可以将线程的调度视为进程的调度, 这里我们谈到的进程调度是基于多任务操作系统, 就是从宏观上看, 同一时间能够同时运行多个进程.


进程的状态:


就绪状态: 准备就绪, 随时等待CPU调度执行.

运行状态: 线程正在CPU上运行的状态.

阻塞状态: 进程由于一系列因素无法及时响应调度, 短时间内无法在CPU上执行.

优先级:


多个进程, 操作系统进行调度的时候, 并不是完全平等, 而是有一定的优先级的.


上下文:


表示上一次进程被调出CPU的时候, 程序的运行状态(将进程的 “中间状态” 保存下来), 这样下一次这个进程调度到CPU时就能接着上次的状态继续运行.


进程的上下文, 就是CPU中的各个寄存器的值, 所以进程调出CPU之前, 需要先把CPU上寄存器上的数据保存到内存中,相当于存档; 下次该进程再被调进CPU时, 就可以根据上次储存在内存中的数据恢复到寄存器中, 相当于读档.


记账信息:


操作系统统计每个进程在CPU上的占用的时间以及指令数目, 根据这个来决定下一阶段如何调度.


举一个形象的场景来进行理解:


比如有三个进程A, B, C, 不妨将操作系统比作一个小姐姐, 将线程A, B, C比作三位小哥哥, A特别帅, B特别有钱, C特别会舔.


这位小姐姐同时与A, B, C 一起谈恋爱, 那么这里小姐姐姐如何同时谈三个男朋友呢, 其实也很简单, 小姐姐只需要做好对ABC的时间管理即可, 让ABC都不知道对方的存在.


所以小姐姐需要妥善地安排与三位小哥哥谈恋爱的时间, 确保时间不能重合, 比如:

因为小姐姐特别喜欢帅的, 其次是有钱的, 最后是会舔的, 所以小姐姐对A, B, C的安排有了优先级, 给A安排的时间多一点, B次之, C最后。

此时小姐姐做了一周时间的规划表, 属实是时间管理大师了.

时间 计划
星期一 与C谈恋爱
星期二到星期三 与B谈恋爱
星期四到星期六 与A谈恋爱
星期日 休息

此时小姐姐这个时间规划, 就是可以理解为进程的调度, 谈恋爱期间, 如果B需要出差一个月, 那么在小姐姐眼中, B这种状态就称为阻塞或睡眠状态, 如果正常, 男朋友可以随叫随到, 则在小姐姐眼中就称这种状态为就绪状态, 小姐姐正在与A谈恋爱的状态称为运行状态.


小姐姐与三位小哥哥同时谈恋爱, 总会有时候"串戏", 比如A需要带小姐姐回家见家长, 让小姐姐准备准备一份礼物, 而B想要带小姐姐去三亚旅游, 要小姐姐做好准备,但是有一天A问小姐姐准备好没?小姐姐说我买了两件泳衣, 这样就 “串戏” 了, 小姐姐最后总算将A忽悠过去了, 小姐姐吸取了教训, 之后每次都会将上次和某人进行到啥程度了, 是否有啥未完成的任务之类的记录下来, 下次约会的时候, 能够恢复出之前的状态, 这样小姐姐在之后就不会串戏了, 这个场景就可以看作是操作系统中的上下文了.


小姐姐和ABC都已经相处了一段时间了, 就需要去想一想, 也就是每隔一段时间,做个总结, 统计一下在每个人身上大概花费了多少精力, 然后根据这个总结做出下一个阶段的时间安排, 要保证和每个人要始终保持好一个合适的尺度, 不能太远也不能太近, 这便可以看作是进程调度中的记账信息了.


3.4 内存管理

我们通过代码程序所获取的内存地址, 其实不是硬件上真真实实的地址, 而是经过一些计算整合出来的虚拟地址.


内存(物理上是一个内存条), 其中可以存很多数据, 内存就可以想象成是一个大走廊, 走廊非常长, 有很多房间, 每个房间大小是1Byte, 每个房间还有个编号, 从0开始依次累加, 这个内存编号就是"地址", 这个地址也就"物理地址".


下面张图就是真实的内存条.


内存有个了不起的特性就是随机访问, 访问内存上的任意地址的数据, 速度都极快, 时间上都差不多, 正是这个特点造就了数组取下标操作的时间复杂度是 O(1).

73d8c9be8b2a4960a39693770de0ac9a.png

我们的程序如果直接访问的就是物理地址, 如果程序正常运行还好, 但如果代码出bug了, 就可能出现越界访问的情况, 就可能会对其他进程的数据做出一些操作, 比如进程1的指针变量因为bug变成了0x8000, 也就是说这里明明是进程1的bug却把进程给搞坏了.

73d8c9be8b2a4960a39693770de0ac9a.png

为了避免这种问题的出现, 针对进程使用的内存空间进行了"隔离", 引入了虚拟地址空间.


代码里不再直接使用真实的物理地址了, 而是使用虚拟的地址; 由操作系统和专门的硬件设备负责进行虚拟地址到物理地址的转换.

73d8c9be8b2a4960a39693770de0ac9a.png

这里引入的虚拟空间地址, 对物理地址进行隐藏和隔离, 避免进程之间的相互影响, MMU硬件设备(很多时候是集成在CPU里的), 可以让虚拟地址和物理地址的转换速度更快.


当我们操作系统内核发现转换后的地址超过了该进程物理地址的访问范围, 就会直接向进程反馈一个错误, 进而引发该进程崩溃, 不会影响到其他进程.

3.5 进程间的通信

上面引入的虚拟空间地址使进程之间相互隔离, 这就导致每个进程只能访问自己的那一块地址空间的数据, 无法访问其他进程的地址空间的数据, 这样当一个进程崩溃时, 另外的进程不会受到影响.


但是, 在实际开发工作中, 有些时候进程间需要进行数据的交互的, 为了实现进程间的通信, 操作系统提供了一块多个进程都能访问到的 “公共空间” , 进程A可以先把数据存入 “公共空间” , 然后进程B可以到 “公共空间” 取出数据, 这样就实现了进程间的通信.


操作系统提供的 “公共空间/通信方式” 有很多种具体的体现形式, 现在最常见的进程间通信机制有:


基于文件操作.

基于网络操作(socket).

3.6 并发编程

CPU进入了多核心的时代, 要想进一步提高程序的执行速度, 就需要充分的利用CPU的多核资源, 这个时候并发编程就应用而生了; 引入进程的概念, 最主要的目的是为了解决"并发编程" 这样的问题, 让代码程序能够把这些CPU核心充分利用.


其实多进程编程已经可以将cpu的多核资源利用起来了, 可以解决并发编程的问题了, 但还是存在一些问题, 进程的创建需要分配资源, 进程的销毁需要回收资源, 而分配和回收资源的效率相对来说是比较低的, 也就是说进程的创建,销毁,调度的开销都是比较大的, 成本是比较高的.


问题解决:


方案1:

使用进程池, 与字符串常量池类似, 将已经创建的进程存入常量池, 后面需要使用时直接加载资源即可, 但是也存在问题, 那就是闲置的进程也会占用资源, 相当于空间换时间, 消耗空间来提升效率.


方案2:

使用多线程来实现并发编程, 因为进程包含线程, 一个进程中包含多个线程,所以线程比进程更轻量,因此也将线程称为 “轻量级进程”, 线程把申请资源/释放资源的操作给省下了, 这就致使线程的创建,销毁,调度的成本很低, 速度更快, 所以使用多线程实现并发编程比多进程更加合适.


类比以下场景进行理解多线程:


我们把进程看做一个工厂, 线程看作工厂中的生产线, 比如有一个任务需要生产一些硬件设备, 想要生产速度更快, 有两个方案:


建造两个工厂进行生产.

只建造一个工厂, 多增加一批生产线.

上面两种方案都以相同的效率生产设备, 但是方案1需要多建造一个工厂; 方案2只需要增加一批生产线, 复用同一个工厂的资源即可(原料, 运输资源等), 而方案1较之方案2的付出的代价更大, 类比一下方案1表示就是多进程实现并发编程, 方案2表示的是多线程实现并发编程.


4. 进程与线程的区别

进程包含线程, 一个进程包含一个或者多个线程.

进程间是独立的, 每个进程都有独立的地址空间, 一个进程崩溃不会影响其余的进程, 但是在同一个进程中, 多个线程是共用的是一块资源, 一个线程崩溃, 这个进程中的所有线程都会崩溃.

进程是操作系统分配资源的基本单位, 线程是操作系统调度执行的基本单位.

多进程相比于多线程不会存在线程安全的问题, 多线程编程可能存在线程安全问题.


目录
相关文章
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
8月前
|
Java 测试技术 API
【JUC】(1)带你重新认识进程与线程!!让你深层次了解线程运行的睡眠与打断!!
JUC是什么?你可以说它就是研究Java方面的并发过程。本篇是JUC专栏的第一章!带你了解并行与并发、线程与程序、线程的启动与休眠、打断和等待!全是干货!快快快!
1150 2
|
8月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
409 1
|
8月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
384 1
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
451 67
|
12月前
|
调度 开发工具 Android开发
【HarmonyOS Next】鸿蒙应用进程和线程详解
进程的定义: 进程是系统进行资源分配的基本单位,是操作系统结构的基础。 在鸿蒙系统中,一个应用下会有三类进程:
433 0
|
缓存 运维 前端开发
阿里云操作系统控制台:高效解决性能瓶颈与抖动之进程热点追踪
遇到“进程性能瓶颈导致业务异常”等多项业务痛点时,提供高效解决方案,并展示案例。
|
SQL 监控 网络协议
YashanDB进程线程体系
YashanDB进程线程体系
|
数据采集 Java 数据处理
Python实用技巧:轻松驾驭多线程与多进程,加速任务执行
在Python编程中,多线程和多进程是提升程序效率的关键工具。多线程适用于I/O密集型任务,如文件读写、网络请求;多进程则适合CPU密集型任务,如科学计算、图像处理。本文详细介绍这两种并发编程方式的基本用法及应用场景,并通过实例代码展示如何使用threading、multiprocessing模块及线程池、进程池来优化程序性能。结合实际案例,帮助读者掌握并发编程技巧,提高程序执行速度和资源利用率。
766 0

热门文章

最新文章

推荐镜像

更多