21xrx.com
2024-12-23 02:32:19 Monday
登录
文章检索 我的文章 写文章
使用C++实现共享内存和信号量。
2023-06-28 15:10:18 深夜i     --     --
C++ 共享内存 信号量

共享内存和信号量是计算机程序中常用的同步机制,它们能够使多个进程或线程之间实现互斥与同步,从而协调它们之间的运行。C++语言提供了丰富的系统调用、函数和库来实现共享内存和信号量,下面来介绍其中的一些。

一、共享内存的实现

共享内存是一种特殊的内存空间,它可以被多个进程同时访问和修改,因此它被广泛应用于进程间通信和数据共享的情景中。在C++中,实现共享内存的方式主要有以下几种:

1. mmap()函数

mmap()函数是linux系统提供的一个用于共享内存映射的系统调用,它可以将一个文件或者匿名内存映射到进程的虚拟地址空间中,进而实现共享内存。具体的使用方法见下面的代码片段:


#include <sys/mman.h>

#include <fcntl.h>

#include <unistd.h>

int main(){

  int fd = open("/tmp/mmaptest", O_CREAT | O_RDWR, 0644);

  if(fd < 0){

    perror("open error");

    exit(1);

  }

  ftruncate(fd, 1024);

  void *addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

  if(addr == MAP_FAILED){

    perror("mmap error");

    exit(1);

  }

  close(fd);

  return 0;

}

这段代码打开了一个文件/tmp/mmaptest,然后调用了ftruncate()函数将其扩展为大小为1024字节的文件。接着使用mmap()函数将整个文件映射到虚拟地址空间中,实现了共享内存。其中MAP_SHARED参数表示共享内存区域可以被其他进程访问。

2. shmget()函数

shmget()函数是另外一个用于创建共享内存区域的系统调用,其主要的作用是申请一块指定大小的共享内存,并返回该共享内存区域的标识符。下面是一个使用shmget()函数创建共享内存区域的示例:


#include <sys/ipc.h>

#include <sys/shm.h>

int main(){

  int shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | 0666);

  if(shmid == -1){

    perror("shmget error");

    exit(1);

  }

  void *addr = shmat(shmid, NULL, 0);

  if(addr == (void*)-1){

    perror("shmat error");

    exit(1);

  }

  //...

  shmdt(addr);

  shmctl(shmid, IPC_RMID, NULL);

  return 0;

}

该程序使用shmget()函数申请了一块大小为1024字节的共享内存,并返回该共享内存区域的标识符shmid。接着使用shmat()函数将这块共享内存映射到当前进程的虚拟地址空间中。最后使用shmdt()函数将该共享内存从虚拟地址空间中解除映射,并使用shmctl()函数将其删除。

二、信号量的实现

信号量是另一种常用的同步机制,它通常用于多个进程或线程之间的互斥访问共享资源。在C++中,信号量的实现有以下方法:

1. semget()函数

semget()函数是创建或打开一个信号量集合的系统调用。调用了该函数之后,会获得一个标识符,这个标识符可以用来对该信号量集合进行操作,如访问、设置或删除信号量。下面是使用semget()函数的样例程序:


#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

int main() {

  int semid;

  key_t key;

  if ((key = ftok(".", 's')) == -1) {

    perror("ftok");

    exit(1);

  }

  if ((semid = semget(key, 1, IPC_CREAT | 0666)) == -1) {

    perror("semget");

    exit(1);

  }

  return 0;

}

这段代码使用ftok()函数产生一个唯一的键值key,然后使用semget()函数创建了一个包含1个信号量的信号量集合,并将该信号量集合的标识符保存在semid变量中。

2. semop()函数

semop()函数用于操作信号量集合中的信号量。它可以对信号量进行P操作(减一操作)、V操作(加一操作)和Z操作(阻塞操作),实现多个进程或线程之间的同步。下面是一个使用semop()函数的示例程序:


#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

union semun {

  int val;

  struct semid_ds *buf;

  ushort *array;

};

int main() {

  int semid;

  key_t key;

  struct sembuf sop;

  if ((key = ftok(".", 's')) == -1) {

    perror("ftok");

    exit(1);

  }

  if ((semid = semget(key, 1, 0666)) == -1) {

    perror("semget");

    exit(1);

  }

  sop.sem_num = 0; // 操作信号量集合中第一个信号量

  sop.sem_op = -1; // 对信号量进行P操作

  sop.sem_flg = SEM_UNDO; // 系统自动处理未释放的信号量

  // 执行P操作,如果该信号量的值不为0,则阻塞等待

  if (semop(semid, &sop, 1) == -1) {

    perror("semop");

    exit(1);

  }

  sop.sem_op = 1; // 对信号量进行V操作

  // 执行V操作,唤醒其它的等待进程或线程

  if (semop(semid, &sop, 1) == -1) {

    perror("semop");

    exit(1);

  }

  return 0;

}

这个程序打开了一个之前创建好的信号量集合,并进行了P和V操作,通过这两个操作来实现线程或进程之间的同步。

总结

共享内存和信号量是Linux系统中经常使用的进程之间通信和同步机制。在C++语言中,我们可以使用mmap()、shmget()函数等API来实现共享内存,在使用信号量时则要调用semop()函数。这些方法虽然相对较为复杂,但是熟练掌握后,能够帮助我们解决并发编程的各种问题,提高程序的效率和可靠性。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复