进程间通讯->共享内存
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;
}