赞
踩
共享内存区是最快的 IPC 形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
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 */
};
shmget
功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的 mode 模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
shmat
功能:将共享内存段连接到进程地址空间
原型
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,表示连接操作用来只读共享内存。
shmdt
功能:将共享内存段与当前进程脱离
原型
int shmdt(const void* shmaddr);
参数
shmaddr:由 shmat 所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
shmctl
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds* buf);
参数
shmid:由 shmget 返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
代码结构:
$ ls
Comm.hpp Fifo.hpp Makefile ShmClient.cc ShmServer.cc
$ cat Makefile
.PHONY:all
all:shm_client shm_server
shm_server:ShmServer.cc
g++ -o $@ $^ -std=c++11
shm_client:ShmClient.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f shm_client shm_server
Fifo.hpp:
#ifndef __COMM_HPP__ #define __COMM_HPP__ #include <iostream> #include <string> #include <cerrno> #include <cstring> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <cassert> using namespace std; #define Mode 0666 #define Path "./fifo" class Fifo { public: Fifo(const string &path = Path) : _path(path) { umask(0); int n = mkfifo(_path.c_str(), Mode); if (n == 0) { cout << "mkfifo success" << endl; } else { cerr << "mkfifo failed, errno: " << errno << ", errstring: " << strerror(errno) << endl; } } ~Fifo() { int n = unlink(_path.c_str()); if (n == 0) { cout << "remove fifo file " << _path << " success" << endl; } else { cerr << "remove failed, errno: " << errno << ", errstring: " << strerror(errno) << endl; } } private: string _path; // 文件路径 + 文件名 }; class Sync { public: Sync() : rfd(-1), wfd(-1) { } void OpenReadOrDie() { rfd = open(Path, O_RDONLY); if (rfd < 0) exit(1); } void OpenWriteOrDie() { wfd = open(Path, O_WRONLY); if (wfd < 0) exit(1); } bool Wait() { bool ret = true; uint32_t c = 0; ssize_t n = read(rfd, &c, sizeof(uint32_t)); if (n == sizeof(uint32_t)) { std::cout << "server wakeup, begin read shm..." << std::endl; } else if (n == 0) { ret = false; } else { return false; } return ret; } void Wakeup() { uint32_t c = 0; ssize_t n = write(wfd, &c, sizeof(c)); assert(n == sizeof(uint32_t)); std::cout << "wakeup server..." << std::endl; } ~Sync() { } private: int rfd; int wfd; }; #endif
Comm.hpp:
#pragma once #include <iostream> #include <cerrno> #include <cstring> #include <cstdlib> #include <string> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> using namespace std; const char *pathname = "/home/ubuntu"; const int proj_id = 0x66; // 在内核中,共享内存的大小是以4KB为基本单位的,你只能用你申请的大小。建议申请大小是N*4KB const int defaultsize = 4096; // 单位是字节 std::string ToHex(key_t k) { char buffer[1024]; snprintf(buffer, sizeof(buffer), "0x%x", k); return buffer; } key_t GetShmKeyOrDie() { key_t k = ftok(pathname, proj_id); if (k < 0) { std::cerr << "ftok error, errno: " << errno << ", error string: " << strerror(errno) << std::endl; exit(1); } return k; } int CreateShmOrDie(key_t key, int size, int flag) { int shmid = shmget(key, size, flag); if (shmid < 0) { std::cerr << "shmget error, errno: " << errno << ", error string: " << strerror(errno) << std::endl; exit(2); } return shmid; } int CreateShm(key_t key, int size) { // IPC_CREAT: 不存在就创建,存在就获取 // IPC_EXCL: 没有意义 // IPC_CREAT | IPC_EXCL: 不存在就创建,存在就出错返回 return CreateShmOrDie(key, size, IPC_CREAT | 0666); } int GetShm(key_t key, int size) { return CreateShmOrDie(key, size, IPC_CREAT); } void DeleteShm(int shmid) { int n = shmctl(shmid, IPC_RMID, nullptr); if (n < 0) { std::cerr << "shmctl error" << std::endl; } else { std::cout << "shmctl delete shm success, shmid: " << shmid << std::endl; } } void ShmDebug(int shmid) { struct shmid_ds shmds; int n = shmctl(shmid, IPC_STAT, &shmds); if (n < 0) { std::cerr << "shmctl error" << std::endl; return; } std::cout << "shmds.shm_segez: " << shmds.shm_segsz << std::endl; std::cout << "shmds.shm_nattch: " << shmds.shm_nattch << std::endl; std::cout << "shmds.shm_ctime: " << shmds.shm_ctime << std::endl; std::cout << "shmds.shm_perm.__key: " << ToHex(shmds.shm_perm.__key) << std::endl; } void *ShmAttach(int shmid) { // 核心自动选择一个地址 void *addr = shmat(shmid, nullptr, 0); if ((long long int)addr == -1) { std::cerr << "shmat error" << std::endl; return nullptr; } return addr; } void ShmDetach(void *addr) { int n = shmdt(addr); if (n < 0) { std::cerr << "shmdt error" << std::endl; } }
ShmServer:
#include "Comm.hpp" #include "Fifo.hpp" int main() { // 1. 获取key key_t key = GetShmKeyOrDie(); std::cout << "key: " << ToHex(key) << std::endl; // 2. 创建共享内存 int shmid = CreateShm(key, defaultsize); std::cout << "shmid: " << shmid << std::endl; // 4. 将共享内存和进程进行挂接(关联) char *addr = (char *)ShmAttach(shmid); std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl; // 0. 先引入管道 Fifo fifo; Sync syn; syn.OpenReadOrDie(); // 可以进行通信了 for (;;) { if (!syn.Wait()) break; std::cout << "shm content: " << addr << std::endl; } ShmDetach(addr); std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl; // 3. 删除共享内存 DeleteShm(shmid); return 0; }
ShmClient:
#include "Comm.hpp" #include "Fifo.hpp" int main() { key_t key = GetShmKeyOrDie(); std::cout << "key: " << ToHex(key) << std::endl; int shmid = GetShm(key, defaultsize); std::cout << "shmid: " << shmid << std::endl; char *addr = (char *)ShmAttach(shmid); std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl; memset(addr, 0, defaultsize); Sync syn; syn.OpenWriteOrDie(); // 可以进行通信了 for (char c = 'A'; c <= 'Z'; c++) // shm没有使用系统调用 { addr[c - 'A'] = c; syn.Wakeup(); sleep(1); } ShmDetach(addr); std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl; sleep(5); return 0; }
结果演示:
shmid
,shmid 是共享内存的唯一性标识!# 查看共享内存
ipcs -m
# 删除指定共享内存
ipcrm -m [shmid]
# 实机演示
$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 0 root 644 80 2
0x00000000 1 root 644 16384 2
0x00000000 2 root 644 280 2
0x6602fc97 10 ubuntu 666 4096 1
$ ipcrm -m 10 # 指定shmid即可删除该共享内存资源
注意:不是必须通过手动来删除,这里只是为了演示相关指令,删除共享内存资源是进程该做的事情。
注意:共享内存没有进程同步与互斥!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。