Answers:
进程和线程都是独立的执行序列。典型的区别是(同一进程的)线程在共享内存空间中运行,而进程在单独的内存空间中运行。
我不确定您可能指的是“硬件”还是“软件”线程。线程是一种操作环境功能,而不是CPU功能(尽管CPU通常具有使线程有效的操作)。
Erlang使用术语“过程”,因为它没有公开共享内存的多程序模型。将它们称为“线程”将暗示它们具有共享内存。
流程
每个流程都提供执行程序所需的资源。进程具有虚拟地址空间,可执行代码,系统对象的打开句柄,安全上下文,唯一进程标识符,环境变量,优先级类别,最小和最大工作集大小以及至少一个执行线程。每个进程都从单个线程(通常称为主线程)开始,但是可以从其任何线程中创建其他线程。
线程
线程是进程中可以安排执行的实体。进程的所有线程共享其虚拟地址空间和系统资源。另外,每个线程维护异常处理程序,调度优先级,线程本地存储,唯一的线程标识符以及系统将用于保存线程上下文直到被调度的一组结构。线程上下文包括线程进程的地址空间中的线程的计算机寄存器集,内核堆栈,线程环境块和用户堆栈。线程也可以具有自己的安全上下文,可用于模拟客户端。
在以下位置的Microsoft文档上可以找到此信息:关于进程和线程
Microsoft Windows支持抢先式多任务处理,这可以同时执行来自多个进程的多个线程。在多处理器计算机上,系统可以同时执行与计算机上处理器数量一样多的线程。
处理:
线:
首先,让我们看一下理论方面。您需要了解流程在概念上的含义,以了解流程与线程之间的区别以及它们之间的共享。
Tanenbaum 在2.2.2节“ 现代操作系统中的经典线程模型”中有以下内容:
流程模型基于两个独立的概念:资源分组和执行。有时将它们分开很有用;这是线程进入的地方。
他继续:
查看流程的一种方式是将相关资源组合在一起。进程的地址空间包含程序文本和数据以及其他资源。这些资源可能包括打开的文件,子进程,挂起的警报,信号处理程序,记帐信息等。通过以流程的形式将它们放在一起,可以更轻松地对其进行管理。进程具有的另一个概念是执行线程,通常简称为线程。该线程具有程序计数器,该计数器跟踪接下来要执行的指令。它具有寄存器,用于保存其当前的工作变量。它有一个堆栈,其中包含执行历史记录,每个调用但尚未返回的过程都有一帧。尽管线程必须在某些进程中执行,线程及其过程是不同的概念,可以分别处理。流程用于将资源分组在一起;线程是计划在CPU上执行的实体。
在下方,他提供了下表:
Per process items | Per thread items
------------------------------|-----------------
Address space | Program counter
Global variables | Registers
Open files | Stack
Child processes | State
Pending alarms |
Signals and signal handlers |
Accounting information |
让我们处理硬件多线程问题。传统上,CPU将支持单个执行线程,并通过单个程序计数器和一组寄存器来维护线程的状态。但是,如果出现缓存未命中怎么办?从主内存中获取数据需要花费很长时间,而在这种情况下,CPU只是闲置在那里。因此,有人想到了基本具有两组线程状态(PC +寄存器)的想法,以便另一个线程(可能在同一进程中,也许在不同进程中)可以在另一个线程正在等待主内存的同时完成工作。此概念有多种名称和实现,例如超线程和同步多线程(简称SMT)。
现在让我们看一下软件方面。基本上可以通过三种方式在软件端实现线程。
实现线程所需要做的就是保存CPU状态并维护多个堆栈的能力,在许多情况下,这可以在用户空间中完成。用户空间线程的优势是超快速的线程切换,因为您不必陷入内核,并且可以按自己的方式调度线程。最大的缺点是无法阻止I / O(这将阻止整个进程及其所有用户线程),这是我们首先使用线程的主要原因之一。在许多情况下,使用线程阻止I / O可以大大简化程序设计。
除了将所有调度问题留给OS之外,内核线程还具有能够使用阻塞I / O的优势。但是每个线程切换都需要陷入内核,这可能相对较慢。但是,如果由于I / O阻塞而切换线程,这并不是真正的问题,因为I / O操作可能已经将您困在了内核中。
另一种方法是将两者结合在一起,每个内核线程具有多个用户线程。
因此,回到术语问题,您可以看到流程和执行线程是两个不同的概念,您选择使用哪个术语取决于您所讨论的内容。关于“轻量级过程”,我个人没有看到它的意义,因为它并没有真正传达出正在发生的事情以及术语“执行线程”。
关于并发编程的更多解释
流程具有独立的执行环境。流程通常具有一套完整的私有基本运行时资源;特别是,每个进程都有自己的存储空间。
线程存在于一个进程中-每个进程至少有一个。线程共享进程的资源,包括内存和打开的文件。这样可以进行有效的通信,但可能会出现问题。
牢记普通人,
在计算机上,打开Microsoft Word和Web浏览器。我们称这两个过程。
用Microsoft Word,您输入一些东西,它将自动保存。现在,您将观察到编辑和保存是并行进行的-在一个线程上进行编辑,然后在另一个线程上进行保存。
处理:
示例:
说,打开任何浏览器(mozilla,Chrome,IE)。此时,新过程将开始执行。
线程数:
示例:
在浏览器中打开多个选项卡。
线程和进程都是OS资源分配的原子单位(即,有一个并发模型描述如何在它们之间分配CPU时间,以及拥有其他OS资源的模型)。有一个区别:
上面的Greg Hewgill关于“过程”一词的Erlang含义是正确的,这里讨论了为什么Erlang可以进行轻量级的过程。
进程和线程都是独立的执行序列。典型的区别是(同一进程的)线程在共享内存空间中运行,而进程在单独的内存空间中运行。
处理
是正在执行的程序。它具有文本部分,即程序代码,由程序计数器的值和处理器寄存器的内容表示的当前活动。它还包括包含临时数据(例如函数参数,返回的寻址变量和局部变量)的过程堆栈,以及包含全局变量的数据部分。进程还可以包括堆,该堆是在进程运行时动态分配的内存。
线
线程是CPU使用率的基本单位。它包括线程ID,程序计数器,寄存器集和堆栈。它与属于同一进程的其他线程共享它的代码段,数据段和其他操作系统资源,例如打开的文件和信号。
-摘自Galvin的操作系统
http://lkml.iu.edu/hypermail/linux/kernel/9608/0191.html
莱纳斯·托瓦尔兹(Linus Torvalds)(torvalds@cs.helsinki.fi)
1996年8月6日,星期二12:47:31 +0300(EET DST)
邮件排序方式:[日期] [主题] [主题] [作者]
下一条消息:Bernd P. Ziller:“回复:糟糕,get_hash_table”
上一条消息:Linus Torvalds:“回复:I / O请求订购”
Peter P. Eiserloh在1996年8月5日星期一写道:
我们需要保持线程概念的清晰。似乎有太多人将线程与进程混淆。以下讨论并不反映linux的当前状态,而是试图停留在较高级别的讨论中。
没有!
没有理由认为“线程”和“进程”是分开的实体。传统上就是这样,但是我个人认为这样做是一个重大错误。认为这种方式的唯一原因是历史包bag。
线程和进程实际上只是一件事:“执行上下文”。试图人为地区分不同情况只是自我限制。
“执行的上下文”,这里称为COE,只是该COE的所有状态的集合体。该状态包括CPU状态(寄存器等),MMU状态(页面映射),许可状态(uid,gid)和各种“通信状态”(打开文件,信号处理程序等)。传统上,“线程”和“进程”之间的区别主要是线程具有CPU状态(可能还有一些其他最小状态),而所有其他上下文都来自进程。但是,这只是 划分COE总体状态的一种方法,没有什么可以说是正确的方法。把自己限制在那种形象上只是愚蠢的。
Linux的想着这个问题的方法(和我想要的东西的工作方式)是有是没有这样的事,作为一个“过程”或“线程”。只有COE的全部(在Linux中称为“任务”)。不同的COE可以彼此共享其上下文的一部分,并且该共享的一个子集是传统的“线程” /“进程”设置,但实际上应该仅将其视为子集(这是一个重要的子集,但重要性就在于此)不是来自设计,而是来自标准:我们显然也想在Linux上运行符合标准的线程程序。
简而言之:不要围绕线程/进程的思维方式进行设计。应该围绕COE的思维方式设计内核,然后pthreads 库可以将受限的pthreads接口导出给想要使用该方式查看COE的用户。
就像当您认为COE而不是线程/进程时可能发生的情况的一个示例:
- 您可以执行一个外部“ cd”程序,这在UNIX和/或进程/线程中是传统上不可能的(愚蠢的示例,但是您的想法是您可以拥有不限于传统UNIX的这类“模块” / threads设置)。做一个:
克隆(CLONE_VM | CLONE_FS);
子代:execve(“ external-cd”);
/ *“ execve()”将取消与VM的关联,因此我们使用CLONE_VM的唯一原因是使克隆动作更快* /
- 您可以自然地执行“ vfork()”(它仅需最小限度的内核支持,但该支持完全适合CUA的思维方式):
克隆(CLONE_VM);
子:继续运行,最终execve()
母亲:等待执行
- 您可以进行外部“ IO重传”:
克隆(CLONE_FILES);
子级:打开文件描述符等
妈妈:用fd的孩子打开vv。
以上所有工作都是因为您不受线程/进程思维方式的束缚。例如,以Web服务器为例,其中CGI脚本作为“执行线程”完成。您无法使用传统线程来做到这一点,因为传统线程总是必须共享整个地址空间,因此您必须链接您想在Web服务器本身中做的所有事情(“线程”无法运行另一个可执行文件)。
作为一个问题的“执行的情况下”这一思想,而不是,你的任务现在可以选择执行外部程序(=分离从父地址空间)等,如果他们想,也可以例如共享一切与家长除了为文件描述符(以便子“线程”可以打开很多文件而无需父级担心它们:当子“线程”退出时它们会自动关闭,并且不会用完父级中的fd) 。
例如,考虑一个线程化的“ inetd”。您需要低开销的fork + exec,因此可以使用Linux方式,而不是使用“ fork()”,而是编写多线程inetd,其中每个线程仅使用CLONE_VM创建(共享地址空间,但不共享文件)描述符等)。然后,如果孩子是外部服务(例如,rlogind),或者它是内部inetd服务(例如,echo,timeofday)之一,则可以执行该命令,然后退出。
您无法使用“线程” /“进程”来执行此操作。
莱纳斯
尝试从Linux内核的OS视图中回答
当程序启动到内存中时,它成为一个进程。进程具有其自己的地址空间,这意味着在内存中具有各种段,例如.text
用于存储已编译代码的段,.bss
用于存储未初始化的静态或全局变量等的段。
每个进程将具有自己的程序计数器和用户空间堆栈。
在内核内部,每个进程将具有自己的内核堆栈(出于安全性问题与用户空间堆栈分开)和一个命名为的结构,该结构task_struct
通常抽象为进程控制块,存储有关进程的所有信息,例如优先级,状态,(以及很多其他大块)。
一个进程可以有多个执行线程。
进入线程后,它们驻留在进程内部,并与创建线程时可以传递的其他资源(例如文件系统资源,共享挂起的信号,共享数据(变量和指令))一起共享父进程的地址空间。因此可以更快地进行上下文切换。
在内核内部,每个线程都有自己的内核堆栈以及task_struct
定义线程的结构。因此,内核将同一进程的线程视为不同的实体,并且它们本身可以进行调度。同一进程中的线程共享一个称为线程组id(tgid
)的通用ID ,也有一个称为进程ID(pid
)的唯一ID 。
试图回答与Java世界有关的问题。
进程是程序的执行,但是线程是进程内的单个执行序列。一个进程可以包含多个线程。线程有时称为轻量级进程。
例如:
示例1:JVM在单个进程中运行,并且JVM中的线程共享属于该进程的堆。这就是为什么多个线程可以访问同一对象的原因。线程共享堆并拥有自己的堆栈空间。这样一来,一个线程对方法及其局部变量的调用就可以使线程免受其他线程的影响。但是堆不是线程安全的,因此必须同步以确保线程安全。
示例2:程序可能无法通过读取按键来绘制图片。该程序必须充分注意键盘输入,并且一次不能处理多个事件的能力将导致麻烦。解决此问题的理想方法是同时无缝执行一个程序的两个或多个部分。线程使我们能够做到这一点。这里的“绘制图片”是一个过程,而读取按键是“子”过程(线程)。
进程是应用程序的执行实例,线程是进程内的执行路径。另外,一个进程可以包含多个线程。重要的是要注意一个线程可以执行一个进程可以做的任何事情。但是由于一个进程可以包含多个线程,所以一个线程可以被认为是“轻量级”进程。因此,线程和进程之间的本质区别是每个线程用于完成的工作。线程用于小型任务,而进程用于更多的“重量级”任务-基本上是应用程序的执行。
线程与进程之间的另一个区别是,同一进程内的线程共享相同的地址空间,而不同的进程则不共享。这允许线程读取和写入相同的数据结构和变量,并且还促进线程之间的通信。进程之间的通信(也称为IPC或进程间通信)非常困难且占用大量资源。
这是线程和进程之间差异的摘要:
线程比进程更容易创建,因为它们不需要单独的地址空间。
多线程需要仔细编程,因为线程共享数据结构,一次只能由一个线程修改。与线程不同,进程不会共享相同的地址空间。
线程被认为是轻量级的,因为它们使用的资源远少于进程。
流程彼此独立。由于线程共享相同的地址空间,因此它们是相互依赖的,因此必须谨慎行事,以防止不同的线程彼此踩踏。
这实际上是上面陈述#2的另一种方式。
一个进程可以包含多个线程。
以下是我从The Code Project的一篇文章中得到的内容。我想它可以清楚地说明所有需要的内容。
线程是将工作负载拆分为单独的执行流的另一种机制。线程比进程轻。这意味着,与完整的过程相比,它提供的灵活性较差,但是由于操作系统的设置较少,因此启动速度更快。当一个程序包含两个或多个线程时,所有线程共享一个内存空间。为进程分配了单独的地址空间。所有线程共享一个堆。但是每个线程都有自己的堆栈。
处理:
流程基本上是一个正在执行的程序。它是一个活动实体。一些操作系统使用术语“任务”来指代正在执行的程序。进程始终存储在主存储器中,也称为主存储器或随机存取存储器。因此,过程被称为活动实体。如果重新启动计算机,它将消失。多个进程可能与同一程序相关联。在多处理器系统上,可以并行执行多个进程。在单处理器系统上,尽管没有实现真正的并行性,但应用了一种进程调度算法,并且该处理器被调度为一次执行一个进程,从而产生了并发的错觉。示例:执行“计算器”程序的多个实例。每个实例都称为一个过程。
线:
线程是进程的子集。它被称为“轻量级进程”,因为它类似于真实进程,但在进程的上下文中执行并共享内核分配给进程的相同资源。通常,一个进程只有一个控制线程–一次执行一组机器指令。进程也可以由同时执行指令的多个执行线程组成。多控制线程可以利用多处理器系统上可能的真正并行性。在单处理器系统上,应用线程调度算法,并安排处理器一次运行每个线程。进程中运行的所有线程共享相同的地址空间,文件描述符,堆栈和其他与进程相关的属性。由于进程的线程共享相同的内存,
ref- https://practice.geeksforgeeks.org/problems/difference-between-process-and-thread
从面试官的角度来看,除了流程等显而易见的事情可能具有多个线程之外,我基本上只想听三件事:
如果您想要更多,Scott Langham的回应几乎涵盖了所有方面。所有这些都是从操作系统的角度来看的。不同的语言可以实现不同的概念,例如任务,轻型线程等等,但是它们仅仅是使用线程(Windows上的线程)的方式。没有硬件和软件线程。有硬件和软件异常与中断,或者有用户模式和内核线程。
对于那些更愿意通过可视化学习的人,这是我创建的一个方便使用的图表,用于解释进程和线程。
我使用了MSDN中的信息- 关于进程和线程
来自嵌入式世界,我想补充一点,进程的概念仅存在于具有MMU(内存管理单元)的“大型”处理器(台式机CPU,ARM Cortex A-9)和支持使用MMU的操作系统(例如Linux)。对于小型/老式处理器和微控制器以及小型RTOS操作系统(实时操作系统)(例如freeRTOS),不支持MMU,因此不支持进程,而仅支持线程。
线程可以访问彼此的内存,并且它们是由操作系统以交错方式调度的,因此它们看起来可以并行运行(或者使用多核,它们实际上可以并行运行)。
另一方面,进程位于由MMU提供和保护的虚拟内存专用沙箱中。这很方便,因为它可以:
there,作为一名正在学习OS课程的本科生,我已经仔细阅读了几乎所有答案,但是我无法完全理解这两个概念。我的意思是大多数人都从某些OS书籍中读到了差异,即线程能够访问事务单元中的全局变量,因为它们利用了进程的地址空间。但是,新的问题提出了为什么要有进程,众所周知,我们已经知道线程相对于进程而言是更轻量级的。让我们通过使用从先前答案之一中摘录的图像来浏览以下示例,
我们有3个线程同时处理一个单词文档,例如Libre Office。第一个通过下划线强调单词是否拼写来进行拼写检查。第二个接收并打印键盘上的字母。最后一个确实在每一次短时间内都保存了文档,以防万一出了问题而丢失了工作文档。在这种情况下,这3个线程不能是3个进程,因为它们共享一个公共内存,该内存是其进程的地址空间,因此都可以访问正在编辑的文档。因此,道路是“文件”一词,还有两个推土机,它们是线程,尽管图像中缺少其中之一。
在用Python(解释语言)构建包含多线程的算法时,我很惊讶地发现与我之前构建的顺序算法相比,执行时间并没有改善。为了理解产生此结果的原因,我做了一些阅读,并相信我所学的内容提供了一个有趣的上下文,从中可以更好地理解多线程与多进程之间的区别。
多核系统可能会行使多个执行线程,因此Python应该支持多线程。但是Python不是编译语言,而是解释语言1。这意味着必须对程序进行解释才能运行,并且解释程序在开始执行之前并不了解该程序。但是,它确实知道Python的规则,然后它动态地应用这些规则。那么,Python中的优化必须主要是解释器本身的优化,而不是要运行的代码。这与诸如C ++之类的编译语言相反,并且对Python中的多线程具有影响。具体来说,Python使用全局解释器锁来管理多线程。
另一方面,编译语言是编译好的。对该程序进行“整体”处理,首先根据其句法定义对其进行解释,然后将其映射到与语言无关的中间表示形式,最后链接到可执行代码中。此过程可以高度优化代码,因为在编译时都可用。在创建可执行文件时可以定义各种程序交互和关系,并且可以做出关于优化的可靠决策。
在现代环境中,Python的解释器必须允许多线程,而且这必须既安全又高效。这就是解释语言与编译语言之间的差异进入画面的地方。解释器必须不打扰来自不同线程的内部共享数据,同时优化处理器在计算中的使用。
如前几篇文章所述,进程和线程都是独立的顺序执行,主要区别在于,进程的多个线程之间共享内存,而进程隔离其内存空间。
在Python中,全局解释器锁保护数据免受不同线程的同时访问。它要求在任何Python程序中任何时候都只能执行一个线程。另一方面,可以运行多个进程,因为每个进程的内存与任何其他进程都是隔离的,并且进程可以在多个内核上运行。
1 Donald Knuth在“计算机编程艺术:基础算法”中很好地解释了解释性例程。
到目前为止,我找到的最佳答案是Michael Kerrisk的“ Linux编程接口”:
在现代UNIX实现中,每个进程可以具有多个执行线程。设想线程的一种方法是共享相同的虚拟内存以及一系列其他属性的一组进程。每个线程执行相同的程序代码,并共享相同的数据区和堆。但是,每个线程都有自己的堆栈,其中包含局部变量和函数调用链接信息。[LPI 2.12]
这本书非常清晰;朱莉娅·埃文斯在清理如何Linux组真的在工作中提到它的帮助这篇文章。
它们几乎相同...但是关键的区别在于,在上下文切换,工作负载等方面,线程是轻量级的,而进程是重量级的。