目录
IPC
Inter-Process Communication(进程间通信)
管道
共享内存
System V 共享内存
shmget 创建或打开一个共享内存段
POSIX 共享内存
内存映射
mmap()系统调用在调用进程的虚拟地址空间中创建一个新内存映射。映射分为两种
1.文件映射:将一个文件的一部分直接映射到内存种,通过对内存的操作来访问文件。
2.匿名映射:没有对应的文件,这种映射的分页会被初始化为0。
1.mmap() 创建一个映射
#include<sys/mman.h>
void* mmap(void* addr, size_t length,int prot,int flag,int fd,off_t offset);
addr: 指定映射被放置的虚拟地址。如果设为NULL,则由内核自动选取。
length: 指定映射的字节数(虽然length无需是系统分页大小的倍数,但内核会以分页大小为单位来创建映射)。
prot: 位掩码,指定了映射上的保护信息。取值要么是PROT_NONE,要么是下表中其他三个标记的组合。
值 | 描述 |
---|---|
PROT_NONE | 区域无法访问 |
PROT_READ | 区域内容可读 |
PROT_WRITE | 区域内容可写 |
PROT_EXEC | 区域内容可执行 |
flag: 控制映射操作的位掩码,取值见下表。
MAP_PRIVATE | 私有映射,内容变更对其他进程不可见 |
---|---|
MAP_SHARED | 共享映射,内容对其他进程可见 |
fd: 标识被映射文件的描述符。
offset: 指定映射在文件中的起点 。
返回值: 成功返回映射其实地址,失败返回MAP_FAILED。
2.munmap() 接触映射区域
#include <sys/mman.h>
int munmap(void* addr,size_t length)
addr: 映射的起始地址。
length: 映射区大小。
返回值: 成功返回0失败返回-1.
3.msync() 同步映射区域
#include <sys/mman.h>
int msync(void* addr,size_t length,int flags)
addr: 映射的起始地址。
length: 映射区大小。
flags: 参数可取下列中值中的一个。
MS_SYNC | 同步文件写入;会阻塞,直到被修改过的内容被写入完毕 |
---|---|
MS_ASYNC | 异步文件写入;内存区域中被修改的分页会在后面某个时刻被写入磁盘病立即对在相应文件区域中执行read的其他进程可见 |
另外,MS_SYNC操作执行后,内存与磁盘同步,而MS_ASYNC执行后,内存与高速缓冲同步。
返回值: 成功返回0失败返回-1.
一些高级或不常用系统调用待补充。案例见POSIX信号量
消息队列
System V 消息队列
POSIX 消息队列
信号量
System V 信号量
POSIX 信号量
SUSv3规定了两种信号量:
1.命令信号量:通过使用相同名字调用sem_open(),不相关的进程能够访问同一个信号量。
2.未命名信号量: 位于内存种预先商定的位置处,当在进程/线程间共享时,信号量必须位于一个共享内存区域中。
sem_open()创建或打开一个新的或既有信号量
创建的信号量位于/etc/shm/处。
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
sem_t* sem_open(const char* name,int oflag,.../*mode_t mode,unsigned int value */)
name: 唯一标识信号量。
oflag: 位掩码,确定了是打开已有信号量还是创建新的信号量。如果为0,那么访问一个既有信号量。如果指定了O_CREAT,并且给定的name对应的信号量不存在,就创建一个信号量。如果同时指定O_CREAT|O_EXCL,并且给定name对应的信号量已经存在,那么sem_open失败。如果打开既有信号量只需要两个参数,如果创建新的信号量就需要后面两个参数。
mode: 位掩码,指定施加于信号量上的权限。
value: 指定了信号量初始值。
返回值: 成功返回信号量指针,失败返回SEM_FAILED。
sem_close() 关闭一个信号量
关闭一个信号量,并不会删除一个信号量,要删除信号量需要调用sem_unlink
#include <semaphore.h>
int sem_close(sem_t* sem)
返回值: 成功返回0,失败返回-1。
sem_unlink() 删除一个命名信号量
#include <semphore.h>
int sem_unlink(const char* name)
name: 唯一标识信号量。
返回值: 成功返回0,失败返回-1。
sem_wait() 等待一个信号量
#include <semaphore.h>
int sem_wait(sem_t* sem) //阻塞版本
int sem_trywait(sem_t* sem) //非阻塞版本
int sem_timewait(sem_t* sem,const struct timespec* abs_timeout) //超时返回版本
返回值: 成功返回0,失败返回-1。
sem_post() 发布一个信号量
#include <semaphore.h>
int sem_post(sem_t* sem)
返回值: 成功返回0,失败返回-1。
sem_getvalue() 获取当前信号量的值
#include <semaphore.h>
int sem_getvalue(sem_t* sem,int* sval)
sval 传出的信号量的值。
返回值: 成功返回0,失败返回-1。
sem_init() 初始化一个未命名信号量
#include <semaphore.h>
int sem_init(sem_t* sem,int pshared,unsigned int value)
pshared: 表明这个信号量是在线程间共享还是在进程间共享,如果pshared等于0,表示在单个进程的线程间共享,如果pshared不等于0,表示在进程间共享。
返回值: 成功返回0,失败返回-1。
sem_destroy() 销毁一个未命名信号量
#include <semaphore.h>
int sem_destroy(sem_t* sem)
返回值: 成功返回0,失败返回-1。
案例
多生产者和多消费者模型{也可以拆分成生产-消费者模型来看}。
1.生产者1将product1放入缓冲区后,消费者1才能取走product1
2.生产者2将product2放入缓冲区后,消费者2才能取走product2
3.只有缓冲区为空时,生产者1或生产者2才能讲product放入缓冲区
4.对缓冲区的访问要互斥的进行
unlink.c 删除创建的信号量
#include <semaphore.h>
#include <stdio.h>
int main(int argc,char** argv)
{
int i = 1;
int ret = 0;
for(; i < argc; ++i)
{
ret = sem_unlink(argv[i]);
if(ret < 0)
printf:("%s unlink error!\n",argv[i]);
}
return 0;
}
producer1.c 生产者1
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
int main()
{
int fd = open("mem.txt",O_RDWR);
//创建共享内存映射区
char* mem = mmap(NULL,9,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(mem == MAP_FAILED)
{
perror("mmap error");
return -1;
}
//创建信号量
//互斥访问缓冲区信号量
sem_t* semMutex = sem_open("SEMMUTEX",O_CREAT,0777,1);
//缓冲区有几个product1
sem_t* semProduct1 = sem_open("SEMPRODUCT1",O_CREAT,0777,0);
//缓冲区可以放多少个product
sem_t* semBuf = sem_open("SEMBUF",O_CREAT,0777,1);
while(1)
{
sem_wait(semBuf);
sem_wait(semMutex);
strcpy(mem,"product1");
printf("生产者1生产了product1\n");
sem_post(semMutex);
sem_post(semProduct1);
sleep(3);
}
//释放mmap
munmap(mem,9);
close(fd);
return 0;
}
producer2.c 生产者2
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
int main()
{
int fd = open("mem.txt",O_RDWR);
//创建共享内存映射区
char* mem = mmap(NULL,9,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(mem == MAP_FAILED)
{
perror("mmap error");
return -1;
}
//创建信号量
//互斥访问缓冲区信号量
sem_t* semMutex = sem_open("SEMMUTEX",O_CREAT,0777,1);
//缓冲区有几个product2
sem_t* semProduct2 = sem_open("SEMPRODUCT2",O_CREAT,0777,0);
//缓冲区可以放多少个product
sem_t* semBuf = sem_open("SEMBUF",O_CREAT,0777,1);
while(1)
{
sem_wait(semBuf);
sem_wait(semMutex);
strcpy(mem,"product2");
printf("生产者2生产了product2\n");
sem_post(semMutex);
sem_post(semProduct2);
sleep(2);
}
//释放mmap
munmap(mem,9);
close(fd);
return 0;
}
consumer1.c 消费者1
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
int main()
{
int fd = open("mem.txt",O_RDWR);
//创建共享内存映射区
char* mem = mmap(NULL,9,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(mem == MAP_FAILED)
{
perror("mmap error");
return -1;
}
//打开信号量
//互斥访问缓冲区信号量
sem_t* semMutex = sem_open("SEMMUTEX",O_CREAT,0777,1);
sem_t* semProduct1 = sem_open("SEMPRODUCT1",O_CREAT,0777,0);
sem_t* semBuf = sem_open("SEMBUF",O_CREAT,0777,1);
while(1)
{
sem_wait(semProduct1);
sem_wait(semMutex);
printf("消费者1取出了%s\n",mem);
strcpy(mem,"");
sem_post(semMutex);
sem_post(semBuf);
}
//释放mmap
munmap(mem,9);
close(fd);
return 0;
}
consumer2.c 消费者2
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
int main()
{
int fd = open("mem.txt",O_RDWR);
//创建共享内存映射区
char* mem = mmap(NULL,9,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(mem == MAP_FAILED)
{
perror("mmap error");
return -1;
}
//打开信号量
//互斥访问缓冲区信号量
sem_t* semMutex = sem_open("SEMMUTEX",O_CREAT,0777,1);
sem_t* semProduct2 = sem_open("SEMPRODUCT2",O_CREAT,0777,0);
sem_t* semBuf = sem_open("SEMBUF",O_CREAT,0777,1);
while(1)
{
sem_wait(semProduct2);
sem_wait(semMutex);
printf("消费者2取出了%s\n",mem);
strcpy(mem,"");
sem_post(semMutex);
sem_post(semBuf);
}
//释放mmap
munmap(mem,9);
close(fd);
return 0;
}
先执行删除信号量代码,再执行生产消费者代码
./unlink SEMMUTEX SEMBUF SEMPRODUCT2 SEMPRODUCT1