关于进程、线程总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_37964071/article/details/81838544

进程

进程是程序的活动实体,是资源分配和调度的基本单位。
例如,用户运行自己的程序,系统就创建一个进程,并为它分配资源,包括各种表格、内存空间、磁盘空间、I/O设备等。然后,把该进程放人进程的就绪队列。进程调度程序选中它,为它分配CPU以及其它有关资源,该进程才真正运行。

进程的状态转换

运行中的进程有可能有三种状态,就绪态,运行态和阻塞态

  1. 当进程处于就绪态时,说明此进程已具备除处理器外的所有资源,等待处理器调度,只要分配了处理器资源即可执行。也就从就绪态变为了运行态。就绪进程可以按多个优先级来划分队列。
    例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。

  2. 运行态:进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。正在执行的进程,如果因为分配给它的时间片已经用完而被暂停执行时,该进程便由执行状态又回到就绪状态;如果因为发生某事件而使进程的执行受阻(如进程请求访问临界资源,而该资源正在被其它进程访问),使之无法继续执行,该进程将有执行状态转变为阻塞状态。

  3. 处于阻塞状态的进程,在获得了资源后,转变为就绪状态。

进程间通信

进程间通信就是在不同进程之间传播或交换信息,进程间通信主要包括管道,系统IPC(Inter-Process Communication,进程间通信)(包括消息队列,信号,共享存储),套接字(SOCKET)。
1、管道
管道分为有名管道和无名管道。

  1. 无名管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系一般指的是父子关系。比如,当一个进程创建了一个管道,并调用fork创建自己的一个子进程后,父进程关闭读管道端,子进程关闭写管道端,这样提供了两个进程之间数据流动的一种方式。
  2. 有名管道也是一种半双工的通信方式,但是它允许无亲缘关系进程间的通信。

比较
无名管道的优点:简单方便;
缺点:
(1)局限于单向通信
(2)只能创建在它的进程以及其有亲缘关系的进程之间;
(3)缓冲区有限;
有名管道的优点:可以实现任意关系的进程间的通信;
缺点:
(1)长期存于系统中,使用不当容易出错;
(2)缓冲区有限
详细请看进程间通信-管道
2、信号量
信号量本质上是一个计数器,可以用来控制多个线程对共享资源的访问。
它不是用于交换大批数据,而用于多线程之间的同步。它常作为一种锁机制,防止某进程在访问资源时其它进程也访问该资源。
因此,主要作为进程间以及同一个进程内不同线程之间的同步手段。

优点:可以同步进程;缺点:信号量有限
具体请看信号量
3、信号
信号,是Linux中向进程发送的消息,接收到该信号的进程会相应地采取一些行动,通过软中断的方式来响应这个信号,触发一些事先指定或特定的事件。进程之间可以互相通过系统调用kill来发送信号,内核也可以因为内部事件而给进程发送信号,通知进程发生了某件事件。
对信号的处理动作共有三种:
(1)忽略。
(2)执行默认动作。
(3)提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉一个信号。
具体请看文章信号
4、消息队列
消息队列是消息的链表,存放在内核中并由消息队列标识符标识。

  1. 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法.
    每个数据块都被认为是一种类型,接受者进程接收的数据块可以有不同的类型值.
  2. 我们可以通过发送消息来避免命名管道的同步与阻塞问题。消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的。且消息队列的读取不一定是先入先出。

缺陷是:每个消息的最大长度是有限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统的消息队列的总数也有一个上限(MSGMNI),信息的复制需要额外消耗CPU的时间,不适宜于信息量大或操作频繁的场合。

优点:可以实现任意进程间的通信,并通过系统调用函数来实现消息发送和接收之间的同步,无需考虑同步问题,方便;
具体请看消息队列
5、共享内存

  1. 共享内存是存在于内核级别的一种资源,在shell中可以使用ipcs命令来查看当前系统IPC中的状态,在文件系统/proc目录下有对其描述的相应文件。共享内存相比其他几种方式有着更方便的数据控制能力,数据在读写过程中会更透明。

  2. 共享内存区是最快的IPC形式。⼀旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

  3. 它是针对其它进程间通信方式运行效率低而专门设计的.它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步与通信.

优点:无须复制,快捷,信息量大;
缺点:
(1)共享内存没有任何的同步与互斥机制,所以要使用信号量来实现对共享内存的存取的同步。
(2)利用内存缓冲区直接交换信息,内存的实体存在于计算机中,只能同一个计算机系统中的诸多进程共享,不方便网络通信
具体请看共享内存
6、套接字
套接字,是支持网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。

套接字是由IP和端口号组成的。
优点:
(1)传输数据为字节级,传输数据可自定义,数据量小效率高;
(2)传输数据时间短,性能高;
(3) 适合于客户端和服务器端之间信息实时交互;
(4) 可以加密,数据安全性强
缺点:
需对传输的数据进行解析,转化成应用级的数据。

具体请看网络编程套接字

线程

线程是操作系统调度的最小单位,亦即执行处理机调度的基本单位。

如果把进程理解为在逻辑上操作系统所完成的任务,那么线程表示完成该任务的许多可能的子任务之一。

线程可以在处理器上独立调度执行,这样,在多处理器环境下就允许几个线程各自在单独处理器上进行。操作系统提供线程就是为了方便而有效地实现这种并发性。

线程的同步与互斥

多线程共享一个进程的地址空间,虽然线程间通信容易进行,但是多线程同时访问共享对象时需要引入同步和互斥机制。

同步指的是多个任务按照约定的顺序相互配合完成一件事情。
互斥,目的是用来保证共享资源数据操作的完整性。
互斥锁主要用来保护临界资源,每个临界资源都由一个互斥锁来保护,任何时刻最多只能有一个线程能访问该资源。线程必须先获得互斥锁才能访问临界资源,访问完临界资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止。

那么?多线程中的锁有哪些?
锁机制,包括互斥锁、条件变量、信号量,读写锁,自旋锁。

(1)互斥锁:提供了以排他方式防止数据结构被并发修改的方法。
(2)读写锁:允许多个线程同时读共享数据,而对写操作是互斥的。
(3)条件变量:可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
(4)信号量
(5)自旋锁:自旋锁与互斥锁比较类似,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁。

怎么确定是否应该被锁,如何判断是否有线程安全问题?
(1)看有没有共享数据
(2)看对共享数据的操作是不是多条语句
(3)看是不是在多线程程序中

进程和线程的区别

  1. 进程是程序运行的活动实体,操作系统会为一个进程分配TCP(进程控制块),虚拟地址空间,页表等一些资源。 线程是轻量级进程,是程序运行的某一片段,是进程内部的一部分,操作系统只会给它分配一点够程序运行的资源,所有线程共享进程的地址空间。
  2. 资源分配与调度:进程是资源分配的最小单位,线程是系统调度的最小单位。
  3. 资源占有角度:操作系统会为一个进程分配一堆资源,所有的进程之间是独立的,是互不影响的。而所有线程共享进程的虚拟地址空间,只拥有一小部分资源,比如自己的线程ID,私有栈空间,自己的上下文数据等。并且因为线程共享资源,所以一个线程的异常退出会影响整个进程异常退出,并且要注意线程间的同步与互斥问题。
  4. 调度与切换:因为进程携带一大堆的资源,所以创建和撤销一个进程的代价要比创建和撤销一个线程大的多。且进行进程之间的切换或调度时,操作系统要做的工作比线程之间的切换或调度多得多。
  5. 通信:对于进程间通信,最主要就是让两个不相干的进程之间看到同一份资源,其通信要比线程难得多,两个线程之间的通信是很容易进行的。
  6. 并行:不仅进程间可以并发执行,线程之间也可以并发执行。但是由于进程的创建、撤消和切换,系统的开销比较大,所以创建的进程数目不能太多,而线程的划分尺度比进程小,所以并发性比进程高,效率和吞吐量都比较高。线程在执行程序的过程中,每个线程有自己的程序入口,函数栈帧,函数出口等。
  7. 创建线程与进程:在Linux下,创建一个子进程可以用fork和vfork函数,在子进程运行完毕后,必须由父进程对子进程进行回收(wait,waitpid,发送信号),否则子进程会成为僵尸进程,会造成资源泄漏。创建一个新线程可以用pthread_create函数,在新线程运行完成后,也必须回收,否则会造成资源泄漏。如果不想回收,则可以利用detach函数对线程进行分离。

线程的优缺点:
优点:
(1)线程是轻量级进程,携带的资源很少,创建一个线程的代价较小。
(2)与进程间的切换相比,线程间的切换操作系统要做的工作较小。
(3)线程占用的资源很少。
(4)线程可以充分利用多处理器数量,并发性高。
(5)在等待慢速IO的同时,可以做计算型任务。
(6)对于计算密集型任务和IO密集型任务,线程的效率都比较高。
缺点:
(1)性能缺失
假设在计算密集型任务中,创建了很多的线程,但是线程的数量比处理器的数量多,那么会造成多余线程在等待,造成性能缺失。
(2)安全性降低
容易引发线程安全等问题,需要使用多线程的同步与互斥机制。
(3)编程难度提高
编写与调试一个多线程程序比单线程困难的多。

什么时候用多线程?什么时候用多进程?

  1. 需要频繁创建销毁的,优先用线程
    这种原则最常见的应用就是Web服务器了,来一个连接建立一个线程,断了就销毁线程,要是用进程,创建和销毁的代价是很难承受的
  2. 需要进行大量计算的优先使用线程
    所谓大量计算,当然就是要耗费很多CPU,切换频繁了,这种情况下线程是最合适的。
    这种原则最常见的是图像处理、算法处理。
  3. 强相关的处理用线程,弱相关的处理用进程
    什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。
    一般的Server需要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。
    当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。
  4. 可能要扩展到多机分布的用进程,多核分布的用线程
  5. 都满足需求的情况下,用你最熟悉、最拿手的方式。
    至于“数据共享、同步”、“编程、调试”、“可靠性”这几个维度的所谓的“复杂、简单”应该怎么取舍,我只能说:没有明确的选择方法。但我可以告诉你一个选择原则:如果多进程和多线程都能够满足要求,那么选择你最熟悉、最拿手的那个。

多线程有几个线程合适?

对于多线程来说启用多少个线程这个问题与程序是什么样的程序有密切的关系。

  1. 如果是I/O密集型的话,程序的效率跟I/O阻塞有关,如果I/O阻塞比较少,那么效率就是最高的。
  2. 如果是算术密集型的话,程序的线程数和CPU的核数有关,线程数目应与CPU核数密切相关。因为如果CPU数目和线程数目一样的话,这样CPU会尽可能的调度相应的线程,CPU的cache就不会浪费,对于算术密集型经常访问内存,所以尽可能的与CPU核数相贴切的话可以最大利用cache这样效率是最高的。

猜你喜欢

转载自blog.csdn.net/baidu_37964071/article/details/81838544
今日推荐