什么样的网站需要改版,汕头建筑工程总公司官网,pc端兼手机端网站模板,内蒙古互联网公司文章目录 #x1f490;专栏导读#x1f490;文章导读#x1f427;共享内存原理#x1f427;共享内存相关函数#x1f426;key 与 shmid 区别 #x1f427;代码实例 #x1f490;专栏导读 #x1f338;作者简介#xff1a;花想云 #xff0c;在读本科生一枚#xff0… 文章目录 专栏导读文章导读共享内存原理共享内存相关函数key 与 shmid 区别 代码实例 专栏导读 作者简介花想云 在读本科生一枚C/C领域新星创作者新星计划导师阿里云专家博主CSDN内容合伙人…致力于 C/C、Linux 学习。 专栏简介本文收录于 Linux从入门到精通本专栏主要内容为本专栏主要内容为Linux的系统性学习专为小白打造的文章专栏。 相关专栏推荐C语言初阶系列、C语言进阶系列 、C系列、数据结构与算法。 文章导读
共享内存是一种进程间通信的机制允许多个进程访问同一块物理内存以实现数据的共享。通过共享内存进程可以直接读写共享的内存区域而无需通过中间的数据传输机制例如管道或消息队列进行通信因此共享内存是最快的IPC形式。
共享内存示意图
共享内存原理 创建共享内存 在一个进程中调用系统调用例如 shmget请求创建一块共享内存。这个调用需要指定内存的大小以及一些标志以控制共享内存的权限和行为。 关联共享内存 其他进程通过调用系统调用例如 shmat将共享内存附加到它们的地址空间中。这个调用返回指向共享内存区域的指针使得进程可以直接读写这块内存。 读写共享内存 一旦多个进程都关联了同一块共享内存它们就可以直接对这块内存进行读写操作就像操作普通的内存一样。因为它们共享同一块物理内存一个进程对共享内存的修改会立即反映到其他进程的视图中。 分离共享内存 当进程不再需要访问共享内存时它可以调用系统调用例如 shmdt将共享内存从它的地址空间中分离。这并不会导致共享内存的删除只是使得该进程无法再访问这块内存。 删除共享内存 当不再需要使用共享内存时一个进程可以调用系统调用例如 shmctl请求删除共享内存。这会导致释放共享内存所占用的系统资源。
特点和注意事项
共享内存提供了高效的进程间通信方式因为数据直接存储在物理内存中无需复制或转移。进程需要谨慎地协调对共享内存的访问以避免数据一致性问题。例如可以使用互斥锁等同步机制。共享内存的使用需要确保不同进程使用相同的数据结构和协议以便正确地进行数据交换和共享。在使用共享内存时应注意防范竞态条件和死锁等并发编程的问题。
共享内存相关函数 创建共享内存区域 使用 shmget 函数创建一个共享内存区域。该函数的原型为 #include sys/ipc.h
#include sys/shm.hint shmget(key_t key, size_t size, int shmflg);key 是一个用于标识共享内存的键值。size 是要分配的共享内存的大小字节数。shmflg 是一组标志通常使用 IPC_CREAT 表示如果内存不存在则创建。 连接到共享内存区域 使用 shmat 函数将进程连接到已经存在的共享内存区域。该函数的原型为 #include sys/types.h
#include sys/shm.hvoid *shmat(int shmid, const void *shmaddr, int shmflg);shmid 是共享内存区域的标识符由 shmget 返回。shmaddr 通常设置为 NULL让系统自动选择合适的地址。shmflg 可以为 0。 使用共享内存 一旦连接到共享内存进程就可以直接在这块内存中读写数据。 分离共享内存 使用 shmdt 函数将进程与共享内存脱离。该函数的原型为 #include sys/types.h
#include sys/shm.hint shmdt(const void *shmaddr);shmaddr 是连接到共享内存区域的地址。 删除共享内存区域 使用 shmctl 函数可以删除或控制共享内存区域的属性。如果不再需要共享内存可以使用 shmctl 函数的 IPC_RMID 命令删除它。函数原型为 #include sys/ipc.h
#include sys/shm.hint shmctl(int shmid, int cmd, struct shmid_ds *buf);shmid 是共享内存区域的标识符。cmd 为控制命令可以使用 IPC_RMID 表示删除共享内存。
这些函数提供了对共享内存的创建、连接、使用和删除的基本操作。
key 与 shmid 区别
在上面所示的接口中shmget 函数需要一个 key_t 类型的参数 key。而其他的函数多数用到 shmid 而不会用到 key那么这两个参数分别是什么有什么区别呢 key键值 key 是一个整数用于在共享内存创建过程中唯一标识一个共享内存段。它并不是由系统自动生成的而是由应用程序提供的通常以某种方式与程序的逻辑相关。可以使用 ftok 函数将路径名和一个整数标识符转换为 key以便在创建共享内存时使用。key 通常用于在不同的进程之间共享相同的内存块因此它是创建共享内存的关键参数之一。 shmid共享内存标识符 shmid 是一个由系统生成的标识符用于标识已经创建的共享内存段。在使用 shmget 函数创建共享内存时它通过返回值返回给调用者用于后续的操作。shmid 是由系统内核分配的通常是一个唯一的整数。通过 shmat 函数将进程连接到共享内存时需要使用 shmid 作为参数。
总的来说key 是在共享内存创建时由应用程序指定的用户定义的标识符而 shmid 是由系统内核在共享内存创建时自动生成的系统级标识符。key 用于唯一标识共享内存的名字而 shmid 用于在程序运行时标识特定的共享内存实例。
shm可以用于多个进程之间通信在同一时刻可能有多个共享内存被用来进行通信。所以系统中一定会有很多个共享内存同时存在那么系统就会采取一定措施来管理这些共享内存。所以共享内存并不是单单的一块内存空间系统会为它构建一个结构体对象来描述它。
所以共享内存 内核数据结构 真正开辟的内存空间
共享内存数据结构
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 */
};两个进程使用共享内存进行通信的前提是如何让两个进程使用同一块共享内存。内存中有许许多多的共享内存我们如何让两个进程使用一个共享内存呢
这就要提到另一个函数 ftok 了。
ftok 函数是一个用于生成System V IPCInter-Process Communication进程间通信的键值的函数。它的主要用途是在创建System V IPC对象如消息队列、信号量、共享内存时为这些对象生成唯一的键值。
函数原型如下
#include sys/types.h
#include sys/ipc.hkey_t ftok(const char *pathname, int proj_id);pathname 是一个与文件相关的路径名用于生成键值。通常是指向一个存在的文件的路径。proj_id 是一个用户定义的整数用于区分不同的IPC对象。在不同的IPC对象中如果 pathname 相同而 proj_id 不同生成的键值也会不同。
ftok 函数通过将 pathname 转换为一个唯一的键值以确保在不同的进程中使用相同的 pathname 和 proj_id 参数生成的键值是一致的。
一般来说ftok 函数的使用场景是在创建System V IPC对象之前通过调用 ftok 来生成一个唯一的键值。这个键值将被传递给诸如 msgget、semget、shmget 等函数用于创建具体的消息队列、信号量或共享内存段。
需要注意的是ftok 存在一些限制和注意事项比如需要确保 pathname 指向的文件是存在的否则 ftok 会返回 -1。此外由于 proj_id 是一个整数因此其范围应在0到255之间以保证生成的键值在合理的范围内。
所以key 是在内核中使用的类比文件的 inode 编号。而 shmid 是给用户使用的类比文件的文件描述符 fd。
代码实例
接下来我们就通过一个简单的代码设计来熟悉共享内存的使用。该设计的内容是通过共享内存让两个进程Server 与 Client进行通信。
communicate.hpp
该头文件内提供shm所用到的函数方法。
/* communicate.hpp */#ifndef __COMM_HPP__
#define __COMM_HPP__#include iostream
#include cerrno
#include cstdio
#include cstring
#include cassert
#include sys/ipc.h
#include sys/types.h
#include sys/stat.h
#include sys/shm.h
#include unistd.husing namespace std;#define PATHNAME . // 文件路径用于key生成
#define PROJID 0x12138 // 项目ID用于key生成const int gsize 4096;key_t getKey() // 获取键值
{key_t key ftok(PATHNAME, PROJID);if (key -1){cerr error: errno : strerror(errno) endl;exit(1);}return key;
}static int createShmHelper(key_t key, int size, int flag)
{int shmid shmget(key, gsize, flag);if (shmid -1){cerr error: errno : strerror(errno) endl;exit(2);}return shmid;
}int createShm(key_t key, int size) // 创建共享内存
{umask(0);return createShmHelper(key, size, IPC_CREAT | IPC_EXCL | 0666);
}int getShm(key_t key, int size) // 获取共享内存
{umask(0);return createShmHelper(key, size, IPC_CREAT);
}char *attachShm(int shmid) // 关联共享内存
{char *start (char *)shmat(shmid, nullptr, 0); // 将共享内存段连接到进程地址空间return start;
}void detachShm(char *start) // 去关联
{int n shmdt(start); // 将共享内存段与当前进程脱离assert(n ! -1);(void)n;
}void delShm(int shmid) // 释放共享内存
{int n shmctl(shmid, IPC_RMID, nullptr); // 释放共享内存assert(n ! -1);(void)n;
}#define SERVER 1
#define CLIENT 0class Init
{
public:Init(int type): type(type){key_t key getKey();if (type SERVER)shmid createShm(key, gsize);elseshmid getShm(key, gsize);start attachShm(shmid);}char *getStart() { return start; }~Init(){detachShm(start);if (type SERVER)delShm(shmid); // 只有共享内存创建者才负责释放}private:char *start; // 起始地址int type; // server or clientint shmid;
};#endifServer
/* Server.cc */
#include communicate.hpp
#include unistd.husing namespace std;int main()
{ // 建立连接Init init(SERVER);char *start init.getStart();// 开始通信int n 0;while (n 30){cout client - server# start endl;sleep(1);n;}return 0;
}Client
/* Client.cc */
#include communicate.hpp
using namespace std;int main()
{// 建立连接Init init(CLIENT);char *start init.getStart();// 开始通信char c A;while (c Z){start[c - A] c;c;start[c - A] 0;sleep(1);}return 0;
}效果展示 注意
当我们运行完一次程序后再次运行程序会发生错误
$ ./server
error: 17 : File exists原因是上次程序运行时创建的共享内存仍然存在可以使用 ipcs -m 指令来查看已经有的共享内存
$ ipcs -m------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x3801203f 0 hxy 666 4096 0
由此可见共享内存的生命周期是随系统的不随进程。
我们可以使用 ipcrm -m 指令删除指定的共享内存
$ ipcrm -m shmid本章的内容到这里就结束了如果觉得对你有所帮助的话欢迎三连~