进程:
指正在运行的一个程序实例。每个进程都拥有自己独立的虚拟地址空间、系统资源和执行状态,进程之间通常是相互独立且并发执行的。
在操作系统中,进程通常包含以下几个方面的信息:
1、进程标识符(PID):用来唯一标识进程。
2、程序计数器(PC):记录当前进程运行到哪条指令。
3、寄存器集合:保存了当前进程的执行环境。
4、虚拟地址空间:表示进程可用的虚拟内存区域,包含代码段、数据段、堆栈段等。
5、文件描述符表:记录了进程打开的文件描述符和相关信息。
6、进程控制块(PCB):用来存储进程的各种信息,例如进程状态、优先级、父子关系等。
种类:
1、用户进程:由用户启动并管理的进程,通常包括应用程序、服务进程等。
2、系统进程:由操作系统启动并管理的进程,通常包括内核、驱动程序、守护进程等。
3、守护进程(Daemon):是指在后台运行的长期运行的系统进程,通常负责提供服务或者监控系统状态等。
4、批处理进程(Batch Process):是指自动执行一系列特定任务的进程,通常用于批量处理数据、生成报表等。
5、实时进程(Real-time Process):是指对响应时间具有严格要求的进程,通常用于实时控制、信号处理等。
线程:
指进程中的一个执行单元,多个线程可以共享同一个进程的虚拟地址空间和系统资源。线程之间通常是并发执行的,因此可以充分利用多核处理器的性能优势。
在操作系统中,线程通常包含以下几个方面的信息:
1、栈空间:保存了线程的栈帧,用来存储局部变量、函数参数和返回地址等。
2、寄存器集合:记录了线程的执行环境。
3、线程控制块(TCB):用来存储线程的各种信息,例如线程状态、优先级、所属进程等。
种类:
1、用户级线程(User-Level Thread):由应用程序通过线程库实现的线程,通常具有更高 的灵活性和可移植性,但是缺乏操作系统级别的支持,因此需要开发者自己管理线程的调度 和资源分配。
2、内核级线程(Kernel-Level Thread):由操作系统内核实现的线程,通常具有更好的并发性和稳定性,但是开销较大,因此需要操作系统来管理线程的调度和资源分配。
3、轻量级线程(Lightweight Thread):是指由操作系统内核实现的一种轻量级线程机制, 通常与用户级线程配合使用,通过将若干个用户级线程映射到一个或多个内核级线程上来提 高并发性和效率。
4、守护线程(Daemon Thread):是指一种不会阻止进程退出的线程,通常用于后台工作,例如垃圾回收、文件压缩等。
进程线程的区别:
1、资源占用:每个进程拥有独立的虚拟地址空间、系统资源和执行状态,因此进程之间相互独立;而多个线程共享同一个进程的虚拟地址空间和系统资源,因此线程之间需要通过同步机制来保护共享资源的访问。
2、切换代价:由于进程拥有独立的虚拟地址空间和系统资源,因此进程切换的代价通常比线程切换更大。
3、并发性:多个进程之间通常是相互独立且并发执行的,而多个线程则可以通过共享进程的虚拟地址空间和系统资源来实现并发执行。
线程间通信:
1、共享内存:
多个线程共享同一块内存区域,每个线程都可以读写这块内存,并且对于其他线程所作的修改也能立即反映出来;共享内存需要采用锁机制来保护共享资源不被并发访问导致数据不一致问题。
锁:
一种常见的同步机制,用于保护共享资源的访问。当多个进程或线程同时访问同一个共享 资源时,如果没有采取合适的同步措施,就容易出现竞争条件、和数据不一致的问题。
为了避免这种问题,通常需要使用锁来保护共享资源,即在访问共享资源之前获取锁,在完成操作后释放锁。只有获得锁的进程或线程才能够对共享资源进行修改,其他进程或线程则需要等待锁被释放后才能够继续执行。
作用:
1、保证原子性:通过限制对共享资源的访问,确保每次操作都能够顺利完成,避免因为竞争条件导致的数据不一致和内存泄漏等问题。
2、避免死锁:在多个进程或线程之间共享资源时,很容易出现互相等待对方释放锁的情况,这就是死锁。通过正确地使用锁可以避免死锁的发生。
3、提高并发度:通过合理地使用锁,可以实现更高的并发度,从而提高程序的性能和 响应速度。
种类:
1、互斥锁:
互斥锁是一种最基本的锁机制,它可以保护共享资源不被并发访问导致数据不一致问 题,在任意时刻只能有一个线程持有,其他线程必须等待该线程释放锁后才能获得锁并且对共享资源进行操作
2、自旋锁
自旋锁是一种轻量级的锁机制,不会引起线程的上下文切换和调度延迟,因此适用于 短时间内持有锁的场景。自旋锁在尝试获取锁时,如果发现锁已经被占用,则会一直忙等待(自旋)直到锁被释放
3、读写锁
读写锁是一种特殊的锁机制,允许多个线程同时读取共享资源,但只允许一个线程写入共享资源,当写入操作正在进行时,所有的读取操作都必须等待;而当读取操作正在进行时,其他读取操作也可以同时进行
4、条件变量
条件变量是一种高级的同步机制,允许一个线程暂停等待另一个线程满足某些条件后再继续执行。条件变量通常与互斥锁一起使用,在条件满足时等待线程会被唤醒,并且该线程会重新尝试获取互斥锁
2、信号量:
信号量是一种用于控制多进程或多线程互斥访问共享资源的同步机制,分为二元信号量和计数器信号量两种类型,其中二元信号量只有0和1两种状态,而计数器信号量则可以有多个取值。信号量通过等待(wait)和释放(signal)操作实现线程的同步和互斥。
二元信号量:
一种最基本的同步机制,它只有两个状态:可以获取和已经被占用。在二元信号量中,初始时通常被设置为可获取状态。
特点:
1、只能由一个进程或线程持有。
2、如果信号量已经被占用,则必须等待其释放后才能继续执行。
3、可以用于实现互斥锁、生产者消费者问题等场景。
计数信号量:
可以允许多个线程或进程同时访问共享资源的同步机制,维护一个计数器,表示当前可供使用的资源数量,初始值可以为任意非负整数。
当一个线程或进程请求获取资源时,如果计数器的值大于0,则该线程或进程可以立即获得资源并将计数器减1;如果计数器的值等于0,则该线程或进程需要阻塞等待其他线程或进程释放资源后才能获得资源。
3、互斥体:
互斥体是一种用于创建互斥锁的同步机制,通过提供一个锁来保护共享资源不被并发访问,只有获得该锁的线程才能够对共享资源进行操作。互斥体可以通过调用CreateMutex()函数创建,并通过WaitForSingleObject()和ReleaseMutex()函数来等待和释放互斥锁。
4、条件变量:
线程间通信和同步,允许一个线程暂停等待另一个线程满足某些条件后再继续执行,通常与互斥体一起使用,当条件满足时,等待线程会被唤醒。条件变量可以通过调用InitializeConditionVariable()函数初始化,并通过SleepConditionVariableCS()和WakeConditionVariable()等函数来实现线程间的等待和唤醒操作。
5、消息队列:
可以使多个线程之间实现异步通信,在消息队列中,发送方将消息放入队列中,然后接收方从队列中取出消息并处理。消息队列适用于不需要立即响应和同步处理的场合。
进程间通信:
1、管道:
一种单向通信机制,分为匿名管道和命名管道两种类型;匿名管道只能用于父子进程或兄弟进程之间通信,而命名管道可以用于任意进程之间通信。
匿名管道:
在Unix/Linux系统中用于进程间通信的机制,可以实现父子进程之间或者兄弟进程之间的通信
特点:
1、匿名:匿名管道没有名字,只能用于同一进程内的亲缘关系进程间通信。
2、单向性:管道是单向的,数据只能从一个方向流入或流出管道。
3、半双工:管道的数据传输方式是半双工的,即数据只能在一个方向上传输。
命名管道:
在Unix/Linux系统中用于进程间通信的机制,不同于匿名管道,命名管道有一个文件名,并且可以允许不同进程之间的通信。
特点:
1、有名字:命名管道有一个文件名,可以在不同进程间共享。
2、双向性:管道可以同时实现双向数据传输。
3、持久化:命名管道会一直存在,直到被显式删除或者系统关闭。
2、信号量
3、共享内存
4、消息队列
5、文件映射:
可以将一个文件映射到进程的地址空间中,并允许多个进程同时访问该文件。在某些情况下,文件映射比共享内存更具优势,因为它可以将大型文件分成多个部分,只映射需要访问的部分,从而节省内存空间。
6、套接字:
套接字可以通过TCP/IP协议等实现进程间通信,不仅允许在同一主机上的进程之间进行通信,还可以实现不同主机上的进程之间通信
同步/异步:
同步是指一个任务的完成必须依赖于其他相关任务的状态,只有在相关任务完成后才能继续执行。
异步是指一个任务的完成不需要依赖于其他相关任务的状态,可以在任何时刻独立执行。
多进程:
同步和异步通常用于描述父子进程之间的数据传输方式
同步:在父进程中调用wait()函数等待子进程结束并获取子进程的返回值,这种方式称为同步。
异步:在父进程中使用信号来通知子进程进行某些操作,子进程完成后返回结果给父进程,这种方式称为异步。
多线程:
同步和异步通常用于描述线程之间的协作方式和任务处理方式
同步:在线程之间共享某个资源时,需要使用同步机制(如锁、条件变量等)来保证数据的正确性和一致性。
异步:通常指采用回调函数或者消息队列等方式实现任务的分离和模块的解耦。
线程间同步:
1、互斥锁
2、条件变量
3、信号量
4、屏障:同步多个线程的执行,使得它们在某个点上汇合并相互等待,然后再一起继续执行。
5、原子变量:一种线程安全的数据类型,可以通过原子操作来进行读取和修改,避免因为竞争条件导致的数据不一致问题。
6、同步队列:支持线程之间的同步和互斥。通常采用生产者-消费者模型,生产者将元素放入队列中,消费者从队列中取出元素进行处理。
7、读写锁
并发/并行:
并发是指两个或多个任务在同一时间段内同时执行,这些任务可能在不同的处理器上运行,也可能在同一个处理器上通过交替执行来实现并发。
并行是指两个或多个任务在同一时间点上同时执行,通常需要多个处理器或者多核处理器的支持。
多进程:
由于每个进程都拥有独立的虚拟地址空间和系统资源,因此进程之间通常是并发执行的。
多线程:
由于多个线程可以共享同一个进程的虚拟地址空间和系统资源,因此线程之间通常是并发执行的。
死锁:
定义:
在多进程或多线程并发执行时,由于各个进程或线程之间互相占有对方所需要的资源而导致的一种僵局状态。当系统中的进程或线程都无法继续执行时,就会陷入死锁状态。
产生的必要条件:
死锁发生时必须满足四个必要条件:
1、互斥条件:
一个资源每次只能被一个进程或线程使用。
2、循环等待条件:
一个进程或线程因请求资源而阻塞时,对已获得的资源保持不放。
3、请求和保持条件
进程或线程已经获得的资源,在未使用完之前,不能被其他进程或线程强行剥夺。
4、不可剥夺条件:
若干进程或线程之间形成一种头尾相接的循环等待资源关系。
解决方法:
预防死锁:通过破坏死锁产生的必要条件来预防死锁的发生。例如,可以避免循环等待、动态分配资源、按序申请资源等。
避免死锁:通过系统提前分析进程或线程的资源需求,然后根据需要进行资源调度以预防死锁的发生。例如,可以使用银行家算法、资源分配图法等。
检测死锁:当系统无法预防或避免死锁时,可以采用检测死锁的方法及时发现并解除死锁。例如,可以使用资源分配图法、死锁定理等。
解除死锁:当系统检测到死锁时,可以通过打破死锁、撤销进程或线程、资源抢占等方式来解除死锁。例如,可以使用资源剥夺法、进程终止法等。
活锁:
定义:
系统中的多个进程或线程因为某种原因而陷入一种循环等待的状态,导致它们无法继续执行下去,但是又不会被阻塞或者死锁。
通常发生在多个进程或线程之间通过竞争共享资源而产生的冲突上。例如,在并发编程中,如果多个线程同时对同一个变量进行读写操作,就可能出现活锁的情况。此时,每个线程都能够顺利地获取锁并进行操作,但是由于其他线程也在竞争同一个锁,因此它们可能会因为“让步”等操作而相互影响,从而导致所有的线程都无法顺利完成操作,最终陷入一种循环等待的状态。
解决方法:
1、调整算法或模型:通过重新设计算法或调整数据模型来避免竞争条件的发生,例如采用乐观锁、悲观锁、CAS等机制。
2、减少并发度:通过限制同时访问某些资源的并发度来避免竞争条件的发生,例如使用锁或信号量来控制资源的访问。
3、引入超时机制:通过引入超时机制来避免进程或线程长时间等待而陷入活锁的状态,例如设置最大等待时间或者定期检查等待的条件是否满足。
4、回退重试:如果出现了活锁的情况,可以尝试回退到之前的状态并重新执行操作,例如使用回溯或者重试机制来避免活锁的发生。