zoukankan      html  css  js  c++  java
  • 共享内存跨进程通信

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

    创建共享内存有两种方式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 
    ", 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

  • 相关阅读:
    Lucene的分页查询
    Lucene索引库的简单优化
    Lucene的排序搜索
    Axure RP 6.5学习记录(1)界面介绍
    Lucene的高亮器Highlighter
    关于Lucene以及索引和搜索的流程
    Lucene初体验
    linux 下 ansi_x3.41968 java 邮件附件乱码
    SQL Server DBA工作内容详解
    SQL Server 导出 insert into 脚本
  • 原文地址:https://www.cnblogs.com/linyx/p/10057626.html
Copyright © 2011-2022 走看看