进程、线程、协程的区别

简介: 进程、线程、协程的区别

进程

进程是操作系统进行资源分配和调度的基本单位。

进程,一个启动的程序, 进程占用的是系统资源,如:物理内存,CPU,终端等,是一个动态的概念。

每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。


线程

线程是操作系统调度的最小单位。

线程又叫轻量级的进程(LWP:light weight process)

  • 进程:拥有独立的地址空间,拥有PCB,相当于独居。
  • 线程:有PCB,但没有独立的地址空间,多个线程共享进程空间,相当于合租。


上述说线程有PCB的原因:有资料说

Linux内核线程实现原理

类Unix系统中,早期是没有“线程”概念的,80年代才引入,借助进程机制实现出了线程的概念。因此在这类系统中,进程和线程关系密切。

轻量级进程(light-weight process),也有PCB,创建线程使用的底层函数和进程一样,都是clone

从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表是相同的

进程可以蜕变成线程

线程可看做寄存器和栈的集合

在linux下,线程最是小的执行单位;进程是最小的分配资源单位

察看LWP号:ps –Lf pid 查看指定线程的lwp号。

三级映射:进程PCB --> 页目录(可看成数组,首地址位于PCB中) --> 页表 --> 物理页面 --> 内存单元

参考:《Linux内核源代码情景分析》 ----毛德操

对于进程来说,相同的地址(同一个虚拟地址)在不同的进程中,反复使用而不冲突。原因是他们虽虚拟址一样,但,页目录、页表、物理页面各不相同。相同的虚拟址,映射到不同的物理页面内存单元,最终访问不同的物理页面。

但!线程不同!两个线程具有各自独立的PCB,但共享同一个页目录,也就共享同一个页表和物理页面。所以两个PCB共享一个地址空间。

实际上,无论是创建进程的fork,还是创建线程的pthread_create,底层实现都是调用同一个内核函数clone。

如果复制对方的地址空间,那么就产出一个“进程”;如果共享对方的地址空间,就产生一个“线程”。

因此:Linux内核是不区分进程和线程的。只在用户层面上进行区分。所以,线程所有操作函数 pthread_* 是库函数,而非系统调用。

也有别的资料说,线程有PCB,不是单独的一个进程,我更偏向第一种说明,这里第二种资料就不展开了。

线程共享资源:

  • 文件描述符表
  • 每种信号的处理方式sigaction
  • 当前工作目录
  • 用户ID和组ID
  • 内存地址空间 (.text/.data/.bss/heap/共享库)

线程非共享资源:

  • 线程id
  • 处理器现场和栈指针(内核栈)
  • 独立的栈空间(用户空间栈)
  • errno变量
  • 信号屏蔽字
  • 调度优先级

多线程中的信号处理

协程

协程的思想很早就被提出来了,最初是为了解决编译器实现中的问题,后来相继出现了很多种实现方式,例如windows中的纤程,再例如lua中coroutine,由于go原生支持协程,所以以下介绍皆是围绕go协程展开。


线程是进程中的执行体,拥有一个执行入口,以及从进程虚拟地址空间中分配的栈(用户栈和内核栈),操作系统会记录线程控制信息,而线程获得CPU时间片以后才可以执行,CPU这里栈指针,指令指针,栈基等寄存器都要切换到对应的线程。如果线程自己又创建了几个执行体(协程),给它们各自指定执行入口,申请一些内存分给它们用作执行栈,那么线程就可以按需调度这几个执行体了。为了实现这些执行体的切换,线程也需要记录它们的控制信息。包括ID,执行栈的位置,执行入口地址,执行现场等等。线程可以选择一个执行体来执行,此时CPU中指令指针就会指向这个执行体的执行入口,栈基和栈指针寄存器也会指向线程给它分配的执行栈。要切换执行体时,需要先保存当前执行体的执行现场,然后切换到另一个执行体。通过同样的方式,可以恢复到之前的执行体, 这样就可以从上次中断的地方继续执行。这些由线程创建的执行体就是所谓的“携程”。因为用户程序不能操作内核空间,所以只能给协程分配用户栈,而操作系统对协程一无所知,所以协程又被称为“用户态线程”。

上图是GMP模型,M是内核线程,对内核而言,它执行什么,全权由P调度器进行调度,G是协程,操作系统对用户态协程一无所知。协程整体对内核保持一个线程的形态,而局部对用户态来说,分成了多个协程。

深入理解GMP模型

协程和IO多路复用

目录
相关文章
|
17天前
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
1月前
|
消息中间件 安全 Linux
线程同步与IPC:单进程多线程环境下的选择与权衡
线程同步与IPC:单进程多线程环境下的选择与权衡
58 0
|
1天前
|
安全 Java
【JAVA】线程的run()和start()有什么区别?
【JAVA】线程的run()和start()有什么区别?
|
4天前
|
Java 数据库连接 数据处理
Python从入门到精通:3.1.2多线程与多进程编程
Python从入门到精通:3.1.2多线程与多进程编程
|
11天前
|
调度 Python
Python多线程、多进程与协程面试题解析
【4月更文挑战第14天】Python并发编程涉及多线程、多进程和协程。面试中,对这些概念的理解和应用是评估候选人的重要标准。本文介绍了它们的基础知识、常见问题和应对策略。多线程在同一进程中并发执行,多进程通过进程间通信实现并发,协程则使用`asyncio`进行轻量级线程控制。面试常遇到的问题包括并发并行混淆、GIL影响多线程性能、进程间通信不当和协程异步IO理解不清。要掌握并发模型,需明确其适用场景,理解GIL、进程间通信和协程调度机制。
28 0
|
26天前
|
安全 Linux API
Android进程与线程
Android进程与线程
18 0
|
1月前
|
消息中间件 Linux 调度
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
70 0
|
4天前
|
NoSQL Linux 程序员
【linux进程信号(一)】信号的概念以及产生信号的方式
【linux进程信号(一)】信号的概念以及产生信号的方式
|
4天前
|
Java Shell Linux
【linux进程控制(三)】进程程序替换--如何自己实现一个bash解释器?
【linux进程控制(三)】进程程序替换--如何自己实现一个bash解释器?
|
4天前
|
算法 Linux Shell
【linux进程(二)】如何创建子进程?--fork函数深度剖析
【linux进程(二)】如何创建子进程?--fork函数深度剖析

热门文章

最新文章

相关实验场景

更多