共享内存跨进程通信

通过共享内存通信是最快的,不过既然是共享资源,那么就必须要有同步机制。

创建共享内存有两种方式shm和mmap的方式。

  1. mmap是在磁盘上建立一个文件,每个进程地址空间中开辟出一块空间进行映射。
  2. 而对于shm而言,shm每个进程最终会映射到同一块物理内存。shm保存在物理内存,这样读写的速度要比磁盘要快,但是存储量不是特别大。
  3. 相对于shm来说,mmap更加简单,调用更加方便,所以这也是大家都喜欢用的原因。
  4. 另外mmap有一个好处是当机器重启,因为mmap把文件保存在磁盘上,这个文件还保存了操作系统同步的映像,所以mmap不会丢失,但是shmget就会丢失。

shm的创建要确保原子性的话,可以通过重命名来做。

https://segmentfault.com/a/1190000000630435

 1 char* SharedMemory::CreateMapping(const std::string file_name, unsigned mapping_size, bool &is_new) {
 2     char* mapping = (char*)MAP_FAILED;
 3     int fd = -1;
 4     fd = open(file_name.c_str(),  O_RDWR | O_CREAT | O_EXCL, 0666); // 同步O_EXCL
 5     if (fd == -1) {
 6         fd = open(file_name.c_str(), O_RDWR, 0666);
 7         if (fd < 0) {
 8             return mapping;
 9         }
10     }
11 
12     struct stat file_stat;
13     if(fstat(fd, &file_stat)== -1)  {
14         close(fd);
15         return mapping;
16     }
17     int file_size = file_stat.st_size;
18     is_new = false;
19     if (file_size == 0) {
20         file_size = mapping_size;
21         ftruncate(fd, file_size);
22         is_new = true;
23     }
24     mapping = (char*)mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
25     if (is_new) {
26         memset(mapping, 0, sizeof(char) * file_size);         
27     }
28     close(fd);
29     return mapping;
30 }

这里用O_CREAT | O_EXCL来确保只创建一次文件,如果创建失败就以rw的方式来打开。

互斥量同步

跨进程的同步机制,根据APUE 15.9节提到的,可以有三种方式,带undo的信号量、记录锁、互斥量。pthread带的跨进程互斥量需要高版本支持。

 1 bool SharedMemory::Init() {
 2     bool is_new = false;
 3     mutex_ = (pthread_mutex_t *)CreateMapping(file_name_ + ".lock", sizeof(pthread_mutex_t), is_new);
 4     if (mutex_ == MAP_FAILED) {
 5         return false;
 6     }
 7     if (is_new) {
 8         InitLock();    
 9     }
10     is_init_ = true;
11     return true;
12 }
13 
14 void SharedMemory::InitLock() {
15     pthread_mutexattr_t attr;
16     pthread_mutexattr_init(&attr); //~necessary, or weird EINVAL error occurs when operating on the mutex
17     pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
18     pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
19     pthread_mutex_init(mutex_, &attr);
20 }
21 
22 void SharedMemory::Lock() {
23     if (!is_init_) {
24         return;
25     }
26     while (EOWNERDEAD == pthread_mutex_lock(mutex_)) {
27         pthread_mutex_consistent(mutex_);
28         pthread_mutex_unlock(mutex_);
29     }
30 }
31 
32 void SharedMemory::Unlock() {
33     if (!is_init_) {
34         return;
35     }
36     pthread_mutex_unlock(mutex_);
37 }

pthread_mutex_consistent这个函数有版本限制。

如果持有 mutex 的线程退出,另外一个线程在 pthread_mutex_lock 的时候会返回 EOWNERDEAD。这时候你需要调用 pthread_mutex_consistent 函数来清除这种状态,否则后果自负。

https://segmentfault.com/a/1190000000630435

pthread_mutexattr_setpshared配合PTHREAD_PROCESS_SHARED可以创建跨进程的mutex,但是必需保证mutex所在的内存区域可以被每个进程访问,也就是说必需被创建在进程间共享的内存区域中,比如mmap创建的共享内存。

https://segmentfault.com/q/1010000000628904

记录锁

记录锁的功能:当一个进程正在读或修改文件的某个部分是,它可以阻止其他进程修改同一文件区。

记录锁是更常用的方式。因为它没有版本限制,进程退出时会自动释放锁。

 1 void SharedMemory::InitLock(short type) {
 2     if (lock_fd_ < 0) {
 3         return;
 4     }
 5     struct flock lock;
 6     lock.l_type = type;
 7     lock.l_whence = SEEK_SET;
 8     lock.l_start = 0;
 9     lock.l_len = 0;
10     int ret = fcntl(lock_fd_, F_SETLKW, &lock);
11     //printf("InitLock %d \n", ret);
12 }
13 
14 void SharedMemory::LockWrite() {
15     if (!is_init_) {
16         return;
17     }
18 
19     InitLock(F_WRLCK);
20 }
21 
22 void SharedMemory::LockRead() {
23     if (!is_init_) {
24         return;
25     }
26 
27     InitLock(F_RDLCK);
28 }
29 
30 void SharedMemory::Unlock() {
31     if (!is_init_) {
32         return;
33     }
34     InitLock(F_UNLCK);
35 }
  1. F_SETLK:获取(l_type为F_RDLCK或F_WRLCK)或释放由lock指向flock结构所描述的锁,如果无法获取锁时,该函数会立即返回一个EACCESS或EAGAIN错误,而不会阻塞。
  2. F_SETLKW:F_SETLKW和F_SETLK的区别是,无法设置锁的时候,调用线程会阻塞到该锁能够授权位置。
  3. F_GETLK:F_GETLK主要用来检测是否有某个已存在锁会妨碍将新锁授予调用进程,如果没有这样的锁,lock所指向的flock结构的l_type成员就会被置成F_UNLCK,否则已存在的锁的信息将会写入lock所指向的flock结构中

https://blog.csdn.net/anonymalias/article/details/9197641

posted @ 2018-12-03 11:49  linyx  阅读(1430)  评论(0编辑  收藏  举报