通过mmap实现进程间的通信需要先了解和理解存储映射I/O(Memory-mapped I/O),它是使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据,就相当于读文件中的相应字节。与此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可以在不使用read和write函数的情况下,使用地址(指针)完成I/O操作。就相当于指针直接指向文件中的字符串,直接对这个字符串更改等于更改文件。又因为不同进程是打开的同一个文件,所以映射区域也是相同的(因为是通过文件创建映射区域),所以不同进程的指针指向是相同的映射区域。
文章目录
- mmap函数
- 互斥锁如何在进程中使用
- 总结
一、mmap函数
当使用父子(有亲缘关系)进程去使用时不需要使用到fd,因为直接将需要访问的资源定义为全局即可,所以将fd参数设置为-1。
这是匿名mmap,只适用父子进程:我们以电影卖票机器为例,不同机器代表不同进程。
#include <stdio.h>
#include <sys/mman.h>
#include <pthread.h>
#include <unistd.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
struct mt
{
int num;
};
int main(int argc, const char *argv[])
{
pid_t pid;
struct mt *mm;
mm = mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);
//注意参数!!!!!!!!!
if(mm == MAP_FAILED)
{
perror("FAIL mmap");
return -1;
}
mm->num = 0;
pid = fork();
if(pid < 0)
{
return -1;
}
else if(pid == 0)//子,负责售票
{
while(mm->num < 10)
{
mm->num = mm->num+1;
printf("第一台机器卖出一张,现在卖出个数为%d\n",mm->num);
sleep(1);
}
exit(1);
}
else
{
while(mm->num < 10)
{
sleep(1);
mm->num = mm->num+1;
printf("第二台卖出一张,当前卖出个数为%d\n",mm->num);
}
exit(1);
}
wait(NULL);
return 0;
}
这样就实现了父子进程共享一个num。
以下为有名mmap,可以应用于无亲缘关系的进程:注意参数:fd这里就需要使用了,因为是不同程序访问相同文件。
#include <stdio.h>
#include <sys/mman.h>
#include <pthread.h>
#include <unistd.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int i = 0;
char *p = NULL;
int fd = open("mmap_test.txt",O_CREAT|O_RDWR,0777);
if(fd < 0)
{
perror("FAIL open");
return -1;
}
lseek(fd,200,SEEK_END);
write(fd,"\0",1);//将文件大小变为201,为了计算映射空间大小
int len = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(p == MAP_FAILED)
{
perror("FAIL mmap");
return -1;
}
while(i++ < 100)
{
strcpy(p,"hello");
printf("这是写,当前大小为%s\n",p);
sleep(1);
}
close(fd);
free(p);
return 0;
}
这是个写文件,是个进程,再写一个读文件当做另一个进程(读文件只printf不更改值),开始时读文件为空白,但是在写文件运行后,读文件也开始输出hello,说明指针确实都指向了同一个区域,并且更改成功了。
在上述匿名mmap例子时,可能会发现第一台机器在卖时,第二台也在卖,假如我们不允许同时卖,这样的话就可以通过互斥锁将将两个进程在访问num时锁起来,但是互斥锁一般用于线程,但是这里用于进程,怎么办呢?看我如何实现它。
二、互斥锁如何在进程中使用
互斥锁的属性是可以更改的
初始化互斥锁
pthread_mutex_init()
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
功能: 初始化一把锁
参数:
@mutex 要初始化的锁
@attr NULL (锁的属性) (NULL默认属性的锁)
返回值:
成功 返回0
失败 返回错误码
<2>初始化互斥锁
pthread_mutex_init()
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
功能: 初始化一把锁
参数:
@mutex 要初始化的锁
@attr NULL (锁的属性) (NULL默认属性的锁)
返回值:
成功 返回0
失败 返回错误码
pthread_mutex默认是线程间通信加的锁,要是想用在进程之间,需要pthread_mutexattr_t *restrict attr 填充这个字段,在属性字段当中修改为PTHREAD_PROCESS_SHARED标识进程间共享。也是定义一个互斥锁的属性,更改属性,将这个属性赋予互斥锁,那么这个锁就可以进行进程间的通信了。最后记得销毁互斥锁及其属性。
pthread_mutexattr_init(&mm->mutexattr);
// 初始化 mutex 属性
pthread_mutexattr_setpshared(&mm->mutexattr, PTHREAD_PROCESS_SHARED);
// 修改属性为进程间共享
pthread_mutex_init(&mm->mutex,&mm->mutexattr);
// 初始化一把 mutex 锁
struct mt { int num; pthread_mutex_t mutex; pthread_mutexattr_t mutexattr; }; int main(int argc, const char *argv[]) { pid_t pid; struct mt *mm; mm = mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0); if(mm == MAP_FAILED) { perror("FAIL mmap"); return -1; } mm->num = 0; pthread_mutexattr_init(&mm->mutexattr);//初始化mutex属性 pthread_mutexattr_setpshared(&mm->mutexattr,PTHREAD_PROCESS_SHARED); pthread_mutex_init(&mm->mutex,&mm->mutexattr);//初始化一把锁 pid = fork(); if(pid < 0) { return -1; } else if(pid == 0)//子,负责售票 { while(mm->num < 10) { pthread_mutex_lock(&mm->mutex); mm->num = mm->num+1; printf("儿子卖出一张,现在卖出个数为%d\n",mm->num); pthread_mutex_unlock(&mm->mutex); sleep(1); } exit(1); } else { while(mm->num < 10) { sleep(1); pthread_mutex_lock(&mm->mutex); mm->num = mm->num+1; printf("父亲卖出一张,当前卖出个数为%d\n",mm->num); pthread_mutex_unlock(&mm->mutex); } exit(1); } wait(NULL); pthread_mutexattr_destroy(&mm->mutexattr);//销毁锁的属性对象 pthread_mutex_destroy(&mm->mutex);//销毁锁 return 0; }
总结
匿名可以使用互斥锁,那么有名同样可以使用,因为锁的是文件资源。