Linux--System V共享内存

Posted qnbk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux--System V共享内存相关的知识,希望对你有一定的参考价值。


共享内存区是最快的IPC方式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据
管道通信的本质是基于文件的!OS没有做过多的设计
system v 进程间通信:OS特地设计的通信方式(让不同的进程看到同一份文件)

system V共享内存



共享内存数据结构

 struct shmid_ds {
               struct ipc_perm shm_perm;    /* Ownership and permissions */
               size_t          shm_segsz;   /* Size of segment (bytes) */
               time_t          shm_atime;   /* Last attach time */
               time_t          shm_dtime;   /* Last detach time */
               time_t          shm_ctime;   /* Last change time */
               pid_t           shm_cpid;    /* PID of creator */
               pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
               shmatt_t        shm_nattch;  /* No. of current attaches */
               ...
           };

 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 */
           };

共享内存函数

shmget函数

功能:创建共享内存
原型:int shmget(key_t key, size_t size, int shmflg);
参数:

  • key:共享内存段名字;在内核层面,多个进程之间上区分共享内存的唯一方式
  • size:共享内存大小
  • shmflg:由九个标志权限组成,开辟成功返回一个非负整数,即该共享内存段的标识码;失败返回-1;


补充:

  • SIZE:PAGE_SIZE:4096字节-》一页数据

共享内存大小是按页对齐的

命令查共享内存

ipcs

ipcs -m

//comm.h
#ifndef _COMM_H//防止头文件被重复包含
#define _COMM_H

#define PATHNAME  "/home/L/10-25/server.c"
#define PROJ_ID 0x6666
#define SIZE 1024
#include <stdio.h>

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


#endif

//server.c
#include "comm.h"
#include <unistd.h>


int main()
{
  key_t k = ftok(PATHNAME,PROJ_ID);
  if(k < 0)
  {
    printf("ftok error!\\n");
    return 1;
  }
  printf("%x\\n",k);
  int shm = shmget(k,SIZE,IPC_CREAT|IPC_EXCL);
  if(shm < 0)
  {
    perror("shmget error!\\n");
    return 2;
  }
  sleep(10);
  return 0;
}


删除共享内存

ipcrm -m shmid号

shmctl函数

功能:用于控制进程内存
原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:

  • shmid:由shmget返回的共享内存标识码
  • cmd:将要采取的动作(三个可取值)
  • buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
  • IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值
  • IPC_SET:在进程有足够权限的前提下,把共享内存的当前关联值设为shmid_ds数据结构中给出的值
  • IPC_RMID:删除共享内存
#include "comm.h"
#include <unistd.h>


int main()
{
  key_t k = ftok(PATHNAME,PROJ_ID);
  if(k < 0)
  {
    printf("ftok error!\\n");
    return 1;
  }
  printf("%x\\n",k);
  int shm = shmget(k,SIZE,IPC_CREAT|IPC_EXCL);
  if(shm < 0)
  {
    perror("shmget error!\\n");
    return 2;
  }
  sleep(10);
  shmctl(shm,IPC_RMID,NULL);
  sleep(10);
  return 0;
}

shmat函数

功能:将共享内存段连接到进程地址空间
原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:

  • shmid:共享内存标识;在进程内部,区分某一个IPC资源
  • shmaddr:指定连接的地址
  • shmflg:SHM_RND /SHM_RDONLY;返回值:成功返回指针(对应共享内存映射到地址空间中的虚拟地址的起始地址),失败返回-1
    补充:
  • shmaddr 为NULL会自动选择一个地址
  • shmaddr不为NULL且shmflg无SHM_RND标记,则以shmadd为连接地址
  • shmaddr不为NULL且shmflg设置SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - shmflg % SHMLBA
  • shmflg = SHM_RDONLY,表示连接操作只用来读共享内存
    1\\
#include "comm.h"
#include <unistd.h>


int main()
{
  key_t k = ftok(PATHNAME,PROJ_ID);
  if(k < 0)
  {
    printf("ftok error!\\n");
    return 1;
  }
  printf("%x\\n",k);
  int shmid = shmget(k,SIZE,IPC_CREAT|IPC_EXCL);
  if(shmid < 0)
  {
    perror("shmget error!\\n");
    return 2;
  }
  printf("shmid:%d\\n",shmid);
  printf("attach begin!\\n");
  sleep(3);
  char *mem = shmat(shmid,NULL,0);
  printf("attach end!,%d\\n",(int)mem);
  sleep(3);
  return 0;
}


2\\

#include "comm.h"
#include <unistd.h>


int main()
{
  key_t k = ftok(PATHNAME,PROJ_ID);
  if(k < 0)
  {
    printf("ftok error!\\n");
    return 1;
  }
  printf("%x\\n",k);
  int shmid = shmget(k,SIZE,IPC_CREAT|IPC_EXCL | 0666);
  if(shmid < 0)
  {
    perror("shmget error!\\n");
    return 2;
  }
  printf("shmid:%d\\n",shmid);
  sleep(3);
  char *mem = shmat(shmid,NULL,0);
  printf("attach end!,%d\\n",(int)mem);
  sleep(3);
  return 0;
}

shmdt函数

功能:将共享内存与当前进程脱离
原型: int shmdt(const void *shmaddr);
参数:

  • shmaddr:由shmat返回的指针
  • 注意:将共享内存与当前进程脱离不相当于删除共享内存段

实例

#include "comm.h"
#include <unistd.h>


int main()
{
  key_t k = ftok(PATHNAME,PROJ_ID);
  if(k < 0)
  {
    printf("ftok error!\\n");
    return 1;
  }
  printf("%x\\n",k);
  int shmid = shmget(k,SIZE,IPC_CREAT|IPC_EXCL | 0666);
  if(shmid < 0)
  {
    perror("shmget error!\\n");
    return 2;
  }
  printf("shmid:%d\\n",shmid);
  printf("attach begin!\\n");
  sleep(3);
  char *mem = shmat(shmid,NULL,0);//关联
  printf("attach end!,%d\\n",(int)mem);
  sleep(3);
  
  printf("dettach begin!\\n");
  sleep(2);
  //
  shmdt(mem);//去关联
  printf("dettach end!\\n");
  sleep(2);
  shmctl(shmid,IPC_RMID,NULL);
  return 0;
}


//server.c
#include "comm.h"
#include <unistd.h>


int main()
{
  key_t k = ftok(PATHNAME,PROJ_ID);
  if(k < 0)
  {
    printf("ftok error!\\n");
    return 1;
  }
  int shmid = shmget(k,SIZE,IPC_CREAT|IPC_EXCL | 0666);
  if(shmid < 0)
  {
    perror("shmget error!\\n");
    return 2;
  }
  char *mem = shmat(shmid,NULL,0);//关联
  //todo
  while(1)
  {
    printf("client msg# %s\\n",mem);
    sleep(1);
  }
  shmdt(mem);
  return 0;
}

//client.c
#include "comm.h"
int main()
{
  key_t k = ftok(PATHNAME,PROJ_ID);
  if(k < 0)
  {
    perror("ftok error!\\n");
    return 1;
  }
  printf("%x\\n",k);
  int shmid = shmget(k,SIZE,IPC_CREAT);
  if(shmid < 0)
  {
    perror("shmid error!\\n");
    return 2;
  }
  char* mem = shmat(shmid,NULL,0);
  //todo
  int i = 0;
  while(1)
  {
    mem[i] = 'A' + i;
    i++;
    sleep(1);
    mem[i] = '\\0';
  }
  shmdt(mem);
  return 0;
}


读共享内存的时候,没有使用OS接口
共享内存是所有的进程间通信中速度最快的

  • 1、拷贝次数少
  • 2、不提供任何保护机制(同步与互斥)

system V 消息队列

  • 消息队列提供了一个从一个进程向另一个进程发送一块数据的方法
  • 每个数据块都被认为是有一个类型,接收者进程接受的数据块可以有不同的类型值
  • 特性方面:IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPV资源生命周期随内核

msgget

获取消息队列

msgctl

删除消息队列

struct msqid_ds {
               struct ipc_perm msg_perm;     /* Ownership and permissions */
               time_t          msg_stime;    /* Time of last msgsnd(2) */
               time_t          msg_rtime;    /* Time of last msgrcv(2) */
               time_t          msg_ctime;    /* Time of last change */
               unsigned long   __msg_cbytes; /* Current number of bytes in
                                                queue (nonstandard) */
               msgqnum_t       msg_qnum;     /* Current number of messages
                                                in queue */
               msglen_t        msg_qbytes;   /* Maximum number of bytes
                                                allowed in queue */
               pid_t           msg_lspid;    /* PID of last msgsnd(2) */
               pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
           };

msgsnd

发送消息队列

system V 信号量

进程互斥

  • 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关心称为互斥
  • 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源
  • 在进程中涉及到互斥资源的程序段叫临界区
  • 特性方面:IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPV资源生命周期随内核

semget

获取信号量

  • nsems:几个信号量

semctl

释放信号量

semop

对信号量操作

信号量

以上是关于Linux--System V共享内存的主要内容,如果未能解决你的问题,请参考以下文章

Linux system v 共享内存

C 中的共享内存代码片段

linux进程间通信之System V共享内存详解及代码示例

IPC System V 消息队列 - 发送一个数组块

System V 共享内存

五十进程间通信——System V IPC 之共享内存