一、信号量
信号量-------让线程间有顺序协调地工作,它可以用来协调多个线程或进程,使得它们不会因为同时访问共享资源而导致数据不一致或系统崩溃。
二、信号量的操作
信号量主要有两个基本操作:
-
等待(P 操作):表示使用这个资源。
- 线程或进程尝试获取信号量的资源。
- 如果信号量的计数器大于 0,则减 1 并继续执行。
- 如果信号量的计数器等于 0,线程或进程将进入等待状态,直到信号量大于 0。
-
释放(V 操作):表示产生这个资源。
- 线程或进程释放信号量的资源。
- 信号量的计数器加 1。
- 如果有等待的线程或进程,则唤醒它们中的一个。
三、信号量的分类
-
有名信号量(Named Semaphore):
- 允许不同进程间通过名称共享信号量。
- 用于进程间同步,可以跨进程访问。
-
无名信号量(Unnamed Semaphore):
- 只能在同一个进程或线程组内使用。
- 不需要名称,在创建时直接初始化。
四、信号量的相关函数
sem_t : 数据类型,用来定义信号量
sem_t sem;
定义了一个数据类型为sem_t 的变量,名为sem;
sem_init
:
- 用于初始化一个无名信号量。
- 原型:
int sem_init(sem_t *sem, int pshared, unsigned int value);
- 参数:
sem
:指向信号量对象的指针。pshared
:为 0 表示信号量用于进程内线程之间的同步;非 0 表示用于进程间同步。value
:信号量的初始值。
sem_wait
:(P操作)
- 使调用线程阻塞,直到信号量值大于 0。
- 原型:
int sem_wait(sem_t *sem);
- 参数:
sem
:指向信号量对象的指针。
sem_post
:(V操作)
- 增加信号量的值(释放信号量)。
- 原型:
int sem_post(sem_t *sem);
- 参数:
sem
:指向信号量对象的指针。
sem_destroy
:
- 用于销毁一个无名信号量。
- 原型:
int sem_destroy(sem_t *sem);
- 参数:
sem
:指向信号量对象的指针。
五、管道
由于每个进程具有独立的内存空间 ,进程通信实际上都是在内核中才能进行数据的交换。
介绍 IPC (进程间通信)中的古老通信方式:管道
管道可以看作是一个数据流的通道,其中一个进程将数据写入管道的写端,另一个进程从管道的读端读取数据。
管道实际上也是在内核中运行的。
管道的类型
管道主要分为以下几种类型:
-
无名管道(Unnamed Pipe)
-
有名管道(Named Pipe)
无名管道的操作方法
1. 创建无名管道
通过 pipe()
系统调用创建无名管道。pipe()
调用会创建一个包含两个文件描述符的数组:
int fd[2];
pipe(fd);
- fd[0]:表示管道的读端(读取数据)。
- fd[1]:表示管道的写端(写入数据)。
- 创建成功返回0,失败返回-1,并设置errno。
2.无名管道的特点
1.单向通信;
无名管道本质上是单向的,即数据只能从管道的一端流向另一端:
- 写端(fd[1]):负责向管道写入数据。
- 读端(fd[0]):负责从管道读取数据。
如果需要双向通信,通常要创建两个无名管道。
2.无名管道的大小:65536byte 64k
管道中的数据量不能超过这个大小,否则写操作会阻塞。
3.管道操作的四种情况
-
写端存在,读端也存在:
- 写操作:当写端和读端都存在时,如果管道为空,写端可以持续写入数据,直到管道缓冲区被写满。
- 读操作:读端在有数据时读取数据,读取后数据会从管道中删除。
-
写端存在,读端不存在:
- 写操作:如果管道的读端被关闭,但写端继续写入数据,此时写操作会导致管道破裂,并触发
SIGPIPE
信号。该信号通常会导致写入的进程终止,除非该信号被捕获或忽略。
- 写操作:如果管道的读端被关闭,但写端继续写入数据,此时写操作会导致管道破裂,并触发
-
写端不存在,读端存在:
- 读操作:当写端被关闭,读端依然可以读取管道中的数据。如果管道中没有数据,读操作会返回 0,表示 EOF(文件结束)。这表明管道的另一端已经关闭,读操作不会阻塞。
-
写端存在,读管道:
- 读操作:当管道中还有数据时,读端可以读取这些数据。如果管道中没有数据且写端未关闭,读操作会阻塞,等待新数据的写入。