linux进程间通讯


进程间通讯->共享内存

struct shmid_ds
{
  struct ipc_perm shm_perm;     /* operation perms */
  int shm_segsz;           /* size of segment (bytes) */
  __kernel_time_t shm_atime;     /* last attach time */
  __kernel_time_t shm_dtime;     /* last detach time */
  __kernel_time_t shm_ctime;     /* last change time */
  __kernel_ipc_pid_t shm_cpid;     /* pid of creator */
  __kernel_ipc_pid_t shm_lpid;     /* pid of last operator */
  unsigned short shm_nattch;     /* no. of current attaches */
  unsigned short shm_unused;      /* compatibility */
  void *shm_unused2;        /* ditto - used by DIPC */
  void *shm_unused3;        /* unused */
};

/****************************************************/
#include <sys/ipc.h>
#include <sys/shm.h>
功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字,能进行唯一标识,通过ftok()函数获取key值。
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
IPC_CREAT: 如果要创建的共享内存不存在,就创建,如果存在,就获取
IPC_EXCL : 无法单独使用
IPC_CREAT | IPC_EXCL 如果不存在,就创建(一定是一个新的共享内存),如果存在,就出错
返回值:
成功返回一个非负整数,即该共享内存段的标识码;失败返回-1


/****************************************************/
#include <sys/types.h>
#include <sys/ipc.h>
功能:将路径名和项目标识符转换为系统V IPC密钥
原型:
key_t ftok(const char *pathname, int proj_id);

参数
pathname:形成key值的字符串.
proj_id:形成key值的id.

返回值:
失败会返回-1,erron会被设置.


/****************************************************/
#include <sys/types.h>
#include <sys/shm.h>
功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);

参数
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

// 说明:
shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr -
(shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存


/****************************************************/
功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1

注意:将共享内存段与当前进程脱离不等于删除共享内存段


/****************************************************/
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
IPC_STAT: 把shmid_ds结构中的数据设置为共享内存的当前关联值。
IPC_SET : 在进程有足够权限的前提下,把共享内存的当前关联之设置为shimd_ds数据结构中给出的值。
IPC_RMID: 立即删除共享内存段
IPC_INFO:
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:
成功返回0;失败返回-1


/****************************************************/
文件:Makefile
.PHONY:all
all:shm_client shm_server

shm_client:shm_client.cpp
g++ -o $@ $^ -std=c++11
shm_server:shm_server.cpp
g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
rm -f shm_client shm_server

/****************************************************/
文件:comm.hpp
#ifndef _COMM_HPP_
#define _COMM_HPP_

#include <iostream>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define PATHNAME "."
#define PROJ_ID 0X66
// 共享内存的大小,一般建议4kb的整数倍,因为系统分配共享内存是以4kb为单位的!
// 内存划分内存块的基本单位,如果不是,内核会给向上取整!
#define MAX_SIZE 4098

/* 创建key_t值 */
key_t GetKey()
{
  key_t k = ftok(PATHNAME, PROJ_ID);
  if(k < 0)
  {
    std::cerr << errno << ":" << strerror(errno) << std::endl;
    exit(1);
  }
  // 程序走到这里,说明创建key_t值成功.
  return k;
}

/* 创建 | 获取共享内存 */
int GetShmHelper(key_t k, int flags)
{
  // k是要通过shmget()函数,设置进入内存属性中的!用来表示该共享内存,在内核中的唯一性!
  // shmid vs k 类似于 fd vs inode
  int shmid = shmget(k, MAX_SIZE,flags);
  if(k < 0)
  {
    std::cerr << errno << ":" << strerror(errno) << std::endl;
    exit(2);
  }
  return shmid;
}

/* 创建共享内存 */
int CreateShm(key_t k)
{
  return GetShmHelper(k, IPC_CREAT | IPC_EXCL | 0666);
}

/* 获取共享内存 */
int GetShm(key_t k)
{
  return GetShmHelper(k, IPC_CREAT);
}

/* 进程关联共享内存 */
void* AttachShm(int shmid)
{
  void* mem = shmat(shmid, nullptr, 0);
  if((long long)mem == -1L)
  {
    std::cerr << errno << ":" << strerror(errno) << std::endl;
    exit(3);
  }
  return mem;
}

/* 进程去除关联共享内存 */
void DetaShm(void *pStart)
{
  if(shmdt(pStart) == -1)
  {
    std::cerr << errno << ":" << strerror(errno) << std::endl;
  }
}

/* 删除共享内存 */
void DelShm(int shmid)
{
  if(shmctl(shmid, IPC_RMID, nullptr) == -1)
  {
    std::cerr << errno << ":" << strerror(errno) << std::endl;
  }
}

#endif//_COMM_HPP_


/****************************************************/
文件:shm_client.cpp
#include "comm.hpp"
int main()
{
  // 获取共享内存所需要的key值
  key_t k = GetKey();
  printf("0x%x\n", k);
  // 获取共享内存id
  int shmid = GetShm(k);
  printf("shmid: %d\n", shmid);
  sleep(5);

  // 进程关联共享内存
  char* pStart = (char*)AttachShm(shmid);
  printf("Attach success, Address start: %p\n", pStart);
  sleep(5);

  //创建消息内容
  const char* message = "Hello Server,我是另一个进程,正在和您通信";
  int count = 0;
  pid_t id = getpid();

  while(true)
  {
    // 发送信息给server(将消息打印到共享内存)
    snprintf(pStart, MAX_SIZE, "%s[我的pid:%d] [消息编号:%d]", message, id, ++count);
    sleep(1);
  }

  // 去除进程关联共享内存
  DetaShm(pStart);
  sleep(5);

  // 不负责删除server会操作
  return 0;
}

/****************************************************/
文件:shm_server.cpp
// shell脚本监控命令:while :; do ipcs -m; sleep 1; done
#include "comm.hpp"
int main()
{
  // 获取共享内存所需要的key值
  key_t k = GetKey();
  printf("0x%x\n", k);

  // 创建共享内存
  int shmid = CreateShm(k);
  printf("shmid: %d\n", shmid);
  sleep(5);

  // 进程关联共享内存
  char* pStart = (char*)AttachShm(shmid);
  printf("Attach success, Address start: %p\n", pStart);
  sleep(5);

  // 接收client的信息
  while(true)
  {
    //打印共享内存内容
    printf("Client say:%s\n", pStart);

    // 打印共享内存的属性
    struct shmid_ds ds;
    shmctl(shmid, IPC_STAT, &ds); //获取消息属性
    printf("获取属性:size:%d,pid:%d,myself:%d,key:0x%x\n", ds.shm_segsz, ds.shm_cpid, getpid(), ds.shm_perm.__key);

    sleep(1);
  }

  // 去除进程关联共享内存ls
  DetaShm(pStart);
  sleep(10);

  // 删除共享内存
  DelShm(shmid);
  return 0;
}
共享内存优点:
共享内存是所有进程通讯最快的,能大大的减少数据的拷贝次数。
同样的代码分别用管道和共享内存实现,综合考虑管道和共享内存,考虑键盘输入和显示器输出,那么共享内存有几次拷贝?管道几次拷贝?
管道:单纯收发消息4(4+2)
共享内存:单纯收发消息2(2+2)

共享内存缺点:
共享内存是不对数据进行保护的,需要用户自助添加保护功能。

 

 

进程间通讯->消息队列
/****************************************************/
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) */
};

struct ipc_perm
{
  key_t __key;      /* Key supplied to msgget(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 */
  unsigned short __seq;    /* Sequence number */
};

/****************************************************/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
功能:获取消息队列
原型:
int msgget(key_t key, int msgflg);
返回值:msqid
-1:错误。

/****************************************************/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
功能:设置消息队列
原型:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
返回值:-1:错误


/****************************************************/
The msgp argument is a pointer to a caller-defined structure of the following general form:
struct msgbuf
{
  long mtype;    /* message type, must be > 0 */
  char mtext[1];   /* message data */
};

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
功能:发送消息
原型:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);


/****************************************************/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
功能:接收消息
原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);


/****************************************************/
// sendmsg.c
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
  long type;
  char text[100];
} message_t;

int main()
{
  key_t key = ftok("/tmp", 'a'); // 创建一个唯一的key
  int msgid = msgget(key, 0666 | IPC_CREAT); // 创建消息队列
  if (msgid == -1)
  {
    perror("msgget");
    exit(EXIT_FAILURE);
  }

  message_t message;
  message.type = 1;
  strcpy(message.text, "Hello, World!");
  int result = msgsnd(msgid, &message, sizeof(message.text), 0);
  if (result == -1)
  {
    perror("msgsnd");
    exit(EXIT_FAILURE);
  }

  printf("消息发送成功,text=%s\n", message.text);

  return 0;
}


/****************************************************/
// rsvmsg.c
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
  long type;
  char text[100];
} message_t;

int main()
{
  key_t key = ftok("/tmp", 'a'); // 创建一个唯一的key
  int msgid = msgget(key, 0666 | IPC_CREAT); // 创建消息队列
  if (msgid == -1)
  {
    perror("msgget");
    exit(EXIT_FAILURE);
  }

  message_t message;
  int result = msgrcv(msgid, &message, sizeof(message.text), 1, 0);
  if (result == -1)
  {
    perror("msgrcv");
    exit(EXIT_FAILURE);
  }

  printf("消息接收成功,text=%s\n", message.text);

  return 0;
}

posted @ 2024-01-24 13:14  *^VV^*  阅读(3)  评论(0编辑  收藏  举报