mmap 进程间通讯

本文介绍mmap机制及其在进程间通信中的应用,并通过producer和consumer实例展示如何利用mmap实现不同进程间的数据共享。此外,还探讨了在多线程环境下如何正确使用互斥锁确保数据的一致性和安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mmap

mmap可以把一个文件映射到进程内存里面,之后操作这块内存就相当于操作文件,文件存在于文件系统里面,不同的进程都能读取这个文件,所以mmap就可以当作IPC来使用。

代码

这里写了一个producer和consumer来模拟mmap在不同的进程之间共享数据。

msg.h

#ifndef MSG_H
#define MSG_H

#define MMAP_FILE "test_mmap"
#define MSG_ARRAY_SIZE 4096

#include <pthread.h>
#include <errno.h>
#include <stdio.h>
struct msg {
  double c;
  int a;
  char b;
};

struct mmap_struct {
  pthread_mutex_t mutex;
  struct msg msg_arr[MSG_ARRAY_SIZE];
  int msg_count;
};

int init_mutex(pthread_mutex_t* mutex) {
  pthread_mutexattr_t mutex_attr;
  pthread_mutexattr_init(&mutex_attr);
  pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
  pthread_mutexattr_setrobust(&mutex_attr, PTHREAD_MUTEX_ROBUST);
  int error = pthread_mutex_init(mutex, &mutex_attr);
  if (error == EBUSY) {
    printf("Mutex do not need to reinit.\n");
  }
  return 0;
}

#endif //MSG_H

producer.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>

#include "msg.h"

int main(int argc, char** argv) {
  int fd = open(MMAP_FILE, O_RDWR, 0777);
  if (fd < 0) {
    perror("open error");
    return -1;
  }
  struct stat sb;
  fstat(fd, &sb);
  printf("%ld.\n", sb.st_size);

  struct mmap_struct* ms = (struct mmap_struct*) mmap(NULL, sizeof(struct mmap_struct), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  if (ms == MAP_FAILED) {
    perror("mmap error");
    return -1;
  }
  init_mutex(&ms->mutex);
  memset(ms->msg_arr, 0, sizeof(struct msg) * MSG_ARRAY_SIZE);
  ms->msg_count = 0;

  int i = 0;
  double j = 1.00;
  while (1) {
    int lock_error = pthread_mutex_lock(&ms->mutex);
    if (lock_error != 0) {
      if (lock_error == EOWNERDEAD) {
        pthread_mutex_consistent(&ms->mutex);
      } else if (lock_error == ENOTRECOVERABLE) {
        pthread_mutex_destroy(&ms->mutex);
        init_mutex(&ms->mutex);
      }
    }
    struct msg cur_msg;
    memset(&cur_msg, 0, sizeof(struct msg));
    cur_msg.a = ++i; 
    cur_msg.b = 'a';
    cur_msg.c = (j = j + 0.01);
    memmove((void*)&ms->msg_arr[ms->msg_count], &cur_msg, sizeof(struct msg));
    if (ms->msg_count++ >= MSG_ARRAY_SIZE) {
        ms->msg_count = 0;
    }
    printf("Success Produce %d msgs.\n", ms->msg_count);
    pthread_mutex_unlock(&ms->mutex);
    sleep(1);
  }
  return 0;
}

consumer.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>

#include "msg.h"

int clear_last_msg(struct mmap_struct* ms) {
  int msg_len = ms->msg_count;
  memset(&ms->msg_arr[msg_len], 0, sizeof(struct msg));
  return 0;
}

int main(int argc, char** argv) {
  int fd = open(MMAP_FILE, O_RDWR, 0777);
  if (fd < 0) {
    perror("open error");
    return -1;
  }
  
  struct mmap_struct* ms = (struct mmap_struct*) mmap(NULL, sizeof(struct mmap_struct), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  if (ms == MAP_FAILED) {
    perror("mmap error");
    return -1;
  }
  
  int cur_msg_index = 0;
  while (1) {
    int lock_error = pthread_mutex_lock(&ms->mutex);
    if (lock_error != 0) {
      if (lock_error == EOWNERDEAD) {
        pthread_mutex_consistent(&ms->mutex);
        clear_last_msg(ms);
      } else if (lock_error == ENOTRECOVERABLE) {
        pthread_mutex_destroy(&ms->mutex);
        init_mutex(&ms->mutex);
      }
    }
    int msg_len = ms->msg_count;
    printf("Total Msg Len is : %d.\n", msg_len);
    struct msg current_msg;
    memset(&current_msg, 0, sizeof(struct msg));
    if (cur_msg_index < msg_len) {
      memmove(&current_msg, &ms->msg_arr[cur_msg_index++], sizeof(struct msg));
      printf("%f %d %c.\n", current_msg.c, current_msg.a, current_msg.b);
    }
    pthread_mutex_unlock(&ms->mutex);
    sleep(2);
  }
  return 0;
}

注意点

  1. test_mmap file
    这个文件是dd写出来的,具体的命令:

dd if=/dev/zeor of=test_mmap count=256

  1. 在初始化mutex的时候,设置了两个mutex的属性,分别是:
    1. PTHREAD_PROCESS_SHARED
      这个属性主要是为了让锁能在不同的进程之间的线程使用,正好符合这个demo的情况。

    2. PTHREAD_MUTEX_ROBUST
      当持有锁的线程异常退出,但是没有unlock锁的时候,这个属性是很有用的。可以使用pthread_mutexattr_setrobust 来设置这个属性。
      man page里面写有:

      如果设置了这个属性,那么当锁持有者异常退出但是没有释放锁的情况下,另外一个线程再去获取这个锁的话,会返回一个EOWNERDEAD的错误码,这样我们就可以调用pthread_mutex_consistent来强制获取这个锁。
      如果另外的线程没有执行pthread_mutex_consistent但是调用了unlock操作时,下一个尝试获取锁的线程会得到ENOTRECOVERABLE的错误,在这种情况下,唯一的合法的操作就是把这个锁销毁掉。

      所以我们针对消费者或者生产者挂掉,但是没有释放锁的情况,需要做一些处理:

      1. 先要把锁据为己有,再进行操作
      2. 如果锁已经失效,那么重构这个锁。

      在msg.h文件里面有一个初始化锁的函数,在man page里面有写,如果当我们调用pthread_mutex_init 函数去初始化一把已经初始化过的锁会返回EBUSY错误码,这种情况下我们就只用提示一下这把锁已经初始化过即可。

知识点

  1. 关于mmap的使用。
  2. 锁的易主、死锁情况的处理。
  3. dd命令使用。
  4. 如何检查mutex是否被初始化。
  5. 进程间的锁的使用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值