# 1.进程和线程的区别

@[TOC] 选自:一篇让你明白进程与线程之间的区别与联系 (opens new window)

进程,线程与多核,多cpu之间的关系 (opens new window)

结论提前讲:

# 进程和线程的关系:

==单CPU==:一台单核处理器计算机 = 一个车间; ==多CPU==:一台多核处理器计算机 = 一座工厂;

进程:一个车间 = 一个进程; (即一个运行的程序) 多进程:一座工厂可以同时运行多个车间; CPU和进程:单CPU只能同时运行单个进程,多CPU可以同时运行多个进程。

线程:车间内一个工人 = 一个线程; 进程与线程:一个进程可以包括多个线程。

线程间内存共享:车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。 一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。

内存安全:可是,每个车间容纳大小不同,有的最多只能容纳一个人。车间人满的时候,其他人就进不去了。 一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。

互斥锁:一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。 这就叫"互斥锁"–Mutex,防止两个线程同时读写某一块内存区域。

信号量:这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。 这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。

锁和信号量:不难看出,互斥锁是信号量的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。

操作系统的资源分配与调度逻辑

以多进程形式,允许多个任务同时运行; 以多线程形式,允许单个任务分成不同的部分运行; 提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。

(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。 (2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。 (3)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信(TCP/IP)的办法实现同步。 (4)处理机分给线程,即真正在处理机上运行的是线程。 (5)线程是指进程内的一个执行单元,也是进程内的可调度实体。

区别: (1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。从操作系统来说,一个进程面对的是磁盘,因为每个进程拥有自己的虚拟内存。而线程更像是面对CPU去执行。 (2)并发性不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。 (3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。 (4)系统开销:在创建或撤销进程的时候,由于系统都要为之分配和回收资源,导致系统的明显大于创建或撤销线程时的开销。但进程有独立的地址空间,进程崩溃后,在保护模式下不会对其他的进程产生影响,而线程只是一个进程中的不同的执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但是在进程切换时,耗费的资源较大,效率要差些。 (5)从JVM来说,多个线程共享进程的堆,方法区,而线程拥有自己的栈空间和程序计数器。

# 一、什么是进程和线程

进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。

线程(thread)操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务

即:

进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位。

线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单位。

每个运行中的程序就是一个进程,这一点在任务管理器上面可以形象的看到。

当一个程序运行时,内部可能会包含多个顺序执行流,每个顺序执行流就是一个线程。

# 二、深入理解

在这里插入图片描述

# 2.1 进程(线程+内存+文件/网络句柄)

我们通过上面的图片进行进一步理解:

“内存”我们通常所理解的内存是我们所见到的(2G/4G/8G/16G)物理内存,它为什么会在进程之中呢? 实际上,这里的内存是逻辑内存。指的是内存的寻址空间。每个进程的内存是相互独立的。 否则的话会出现一个问题:我们把指针的值改一改就指向其他进程的内存了,通过这样我们岂不是就可以看到其他进程中"微信"或者是"网上银行"的信息, 这样的话,那我们的微信聊天记录或者是银行账户的信息就都被别人找到了,这是一个很危险的信号!显然这样是不可能的。

“文件/网络句柄”它们是所有的进程所共有的,例如打开同一个文件,去抢同一个网络的端口这样的操作是被允许的。

“线程”: 接下来,我们就要介绍一下我们的“线程”有关知识 在这里插入图片描述

# 2.2 线程(栈+PC+TLS)

2.2.1 栈: 我们通常都是说调用堆栈,其实这里的堆是没有含义的,调用堆栈就是调用栈的意思。 那么我们的栈里面有什么呢? 我们从主线程的入口main函数,会不断的进行函数调用, 每次调用的时候,会把所有的参数和返回地址压入到栈中。

2.2.2 PC: Program Counter 程序计数器,操作系统真正运行的是一个个的线程, 而我们的进程只是它的一个容器。PC就是指向当前的指令,而这个指令是放在内存中。 每个线程都有一串自己的指针,去指向自己当前所在内存的指针。 计算机绝大部分是存储程序性的,说的就是我们的数据和程序是存储在同一片内存里的 这个内存中既有我们的数据变量又有我们的程序。所以我们的PC指针就是指向我们的内存的。

  • 2.2.2.1 缓冲区溢出 例如我们经常听到一个漏洞:缓冲区溢出 这是什么意思呢? 例如:

我们有个地方要输入用户名,本来是用来存数据的地方。 然后黑客把数据输入的特别长。这个长度超出了我们给数据存储的内存区,这时候跑到了 我们给程序分配的一部分内存中。黑客就可以通过这种办法将他所要运行的代码写入到用户名框中,来植入进来。

我们的解决方法就是,用用户名的长度来限制不要超过用户名的缓冲区的大小来解决

# 2.3 TLS:

全称:thread local storage 之前我们看到每个进程都有自己独立的内存,这时候我们想,我们的线程有没有一块独立的内存呢?答案是有的,就是TLS。 可以用来存储我们线程所独有的数据。 可以看到:线程才是我们操作系统所真正去运行的,而进程呢,则是像容器一样他把需要的一些东西放在了一起,而把不需要的东西做了一层隔离,进行隔离开来。

# 3. 进程之间的是怎么进行交互的呢?(七种)

  1. 管道pipe(数据只能单向流动,而且只能在具有亲缘关系的进程间使用)
  2. 命名管道FIFO(有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。)
  3. 消息队列MessageQueue(消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。)
  4. 共享存储SharedMemory(共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。)
  5. 信号量Semaphore(它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。)
  6. 套接字Socket(它可用于不同及其间的进程通信。)
  7. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

通过TCP/IP的端口来实现

# 4. 线程之间又是怎样进行交互?

线程的通信就比较简单,有一大块共享的内存,只要大家的指针是同一个就可以看到各自的内存。

# 5.小结:

1.进程要分配一大部分的内存,而线程只需要分配一部分栈就可以了. 2.一个程序至少有一个进程,一个进程至少有一个线程. 3.进程是资源分配的最小单位,线程是程序执行的最小单位。 4.一个线程可以创建和撤销另一个线程同一个进程中的多个线程之间可以并发执行.

# 多个进程在多cpu中是并行运行的,在单cpu中是串行(并发)

# 三、进程之间的调度怎么实现?

# 1. 先来先服务算法

  • 最常见的算法
  • 优点就是简单且实现容易,缺点则是短的工作有可能变得很慢

# 2. 时间片轮转算法

  • 周期性地进行进程切换

# 3. 短任务优先算法

  • 短任务的优先级比长任务的高,而我们总是安排优先级高的任务先运行
  • 短任务优先算法又分为两种类型:一种是非抢占式,一种是抢占式。非抢占式当已经在CPU上运行的任务结束或阻塞时,从候选任务中选择执行时间最短的进程来执行。而抢占式则是每增加一个新的进程就需要对所有进程(包括正在CPU上运行的进程)进行检查,谁的时间短就运行谁

# 4. 优先级调度算法

  • 每个进程赋予一个优先级,每次需要进程切换时,找一个优先级最高的进程进行调度
  • 事实上,短任务优先算法本身就是一种优先级调度,只不过它给予短进程更高的优先级而已。
  • 其缺点则有二:
    • 一是低优先级的进程可能会“饥饿”,
      • 第一个缺点可以通过动态地调节任务的优先级解决,例如一个进程如果等待时间过长,其优先级将因持续提升而超越其他进程的优先级,从而得到CPU时间。
    • 二是响应时间无法保证。
      • 第二个缺点可以通过将一个进程优先级设置为最高来解决,但即使将优先级设置为最高,但如果每个人都将自己的进程优先级设置为最高,其响应时间还是无法保证。

# 5. 混合调度算法

  • 将所有进程分为不同的大类,每个大类为一个优先级。如果两个进程处于不同的大类,则处于高优先级大类的进程优先执行;如果处于同一个大类,则采用时间片轮转算法来执行。
Last Updated: 6/3/2024, 1:08:34 AM