进程

​ cpu是计算机的核心,主要处理计算机的计算任务。操作系统是计算机的管理员,它负责任务的调度,资源的管理和分配,统一管理计算机的硬件资源。应用程序则是具有某种功能的程序,应用程序运行于操作系统之上。

​ 进程是一个具有特定功能的程序运行在一个数据集上的一次动态过程。是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。

进程一般由这几部分组成:

程序:用于描述进程要完成的功能,是控制进程执行的指令集

数据集合:程序执行时所需的数据和工作空间

程序控制块:包含进程的描述信息和控制信息,组成进程的唯一标志

进程间通信的几种方式

管道

​ 一般用于单向数据流转,只有父子进程进行通信,特点是容量小,数据传输慢。

消息队列

​ 消息队列是由消息组成的链表,存放在内核中并由消息队列标识符标识。消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点

信号量

​ 不能用来传递复杂消息,只能用来同步

共享内存

​ 利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点。共享内存块提供了在任意数量的进程之间进行高效双向通信的机制。每个使用者都可以读取写入数据,但是所有程序之间必须达成并遵守一定的协议,以防止诸如在读取信息之前覆写内存空间等竞争状态的出现。

套接字

​ 套解字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

进程间通信方式的选择

  • 管道用来实现进程间相互发送非常短小的、频率很高的消息,这种方式通常适用于两个进程间的通信
  • 共享内存用来实现进程间共享的、非常庞大的、读写操作频率很高的数据;这种方法适用于多进程间的通信
  • 其他考虑用socket。主要应用在分布式开发中

线程上下文切换

由于中断处理,用户态切换,多任务处理等原因会导致cpu从一个线程切换到另一个线程时,切换过程需要保存当前线程的状态并且恢复另一个线程的状态。

上下文切换的代价是高昂的,上下文切换的延迟取决于不同的因素,一次切换大概的耗时为60~100纳秒,每个CPU核心平均每纳秒能执行12条指令,所以一次上下文切换会耗费720~1200条指令的延迟时间。而且在实际程序中,上下文切换会占用大量程序执行的时间。

如果存在跨核上下文切换,会导致cpu缓存失效(cpu从缓存访问数据的大致成本在3到40个时钟周期,从内存访问数据的成本在100到300个时间周期),这时的切换成本会更加高昂。

协程

Golang 从语言级别支持并发,通过轻量级协程 Goroutine 来实现程序并发运行。

goroutine非常轻量,主要体现在这两方面:

上下文切换代价小:上下文切换只涉及3个寄存器的值修改。线程上下文切换需要从用户态切换到内核态,并且需要修改20多个寄存器的值。

内存空间占用小:一个线程栈空间通常是2M,Goroutine最小栈空间为2K。

GO调度器实现机制

go程序通过调度器来调度goroutine在内核线程上执行,但不是直接使用内核线程(Machine)来直接执行goroutine,而是由scheduler中的processor来作为获取内核线程的【中介】。

Go 调度器模型我们通常叫做G-P-M 模型,他包括 4 个重要结构,分别是G、P、M、Sched

  • G: Goroutine,每个 Goroutine 对应一个 G 结构体,G 存储 Goroutine 的运行堆栈、状态以及任务函数,可重用。G 并非执行体,每个 G 需要绑定到 P 才能被调度执行。
  • P: Processor,表示逻辑处理器,对 G 来说,P 相当于 CPU 核,G 只有绑定到 P 才能被调度。对 M 来说,P 提供了相关的执行环境(Context),如内存分配状态(mcache),任务队列(G)等。P 的数量决定了系统内最大可并行的 G 的数量(前提:物理 CPU 核数 >= P 的数量)。P 的数量由用户设置的 GoMAXPROCS 决定,但是不论 GoMAXPROCS 设置为多大,P 的数量最大为 256。
  • M: Machine,OS 内核线程抽象,代表着真正执行计算的资源,在绑定有效的 P 后,进入 schedule 循环;而 schedule 循环的机制大致是从 Global 队列、P 的 Local 队列以及 wait 队列中获取。M 的数量是不定的,由 Go Runtime 调整,为了防止创建过多 OS 线程导致系统调度不过来,目前默认最大限制为 10000 个。M 并不保留 G 状态,这是 G 可以跨 M 调度的基础。
  • Sched:Go 调度器,它维护有存储 M 和 G 的队列以及调度器的一些状态信息等。调度器循环的机制大致是从各种队列、P 的本地队列中获取 G,切换到 G 的执行栈上并执行 G 的函数,调用 Goexit 做清理工作并回到 M,如此反复。

在 Go 程序里我们通过下面的图示来展示 G-P-M 模型:

P代表可以并行运行的逻辑处理器,每个P被分配到一个系统线程M。G代表协程(Goroutine)。Go调度器有两个不同的调度队列,全局队列(GRQ)和每个P都有一个本地队列(LRQ),LRQ用于管理分配给P中上下文执行的G,这些Gorotine轮流被和P绑定的M执行上下文切换。GRQ适用于那些还未分配P的goroutine。

从上图可以看出,G 的数量可以远远大于 M 的数量,换句话说,Go 程序可以利用少量的内核级线程来支撑大量 Goroutine 的并发。多个 Goroutine 通过用户级别的上下文切换来共享内核线程 M 的计算资源,但对于操作系统来说并没有线程上下文切换产生的性能损耗。

为了更加充分利用线程的计算资源,Go 调度器采取了以下几种调度策略:

任务窃取(work-stealing)

当某个P的运行负载比较低时,调度器允许从GRQ或者别的P的LRQ获取G执行。(使各个P的运行压力均衡)

减少阻塞

场景一

由于一些原子,互斥或者通道操作导致Goroutine阻塞,调度器会把当前阻塞的G切换出去,从LRQ获取一个新的G执行。

场景二

由于网络操作和IO操作导致Goroutine阻塞,这种情况如何优化呢?

Go程序提供了网络轮训器(netpoller)来处理网络请求和IO操作的阻塞问题。netpoller底层通过IO多路复用技术来实现,通过netpoller来进行网络系统调用,调度器可以防止G在进行这些系统调用时阻塞M。将网络事件的监听交给netpoller来处理,从而可以使M去获取LRQ中的另一个G执行,不用再创建一个新的内核线程M,有助于减少操作的调度负载。

(1)G1 正在 M 上执行,还有 3 个 Goroutine 在 LRQ 上等待执行。网络轮询器空闲着,什么都没干。

(2)G1执行一个网络操作,G1被移动到网络轮训器并且处理异步网络系统调用。M从LRQ获取一个新的G2执行。

(3)异步网络系统调用完成,G1重新加入到LRQ,一旦G1再次被M上下文切换,会继续执行G1负责的相关代码。这里最大的优势是网络系统的调用不需要创建额外的系统线程M。并且Go网络轮训器底层使用非阻塞IO+IO多路复用机制实现了一个高性能的网络编程机制。

场景三

进行一些系统调用会导致阻塞时,网络轮训器此时无法使用,这时的解决方式是,将原来的内核线程M1和这个阻塞G1移除出去,给原来的P1创建一个新的内核线程M2,由M2来执调度LRQ的G。G1阻塞的系统调用完成之后,G1会重新放入到P1的LRQ中,M1会暂时闲置等待后面再次被使用。

场景四

如果Goroutine去执行了一个sleep()操作,导致M阻塞了。这时后台会有一个监控线程syscom,它监控那些长时间运行的G任务然后设置可抢占标识,别的Goroutine就可以抢行进来执行。

总结:

进程是具有特定功能的程序运行在数据集上的一次动态过程。是操作系统分配资源的最小单位,是应用程序运行的载体。一个进程可以有多个线程,进程之间相互独立,都有一块自己的内存空间。

线程是程序执行的最小单位,一个进程由一个或者多个线程组成,线程是进程中代码执行的不同路线。同一个进程中的各个线程共享程序的内存空间(包括代码段,堆和数据集,打开的文件),每个线程还会有自己的栈和寄存器,这些空间是每个线程独占的。

协程是一种用户态的轻量级线程,协程的切换完全由用户控制,协程间切换只需保存任务的上下文,没有内核的消耗。

操作系统的线程和进程的区别_进程,线程,协程,有何区别?相关推荐

  1. python线程协程进程的区别_进程和线程、协程的区别

    现在多进程多线程已经是老生常谈了,协程也在最近几年流行起来.python中有协程库gevent,py web框架tornado中也用了gevent封装好的协程.本文主要介绍进程.线程和协程三者之间的区 ...

  2. python进程线程协程区别_进程和线程、协程的区别

    现在多进程多线程已经是老生常谈了,协程也在最近几年流行起来.python中有协程库gevent,py web框架tornado中也用了gevent封装好的协程.本文主要介绍进程.线程和协程三者之间的区 ...

  3. 比物理线程都好用的C++20的协程,你会用吗?

    摘要:事件驱动(event driven)是一种常见的代码模型,其通常会有一个主循环(mainloop)不断的从队列中接收事件,然后分发给相应的函数/模块处理.常见使用事件驱动模型的软件包括图形用户界 ...

  4. unity怪物攻击玩家减血_利用Unity协程实现一个简单的怪物寻路与跟随AI

    利用Unity协程实现一个简单的怪物寻路与跟随AI,通过分析怪物行为与逻辑,实现简单的平面怪物寻路与跟随效果. 分析 对于游戏中怪物的行为,简单归纳为如下几部分: 怪物在预设范围内随机移动. 玩家走入 ...

  5. 操作系统的线程和进程的区别_面试官:你熟悉多线程嘛?线程跟进程有什么区别?...

    这篇文章跟大家聊聊线程,讲到线程,⼜不得不提进程了~ 进程我们估计是很了解的了,在windows下打开任务管理器,可以发现我们在操作系统上运⾏的程序都是进程. 什么是叫一个进程? 什么叫一个线程? 进 ...

  6. [Linux]线程概念_线程控制(线程与进程的区别与联系 | 线程创建 | 线程等待 | 线程终止 | 线程分离 | LWP)

    文章目录 线程概念 进程和线程的关系 线程的优点 线程的缺点 线程控制 Linux线程和接口关系的认识 线程创建 线程ID及进程地址空间布局 线程等待 线程终止 线程终止状态 线程分离 LWP和pth ...

  7. 进程和线程的概念、区别及进程线程间通信

    进程与线程的概念,以及为什么要有进程线程,其中有什么区别,他们各自又是怎么同步的? 1. 基本概念: 进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发: 线程是进程 ...

  8. python进程线程协程区别_Python3多线程与协程

    python中的多线程非常的常用,之前一直糊里糊涂地使用,没有一些系统性的概念,记录一下~ 0x001 多线程的优势:可将长时间占用的程序放到后台 可能会加速程序执行速度 能够实现一些类似同步执行的效 ...

  9. taskkill无法终止进程 拒绝访问_进程的基本概念

    程序顺序执行的特征 程序并发执行的特性 进程的特征 进程的状态及转换 进程的三种基本状态 创建和终止状态 进程状态的转换 进程管理中的数据结构 进程控制块PCB的作用 进程控制块中的信息 进程控制块的 ...

最新文章

  1. linux多用户怎么表示,Linux如何建立多用户
  2. Java经典面试题详解:springboot文件下载大小限制
  3. 深入理解int a[5];
  4. TabControl控件
  5. C语言数组元素总和最大的连续子序列的算法(附完整源码)
  6. 【C语言】练习5-8
  7. [UE4]瞬移前后屏幕亮度变化,Get Player Camera Manager.Start Camera Fade
  8. iOS 定位功能的实现
  9. android -------- MVP+DataBinding 的使用
  10. 利用C#实现标准的 Dispose模式
  11. [Linux 使用(2)] 64位Linux下安装jboss-as-7.1 以及jdk1.7
  12. arduino温湿度计库文件_arduino学习笔记八 温湿度计
  13. 多目标进化优化 郑金华pdf_简化审批流程 金华首张以“告知承诺制”审批的医疗器械经营许可证发放...
  14. 微软想让所有人都成为开发者?
  15. Android入门:EditText
  16. Zabbix监控系统深度实践
  17. 2. MFC编程——各函数作用
  18. Hyperscan中的 NFA模型演化
  19. 11月8日 课程设计幸运抽奖系统
  20. 36岁老码农现身说法

热门文章

  1. IT Monitor
  2. jquery 获取 id ,但是id 里面不能有. 这个符号
  3. Nginx 服务并发过10万的Linux内核优化配置
  4. 《数据库原理与应用(第3版)》——小结
  5. perf之sched
  6. android 控件描边取消重叠
  7. 手把手实现腾讯qq拖拽删去效果(二)
  8. petshop 4.0的数据持久层
  9. 「干货」编程语言十大经典算法,你知道几个?
  10. 解决方案 | tensorflow安装慢解决方案