Linux 进程间通信(六)共享内存

可以说, 共享内存是一种最为高效的进程间通信方式, 因为进程可以直接读写内存, 不需要任何数据的复制。 为了在多个进程间交换信息, 内核专门留出了一块内存区, 这段内存区可以由需要访问的进程将其映射到自己的私有地址空间。 因此, 进程就可以直接读写这一内存区而不需要进行数据的复制, 从而大大提高了效率。 当然, 由于多个进程共享一段内存,因此也需要依靠某种同步机制, 如互斥锁和信号量等(请参考 4.7.2 节)。

其原理示意图如图4.7 所示。
先参考这个博客:

共享内存的实现分为两个步骤:

第一步是创建共享内存, 这里用到的函数是 shmget(),也就是从内存中获得一段共享内存区域;

第二步是映射共享内存, 也就是把这段创建的共享内存映射到具体的进程空间中, 这里使用的函数是 shmat()。

到这里, 就可以使用这段共享内存了, 也就是可以使用不带缓冲的 I/O 读写命令对其进行操作。

除此之外, 还有撤销映射的操作, 其函数为 shmdt()。 这里主要介绍这 3 个函数。

在Linux中如何查看ipc对象如何删除ipc对象命令
 

ipcs -a:查看所有的ipc对象
ipcs -m:查看共享内存
ipcs -q:消息对列
ipcs -s:信号量

删除ipc对象命令:
ipcrm -m  SHM_ID 或ipcrm -M  shm_key
ipcrm -q  MSG_ID 或ipcrm -Q  msg_key
ipcrm -s  SEM_ID 或ipcrm -S  sem_key

 

shmget()函数的语法要点。

    函数原型

    int shmget(key_t key, int size, int shmflg)

    功能

    创建获得共享内存

    函数传入值
    key: 共享内存的键值, 多个进程可以通过它访问同一个共享内存, 其中有个特殊值
    IPC_PRIVATE, 用于创建当前进程的私有共享内存
    size: 共享内存区大小
    shmflg: 同 open()函数的权限位, 也可以用八进制表示法
    函数返回值
    成功: 共享内存段标识符
    出错: 1

shmat()函数的语法要点。

    函数原型

    char *shmat(int shmid, const void *shmaddr, int shmflg)
    函数传入值
    shmid: 要映射的共享内存区标识符
    shmaddr: 将共享内存映射到指定地址(若为 0 则表示系统自动分配地址并把该段共享内存映射到调用进程的地址空间)
    shmflg:

        SHM_RDONLY: 共享内存只读
        默认 0: 共享内存可读写

    函数返回值
    成功: 被映射的段地址
    出错: 1

shmdt()函数的语法要点。

    函数原型
    int shmdt(const void *shmaddr)
    函数参数

    shmaddr: 被映射的共享内存段地址

    函数返回值
    成功: 0
    出错: 1

shmctl()函数的语法要点。

    函数原型

    int shmctl(int shmid, int cmd, struct shmid_ds *buf);

    功能

    对已存在的共享内存进行控制

    函数参数

    shmid:共享内存标识

    cmd:操作类型

    buf:指向操作的信息

    返回值

    成功返回0,否则返回-1

 重要结构体:

struct shmid_ds {
               struct ipc_perm shm_perm;    /* 存取权限 */
               size_t          shm_segsz;   /* 共享内存大小 */
               time_t          shm_atime;   /* 最后映射时间 */
               time_t          shm_dtime;   /* 最后删除映射时间 */
               time_t          shm_ctime;   /* 最后修改时间 */
               pid_t           shm_cpid;    /* 创建进程ID */
               pid_t           shm_lpid;    /* 最近操作的进程ID */
               shmatt_t        shm_nattch;  /* 建立映射的进程数 */
               ...
           };
struct ipc_perm {
               key_t          __key;    /* Key supplied to shmget(2) */
               uid_t          uid;      /* Effective UID of owner */
               gid_t          gid;      /* Effective GID of owner */
               uid_t          cuid;     /* Effective UID of creator */
               gid_t          cgid;     /* Effective GID of creator */
               unsigned short mode;     /* Permissions + SHM_DEST and
                                           SHM_LOCKED flags */
               unsigned short __seq;    /* Sequence number */
           };

实例代码:

发送端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
#define N 64
 
typedef struct 
{
  pid_t pid;
  char buf[N];
}SHM;
 
void handler(int signo)
{
  //printf("get signal\n");
  return;
}
 
void keycontrol(int signo)
{
  printf("keycontrol is : %d\n",signo);
  if(signo == SIGINT)
  {
    return;
  }
}
 
int main()
{
  key_t key;
  int shmid;
  SHM *p;
  pid_t pid;
 
  //建立IPC通讯,获取key值
  if((key = ftok(".",'m')) < 0)
  {
    perror("fail to ftok");
    exit(-1);
  }
 
 
 
  signal(SIGUSR1,handler);
  signal(SIGINT,keycontrol); 
  // 创建共享内存
  if((shmid = shmget(key,sizeof(SHM),0666|IPC_CREAT|IPC_EXCL)) < 0)
  {//如果创建失败
    if(EEXIST == errno)//如果共享内存已经存在
    {
      shmid = shmget(key,sizeof(SHM),0666);//只是为了获得标识符
      p = (SHM *)shmat(shmid,NULL,0);
      pid = p->pid;
      p->pid = getpid();
      kill(pid,SIGUSR1);
    }
    else
    {
      perror("fail to shmget");
      exit(-1);
    }
  }
  else//如果创建成功
  {
    // 连接共享内存
    p = (SHM*)shmat(shmid,NULL,0);
    p->pid = getpid();
    pause();
    pid=p->pid;
  }
 
  printf("shmid = %d\n",shmid);
  while(1)
  {
    printf("write to shm:");
    fgets(p->buf,N,stdin);
    kill(pid,SIGUSR1);
    if(strcmp(p->buf,"quit\n")==0)
      break;
    pause();
  }
  // 断开连接
  shmdt(p);
  shmctl(shmid,IPC_RMID,NULL);
 
  return 0;
}

接收端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
 
#define N 64
 
typedef struct 
{
  pid_t pid;
  char buf[N];
}SHM;
 
void handler(int signo)
{
  //printf("get signal\n");
  return;
}
 
int main()
{
  key_t key;
  int shmid;
  SHM *p;
  pid_t pid;
 
  // 获取key值
  if((key = ftok(".",'m')) < 0)
  {
    perror("fail to ftok");
    exit(-1);
  }
 
  signal(SIGUSR1,handler);
  // 创建共享内存
  if((shmid = shmget(key,sizeof(SHM),0666|IPC_CREAT|IPC_EXCL)) < 0)
  {//如果创建失败
    if(EEXIST == errno)//如果共享内存已经存在
    {
      shmid = shmget(key,sizeof(SHM),0666);//只是为了获得标识符
      p = (SHM *)shmat(shmid,NULL,0);
      pid = p->pid;
      p->pid = getpid();
      kill(pid,SIGUSR1);
    }
    else
    {
      perror("fail to shmget");
      exit(-1);
    }
  }
  else//如果创建成功
  {
    // 连接共享内存
    p = (SHM*)shmat(shmid,NULL,0);
    p->pid = getpid();
    pause();
    pid=p->pid;
  }
 
  while(1)
  {
    pause();
    if (strcmp(p->buf,"quit\n") == 0)
      exit(0);
    printf("read from shm:%s",p->buf);
    kill(pid,SIGUSR1);
  }
 
  return 0;
}

 


Linux 进程间通信(六)共享内存
https://blog.51cto.com/u_13267193/5371013

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值