Linux共享内存的三种实现示例

在Linux中,有三种主要的方式可以实现共享内存:使用shmgetmmapPOSIX共享内存。代码仅供示例,实际使用时需要添加错误处理和适当的同步机制来确保共享内存的安全访问。

以下是每种方式的示例代码:

1、使用shmget

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main() {
   int shm_id;
   key_t key;
   int *shm_ptr;

   // 创建共享内存
   key = ftok("shared_memory_example", 'R');
   shm_id = shmget(key, sizeof(int), IPC_CREAT | IPC_EXCL | 0666);

   if (shm_id < 0) {
      perror("shmget");
      return 1;
   }

   // 连接共享内存
   shm_ptr = (int*) shmat(shm_id, NULL, 0);

   if (*shm_ptr == -1) {
      perror("shmat");
      return 1;
   }

   // 使用共享内存
   *shm_ptr = 42;
   printf("共享内存中的值:%d\n", *shm_ptr);

   // 解除连接
   shmdt((void *)shm_ptr);

   // 删除共享内存
   shmctl(shm_id, IPC_RMID, NULL);

   return 0;
}

通过shmget,根据key值去分配一个共享内存会返回一个shm_id用于表示该共享内存,如果调用了shmctl去删除该共享内存,即使后续再去根据想用的key值去获取共享内存,还是由于是新分配了一个共享内存,因此会返回与上次不一样的shm_id。

上面的代码仅展示了简单的获取共享内存的方法,实际使用过程中,按照man shmget中的介绍

       If  shmflg  specifies  both  IPC_CREAT and IPC_EXCL and a shared memory segment already exists for key, then shmget() fails with errno set to EEXIST.
       (This is analogous to the effect of the combination O_CREAT | O_EXCL for open(2).)

我们会这样写:

    if ((shm_id = shmget(key, sizeof(SHM), 0666|IPC_CREAT | IPC_EXCL)) < 0)
    {
        if (EEXIST == errno)//共享内存已经存在
        {
            // 重新调用shmget,但是不加上IPC_CREAT flag,否则是创建新的共享内存了
            shm_id = shmget(key, sizeof(SHM), 0666);
            // 连接共享内存
            shm_ptr = (int*) shmat(shm_id, NULL, 0);
        }
        else//出错
        {
            perror("fail to shmget");
            exit(-1);
        }
    }
    else//成功
    {
        // 连接共享内存
        shm_ptr = (int*) shmat(shm_id, NULL, 0);
    }

shmctl不是删除共享内存的函数,它指示发指令的函数,删除共享内存的操作是通过传入IPC_RMID这样的cmd实现的。

SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
{
	return ksys_shmctl(shmid, cmd, buf, IPC_64);
}

2、使用mmap

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

int main() {
   int fd;
   int *ptr;

   // 创建映射文件
   fd = open("shared_memory_example", O_RDWR | O_CREAT, 0666);
   if (fd == -1) {
      perror("open");
      return 1;
   }

   // 设置文件大小
   lseek(fd, sizeof(int)-1, SEEK_SET);
   write(fd, "", 1);

   // 映射内存
   ptr = (int *)mmap(0, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
   if (ptr == MAP_FAILED) {
      perror("mmap");
      return 1;
   }

   // 使用共享内存
   *ptr = 42;
   printf("共享内存中的值:%d\n", *ptr);

   // 解除映射
   if (munmap(ptr, sizeof(int)) == -1) {
      perror("munmap");
      return 1;
   }

   // 关闭文件,也可以提前,只要映射成功。
   close(fd);

   // 删除映射文件
   if (unlink("shared_memory_example") == -1) {
      perror("unlink");
      return 1;
   }

   return 0;
}

关于mmap系统调用,如下所示:

#define offset_in_page(p) (((unsigned long)p) % PAGE_SIZE)

SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
		unsigned long, prot, unsigned long, flags,
		unsigned long, fd, unsigned long, off)
{
	if (offset_in_page(off) != 0)
		return -EINVAL;

	return ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
}

3、使用POSIX共享内存

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

int main() {
   int shm_fd;
   int *ptr;

   // 创建共享内存对象
   shm_fd = shm_open("/shared_memory_example", O_CREAT | O_RDWR, 0666);
   if (shm_fd == -1) {
      perror("shm_open");
      return 1;
   }

   // 设置共享内存大小
   if (ftruncate(shm_fd, sizeof(int)) == -1) {
      perror("ftruncate");
      return 1;
   }

   // 映射共享内存
   ptr = (int *)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
   if (ptr == MAP_FAILED) {
      perror("mmap");
      return 1;
   }

   // 使用共享内存
   *ptr = 42;
   printf("共享内存中的值:%d\n", *ptr);

   // 解除映射
   if (munmap(ptr, sizeof(int)) == -1) {
      perror("munmap");
      return 1;
   }

   // 关闭共享内存对象
   if (shm_unlink("/shared_memory_example") == -1) {
      perror("shm_unlink");
      return 1;
   }

   return 0;
}

注意,shm_open使用gcc编译时,需要加上-lrt。否则会报error:

posixshm.c:(.text+0x1a):对‘shm_open’未定义的引用
posixshm.c:(.text+0xfe):对‘shm_unlink’未定义的引用
collect2: error: ld returned 1 exit status

相关说明(man shm_open)如下:

NAME
       shm_open, shm_unlink - create/open or unlink POSIX shared memory objects

SYNOPSIS
       #include <sys/mman.h>
       #include <sys/stat.h>        /* For mode constants */
       #include <fcntl.h>           /* For O_* constants */

       int shm_open(const char *name, int oflag, mode_t mode);

       int shm_unlink(const char *name);

       Link with -lrt.

4、请注意线程同步问题

线程同步的方式很多,无外乎信号量、互斥锁、条件变量等手段,一些示例可参考Linux进程间共享内存通信时如何同步?(附源码)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阅后即奋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值