进程和线程及相关概念

本文主要记录了进程,线程等相关内容。

抛开各种技术细节,从应用程序角度讲:
1、在单核计算机里,有一个资源是无法被多个程序并行使用的:CPU。
没有操作系统的情况下,一个程序一直独占着全部 CPU。
如果要有两个任务来共享同一个 CPU,程序员就需要仔细地为程序安排好运行计划–某时刻 CPU 和由程序 A 来独享,下一时刻 CPU 由程序 B 来独享,而这种安排计划后来成为 OS 的核心组件,被单独名命为 “scheduler”,即“调度器”,它关心的只是怎样把单个 CPU 的运行拆分成一段一段的“运行片”,轮流分给不同的程序去使用,而在宏观上,因为分配切换的速度极快,就制造出多程序并行在一个 CPU 上的假象。

2、在单核计算机里,有一个资源可以被多个程序共用,然而会引出麻烦:内存
在一个只有调度器,没有内存管理组件的操作系统上,程序员需要手工为每个程序安排运行的空间 – 程序A使用物理地址 0x00-0xff, 程序B使用物理地址0x100-0x1ff,等等。
然而这样做有个很大的问题:每个程序都要协调商量好怎样使用同一个内存上的不同空间,软件系统和硬件系统千差万别,使这种定制的方案没有可行性。
为了解决这个麻烦,计算机系统引入了“虚拟地址”的概念,从三方面入手来做:
2.1、硬件上,CPU 增加了一个专门的模块叫 MMU,负责转换虚拟地址和物理地址。
2.2、操作系统上,操作系统增加了另一个核心组件:memory management,即内存管理模块,它管理物理内存、虚拟内存相关的一系列事务。
2.3、应用程序上,发明了一个叫做【进程】的模型,(注意)每个进程都用【完全一样的】虚拟地址空间,然而经由操作系统和硬件MMU协作,映射到不同的物理地址空间上。不同的【进程】,都有各自独立的物理内存空间,不用一些特殊手段,是无法访问别的进程的物理内存的。

3、现在,不同的应用程序,可以不关心底层的物理内存分配,也不关心 CPU 的协调共享了。然而还有一个问题存在:有一些程序,想要共享 CPU,【并且还要共享同样的物理内存】,这时候,一个叫【线程】的模型就出现了,它们被包裹在进程里面,在调度器的管理下共享 CPU,拥有同样的虚拟地址空间,同时也共享同一个物理地址空间,然而,它们无法越过包裹自己的进程,去访问别一个进程的物理地址空间。

4、进程之间怎样共享同一个物理地址空间呢?不同的系统方法各异,符合 posix 规范的操作系统都提供了一个接口,叫 mmap,可以把一个物理地址空间映射到不同的进程中,由不同的进程来共享。

5、PS:在有的操作系统里,进程不是调度单位(即不能被调度器使用),线程是最基本的调度单位,调度器只调度线程,不调度进程,比如 VxWorks
来源


>CPU+RAM+各种资源(比如显卡,光驱,键盘,GPS, 等等外设)构成我们的电脑,但是电脑的运行,实际就是 CPU 和相关寄存器以及 RAM 之间的事情。 **一个最最基础的事实**:CPU 太快,太快,太快了,寄存器仅仅能够追的上他的脚步,RAM 和别的挂在各总线上的设备完全是望其项背。那当多个任务要执行的时候怎么办呢?轮流着来?或者谁优先级高谁来?不管怎么样的策略,一句话就是在 CPU 看来就是轮流着来。 **一个必须知道的事实**:执行一段程序代码,实现一个功能的过程介绍 ,当得到 CPU 的时候,相关的资源必须也已经就位,就是显卡啊,GPS 啊什么的必须就位,然后 CPU 开始执行。这里除了 CPU 以外所有的就构成了这个程序的执行环境,也就是我们所定义的程序上下文。当这个程序执行完了,或者分配给他的 CPU 执行时间用完了,那它就要被切换出去,等待下一次 CPU 的临幸。在被切换出去的最后一步工作就是保存程序上下文,因为这个是下次他被 CPU 临幸的运行环境,必须保存。 **串联起来的事实**:前面讲过在 CPU 看来所有的任务都是一个一个的轮流执行的,具体的轮流方法就是:先加载程序A的上下文,然后开始执行 A,保存程序 A 的上下文,调入下一个要执行的程序 B 的程序上下文,然后开始执行 B,保存程序 B 的上下文。。。。

========= 重要的东西出现了========
进程和线程就是这样的背景出来的,两个名词不过是对应的CPU时间段的描述,名词就是这样的功能
进程就是包换上下文切换的程序执行时间总和 = CPU 加载上下文 + CPU 执行 + CPU 保存上下文
**线程是什么呢?**进程的颗粒度太大,每次都要有上下的调入,保存,调出。如果我们把进程比喻为一个运行在电脑上的软件,那么一个软件的执行不可能是一条逻辑执行的,必定有多个分支和多个程序段,就好比要实现程序 A,实际分成 a,b,c 等多个块组合而成。那么这里具体的执行就可能变成:程序 A 得到 CPU => CPU 加载上下文,开始执行程序 A 的 a 小段,然后执行 A 的 b 小段,然后再执行 A 的 c 小段,最后 CPU 保存 A 的上下文。这里 a,b,c 的执行是共享了 A 的上下文,CPU 在执行的时候没有进行上下文切换的。这里的 a,b,c 就是线程,也就是说线程是共享了进程的上下文环境的更为细小的 CPU 时间段。到此全文结束,再一个总结:进程和线程都是一个时间段的描述,是 CPU 工作时间段的描述,不过是颗粒大小不同。
来源

一、 cpu个数、核数、线程数的关系
cpu个数:是指物理上,也及硬件上的核心数;
核数:是逻辑上的,简单理解为逻辑上模拟出的核心数;
线程数:是同一时刻设备能并行执行的程序个数,线程数=cpu个数 * 核数【如果有超线程,再乘以超线程数】

二、 cpu线程数和Java多线程
首先明白几个概念:
(1) 单个cpu线程在同一时刻只能执行单一Java程序,也就是一个线程
(2) 单个线程同时只能在单个cpu线程中执行
(3) 线程是操作系统最小的调度单位,进程是资源(比如:内存)分配的最小单位
(4)Java中的所有线程在JVM进程中,CPU调度的是进程中的线程
(5)Java多线程并不是由于cpu线程数为多个才称为多线程,当Java线程数大于cpu线程数,操作系统使用时间片机制,采用线程调度算法,频繁的进行线程切换。

a 那么java多进程,每个进程又多线程,cpu是如何调度的呢?
个人理解:操作系统并不是单纯均匀的分配cpu执行不同的进程,因为线程是调度的最小单位,所以会根据不同进程中的线程个数进行时间分片,均匀的执行每个线程,也就是说A进程中有10个线程,而B进程中有2个线程,那么cpu分给进程的执行时间理论上应该是5:1才合理。

b cpu线程数和java线程数有直接关系吗?
个人理解:没有直接关系,正如上面所说,cpu采用分片机制执行线程,给每个线程划分很小的时间颗粒去执行,但是真正的项目中,一个程序要做很多的的操作,读写磁盘、数据逻辑处理、出于业务需求必要的休眠等等操作,当程序在进行I/O操作的时候,线程是阻塞的,线程由运行状态切换到等待状态,此时cpu会做上下文切换,以便处理其他的程序;当I/O操作完成后,cpu 会收到一个来自硬盘的中断信号,并进入中断处理例程,手头正在执行的线程因此被打断,回到 ready 队列。而先前因 I/O 而waiting 的线程随着 I/O 的完成也再次回到 就绪 队列,这时 cpu 可能会选择它来执行。

c 如何确定程序线程数?
个人理解:如果所有的任务都是计算密集型的,则创建的多线程数 = 处理器核心数就可以了
如果io操作比较耗时,则根据具体情况调整线程数,此时 多线程数 = n*处理器核心数
一般情况程序线程数等于cpu线程数的两到三倍就能很好的利用cpu了,过多的程序线程数不但不会提高性能,反而还会因为线程间的频繁切换而受影响,具体需要根据线程处理的业务考略,不断调整线程数个数,确定当前系统最优的线程数。
原文


一篇非常好的文章

进程与线程,单核与多核


文章作者: taosean
文章链接: https://taosean.github.io/2019/06/14/Process-and-Thread/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 taosean's 学习之旅