信号量————来描述可使用资源的个数
信号量(Semaphore)是一种用于控制多个进程或线程对共享资源访问的同步机制。在C语言中,通常我们会使用POSIX线程(pthread)库来实现信号量的操作
信号量有两个主要操作:P(等待)和V(释放)
初始化信号量
静态初始化
sem_t sem;
sem = SEM_INITIALIZER(1); // 初始值为1
动态初始化
sem_t sem;
sem_init(&sem, 0, 1); // 第二个参数为0表示信号量是进程内的,初始值为1
操作信号量
等待(P操作):
sem_wait
函数将阻塞调用线程,直到信号量的值大于0。调用成功后,信号量的值减1。
sem_wait(&sem);
信号(V操作):
sem_post
函数将信号量的值增加1,并唤醒正在等待该信号量的一个线程(如果有的话)。
sem_post(&sem);
尝试等待(非阻塞):
sem_trywait
函数尝试对信号量执行P操作,但如果信号量的值为0,则不会阻塞调用线程,而是立即返回一个错误。
if (sem_trywait(&sem) == 0)
{
// 成功获取信号量
}
else
{
// 信号量值为0,处理错误
}
销毁信号量
sem_destroy(&sem);
sem_t sem_w;
sem_t sem_r;char buf[100] = {0};
void* doSth1(void* arg)
{while(1){sem_wait(&sem_w);printf(">");fgets(buf,sizeof(buf),stdin);sem_post(&sem_r);}
}void* doSth2(void* arg)
{while(1){sem_wait(&sem_r);printf("buf = %s",buf);sem_post(&sem_w);}
}int main(int argc, char *argv[])
{sem_init(&sem_w,0,1);sem_init(&sem_r,0,0);pthread_t tid[2];int ret = pthread_create(&tid[0],NULL,doSth1,NULL);if(ret != 0)handle_error_en(ret,"pthread_create fail");ret = pthread_create(&tid[1],NULL,doSth2,NULL);if(ret != 0)handle_error_en(ret,"pthread_create fail");pthread_join(tid[0],NULL);pthread_join(tid[1],NULL);sem_destroy(&sem_w);sem_destroy(&sem_r);return 0;
}
进程间的通信
同一主机:古老的通信方式;
管道:通过操作系统提供的内存缓冲区,在内核中实现的通信机制。
有名管道:有名管道则可以跨越无亲缘关系的进程。
无名管道:无名管道只能用于具有亲缘关系的进程间通信
信号:操作系统通过信号来通知进程系统中发生了某种预先规定好的事件。
IPC对象通信:
消息队列:在进程之间传递数据的通信机制,通过消息队列标识符进行 通信。
共享内存 :允许多个进程访问同一块物理内存区域。最快的IPC形式之一一 因为进程可以直接访问共享内存中的数据,无需进行数据的复 制。但需要开发者自行负责同步和互斥,以防止数据竞争和一 致性问题。
信号量级:一个计数器,用于控制多个进程对共享资源的访问。
不同主机:socket通信:网络通信
无名管道
pipe() 函数
通过pipe()
函数创建了管道,你就可以在进程间通过它来传递数据了。通常,一个进程(如父进程)会写入管道,而另一个进程(如子进程)会从管道中读取数据。
#include <unistd.h>
int pipe(int filedes[2]);
- 参数:
filedes
是一个整型数组,用于存储管道的两个文件描述符。filedes[0]
用于读管道,filedes[1]
用于写管道。 - 返回值:成功时返回0;失败时返回-1,并设置errno以指示错误。
pipefd函数
它创建一个管道,并返回两个文件描述符,一个用于读(pipefd[0]
),另一个用于写(pipefd[1])
管道的读写规则:
1.读端存在,写管道
管道空:可以写数据
管道满:会造成-->写阻塞
2.读端不存在,写管道
系统会给进程发一个信号SIGPIPE(管道破裂)
3.写端存在,读管道
管道空,读不到数据,
这时会造成读操作阻塞
4.写端不存在,读管道
如果管道中有数据,则读取这些数据!
如果没有数据,读操作不阻塞,立即返回!
管道的特点:
-
半双工通信:
管道是半双工的,意味着在同一时间内数据只能沿一个方向流动。如果需要双向通信,必须创建两个管道,一个用于一个方向的通信,另一个用于相反方向的通信。 -
基于文件描述符:
管道是通过文件描述符来访问的。在创建管道时,系统返回两个文件描述符:一个用于读(read),另一个用于写(write)。这些文件描述符在文件系统中没有对应的文件,它们是内核中的特殊文件。 -
数据流动性:
数据一旦被写入管道,就会被读取端立即读取(如果有进程在读取)。一旦数据被读取,它就会从管道中消失,无法再次被读取(除非有数据复制机制)。这意味着管道不支持像文件那样的随机访问。 -
有限容量:
管道有一个有限的容量(65536字节),这取决于系统的实现。如果管道满了,写入操作将会阻塞,直到有足够的空间为止(除非是非阻塞模式或使用了特定的系统调用选项)。类似地,如果管道为空,并且没有进程在写入,读取操作也可能会阻塞。