1.进程间通信机制
常用的六种通信机制: 管道、消息队列、共享内存、信号灯集、信号、Socket
管道(Pipe)和无名管道(匿名管道):
管道是一种半双工的通信方式,数据只能单向流动,通常用于具有亲缘关系的进程间通信(例如,父子进程)。
无名管道是在内存中开辟的一段缓冲区,一端进程写入数据,另一端进程读取数据。
无名管道的特点:
1.只能用于亲缘间进程的通信
2.无名管道数据半双工的通信的方式
3.无名管道的大小是64K
4.无名管道不能够使用lseek函数(调用会出错 返回 -1)
5.读写的特点
如果读端存在写管道:有多少写多少,直到写满为止(64k)写阻塞,直到管道中腾出新的4K空间,写操作解除阻塞
如果读端不存写管道,管道破裂(SIGPIPE)
如果写端存在读管道:有多少读多少,没有数据的时候阻塞等待
如果写端不存在读管道:有多少读多少,没有数据的时候立即返回(非阻塞)
命名管道(FIFO):
可以用于非亲缘进程间通信,也可以用于亲缘进程间通信有名管道会创建一个管道文件,只需要打开这个文件,进行读写操作即可管道文件本质是在内存上的,在硬盘上的只是一个标识。
有名管道的特点:
1.可以用于任意进程间的通信,不仅限亲缘进程
2.有名管道数据是半双工的通信方式
3.有名管道的大小是64K
4.有名管道不能够使用lseek函数(调用会失败 返回 -1)
5.读写的特点
如果读端存在写管道:有多少写多少,直到写满为止(64k)写阻塞
如果读端不存在写管道
1.读端没有打开,写端在open的位置阻塞
2.读端打开后关闭,管道破裂(SIGPIPE)
如果写端存在读管道:有多少读多少,没有数据的时候阻塞等待
如果写端不存在读管道
1.写端没有打开,读端在open的位置阻塞
2.写端打开后关闭,有多少读多少,没有数据的时候立即返回
信号(Signal):
信号是一种进程间的异步通信方式,用于通知接收进程发生了某种事件,如进程终止、用户输入等。信号处理通常是预定义的。
消息队列(Message Queue):
消息队列也是基于内核实现的,A进程将消息写入消息队列
消息队列中的消息有类型和正文。
B进程可以根据消息的类型从消息队列中将对应类型的消息取走。
消息队列的大小,默认是 16K,
如果消息队列满了,A进程还想向消息队列中写入消息,此时A进程将会阻塞。
共享内存(Shared Memory):
最直接的通信方式之一,允许两个或多个进程直接访问同一块内存区域。速度快,但需要考虑同步问题,如使用互斥锁或信号量。
信号灯集:
信号灯集:又叫做信号量数组,他是实现进程间同步的机制
在一个信号灯集中可以有很多个信号灯,这些信号灯之间工作相互互不干扰。
一般使用时使用的都是二值信号灯
套接字(Socket):
虽然最初用于网络通信,但也可以用于同一台机器上的进程间通信,支持TCP(面向连接、可靠)和UDP(无连接、不可靠)两种通信方式。
2.线程的同步互斥机制:
互斥锁(Mutex):
互斥锁是最基本的同步原则,用于保护临界区(Critical Section),确保同一时间只有一个线程可以访问共享资源。其他试图获取锁的线程会被阻塞,直到持有锁的线程释放锁。
信号量(Semaphore):
信号量可以视为一个计数器,用于控制对公共资源的访问数量。它允许一个或多个线程等待特定条件满足(计数非零)。线程通过wait(P操作)减少计数,通过post(V操作)增加计数。当计数为零时,wait操作会使线程等待。
条件变量(Condition Variable):
条件变量用于线程间的同步,允许线程等待某个条件成立。线程在条件满足前调用wait()函数挂起自身,直到其他线程通过notify()或notify_all()函数通知条件已变成就绪。
读写锁(Read-Write Lock):
读写锁允许多个读线程同时访问共享资源,但在有写线程时会排斥所有读写线程。适用于读多写少的场景,能提高并发效率。
自旋锁(Spin Lock):
自旋锁在获取锁失败时,并不立即放弃CPU,而是在原地循环(自旋)等待锁释放。适用于锁持有时间很短的情况,避免了线程上下文切换的开销。
原子操作(Atomic Operation):
原子操作是不可分割的操作,保证了操作的完整性。在多线程环境下,对变量进行原子操作可以避免数据竞争,如原子加、减、交换等。