网站设计服务合同,高端网站建设专业公司,网站开发的研究背景,上海哪里做网站比较好文章目录 一、system V共享内存1. 共享内存的原理2. 共享内存相关函数3. 共享内存实现通信4. 共享内存的特点 二、system V消息队列#xff08;了解#xff09;三、system V信号量#xff08;信号量#xff09; 一、system V共享内存
1. 共享内存的原理
共享内存是一种在… 文章目录 一、system V共享内存1. 共享内存的原理2. 共享内存相关函数3. 共享内存实现通信4. 共享内存的特点 二、system V消息队列了解三、system V信号量信号量 一、system V共享内存
1. 共享内存的原理
共享内存是一种在多个进程之间进行进程间通信的机制。它允许多个进程访问相同的物理内存区域从而实现高效的数据交换和通信。
因为进程具有独立性隔离性内核数据结构包括对应的代码、数据与页表都是独立的。OS系统为了让进程间进行通信必须让不同的进程看到同一份资源。所以共享内存的原理如下 1.申请一块空间 2.将创建好的内存映射进进程的地址空间。 共享内存让不同的进程看到同一份的资源就是在物理内存上申请一块内存空间将创建好的内存分别与各个进程的页表之间建立映射然后在虚拟地址空间中将虚拟地址填充到各自页表的对应位置建立起物理地址与虚拟地址的联系。 我们把创建好的内存称为共享内存把进程和共享内存建立映射关系的操作称为挂接把取消进程和内存的映射关系称为去关联把释放内存称为释放共享内存。
共享内存的建立: 在物理内存当中申请共享内存空间将申请到的共享内存挂接到地址空间即建立映射关系。
共享内存的释放: 共享内存与地址空间去关联即取消映射关系释放共享内存空间即将物理内存归还给系统。
对共享内存的理解 共享内存不属于通信的任意一个进程其属于操作系统由操作系统所管理。 管道的本质是文件操作系统已经有相应的内核数据结构来管理文件因此不需要再去设计新的内核数据结构去管理管道。而共享内存是专门为了进程间通信而设计的操作系统可能会有很多共享内存那么操作系统就需要将这些共享内存管理起来。 管理的方式是先描述再组织那么共享内存就等于共享内存块加上共享内存对应的内核数据结构。 对共享内存的修改包括对属性的修改和对内容的修改。 2. 共享内存相关函数 shmget 用来创建或者获取共享内存。失败时返回-1。 参数
shmflg: 通常被设置成两个选项 IPC_CREAT、 IPC_EXCL
IPC_CREAT共享内存不存在则创建如果存在则获取IPC_EXCL无法单独使用IPC_CREAT | IPC_EXCL如果不存在就创建如果存在就出错返回
size: 共享内存的大小
key: 共享内存字段的名字通信的进程需要通过该key值找到同一个共享内存从而进行通信。因此key能保证多个进程看到同一份共享内存能进行唯一性标识。 ftok 生成key。失败时返回-1。 ftok函数将pathname和project id 经过一定的算法转换成 keypathname必须存在projectid不能为0。
OS一定会存在很多的共享内存共享内存本质就是在内存中申请一块空间而key能进行唯一标识。OS申请的自然要做管理共享内存也是如此如何管理先描述在组织。所以共享内存物理内存块共享内存的相关属性。进程如果在内存中创建了共享内存为了让共享内存在系统中保证唯一的通过key来进行标识只要让另一个进程也看到同一个key。 shmat将共享内存段连接到进程地址空间建立页表映射关系。成功返回一个指针指向共享内存第一个字节失败返回 (void*) -1。 参数
shmid 共享内存的标识符。
shmaddr 指定连接地址如果设置为nullptr则让操作系统指定连接到合适的地址上。
shmflg 它的两个可能取值是 SHM_RND 和 SHM_RDONLY。shmflg 等于 SHM_RDONLY 时表示连接操作用来只读共享内存。 shmdt 将共享内存段与当前进程脱离。成功返回0失败返回-1。 参数
shmaddr 由shmat函数所返回的指针。
这里我们需要注意将共享内存和当前进程脱离不等于删除共享内存。 shmctl 用语控制共享内存。 参数
shmid 由shmget函数返回的共享内存标识符。
cmd 将要采取的动作。有三个可以选择
buf 为指向一个保存着共享内存的模式状态和访问权限的数据结构。不关心共享内存的内核数据结构时buf可以设置为nullptr。 这里我们需要注意的是当进程运行结束时进程创建的共享内存还会存在这是因为system V IPC资源的生命周期是随其内核的。其内核可以通过代码删除shmctl函数也可以通过ipcrm -m shmid 指令手动删除。使用ipcs -m 指令可以查看系统中已经创建好的共享内存。 3. 共享内存实现通信
makefile
.PHONY:all
all: shmclient shmservershmclient:client.ccg -o $ $^ -stdc11
shmserver:server.ccg -o $ $^ -stdc11.PHONY:clean
clean:rm -f shmclient shmservercomm.hpp
#ifndef __COMM_HPP__
#define __COMM_HPP_#include iostream
#include cstring
#include cstdio
#include cassert
#include unistd.h
#include sys/ipc.h
#include sys/shm.h
#include sys/types.h
#include sys/stat.h
using namespace std;#define PATHNAME .
#define PROJID 0x6666const int gsize 4096;//获取key
key_t getKey()
{key_t k ftok(PATHNAME, PROJID);if(k -1){cerr error: errno : strerror(errno) endl;exit(1);}return k;
}//转十六进制函数
string toHex(int x)
{char buffer[64];snprintf(buffer, sizeof buffer, 0X%x, x);return buffer;
}//共享内存公共函数
static int createShmHelper(key_t k, size_t size, int flag)
{int shmid shmget(k, size, flag);if(shmid -1){cerr error: errno : strerror(errno) endl;exit(2);}return shmid;
}//创建共享内存
int createShm(key_t k, size_t size)
{umask(0);return createShmHelper(k, size, IPC_CREAT | IPC_EXCL | 0666);
}//获取共享内存
int getShm(key_t k, size_t size)
{return createShmHelper(k, 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 t):_type(t){key_t key getKey();if(_type SERVER)_shmid createShm(key, gsize);else_shmid getShm(key, gsize);_start attachShm(_shmid); }char* getChar(){return _start;}~Init(){detachShm(_start);if(_type SERVER) delShm(_shmid);}
private:char* _start;int _type; // server or clientint _shmid;
};#endif服务端server.cc
#include comm.hppint main()
{Init init(SERVER);char* start init.getChar();int n 0;while(n 35){cout client - server# start endl;sleep(1);n;}// // 1. 创建key// key_t k getKey();// cout server: toHex(k) endl;// // 2. 创建共享内存// int shmid createShm(k, gsize);// cout shmid: shmid endl;// sleep(8);// // 3. 将自己和共享内存关联起来// char* start attachShm(shmid);// sleep(20);// // 4. 将自己和共享内存去关联// detachShm(start);// sleep(3);// 5. 删除共享内存// delShm(shmid);return 0;
}客户端client
#include comm.hppint main()
{Init init(CLIENT);char *start init.getChar();char c A;while(c Z){start[c - A] c;c;start[c - A] \0;sleep(1);}// // 1. 获取key// key_t k getKey();// cout client: toHex(k) endl;// // 2. 获取共享内存// int shmid getShm(k, gsize);// cout shmid: shmid endl;// // 3. 将自己和共享内存关联起来// char* start attachShm(shmid);// sleep(15);// // 4. 将自己和共享内存去关联// detachShm(start);return 0;
}4. 共享内存的特点
优点
只要通信双方使用共享内存一方直接向共享内存中写入数据另一方就可以马上看到对方写入的数据。共享内存是所有进程间通信IPC中速度最快的因为其不需要过多的拷贝不需要将数据给操作系统
下面我们来比对一下共享内存和管道
管道通信的拷贝
C输入设备把数据拷贝到cin或者stdin文件缓存区不考虑这里总共进行了4次拷贝。 共享内存通信的拷贝
直接从输入到共享内存从共享内存到输出。 缺点
以共享内存的方式进行进程间通信缺乏访问控制会带来同步问题比如写端还没将全部数据写入读端就已经开始读取了这将会带来巨大的问题 二、system V消息队列了解
消息队列 是OS提供的内核级队列消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法每个数据块都被认为是有一个类型接收者进程接收的数据块可以有不同的类型值。
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法。每个数据块都被认为是有一个类型接收者进程接收的数据块可以有不同的类型值。IPC资源必须删除否则不会自动清除除非重启所以system V IPC资源的生命周期随内核
常用系统调用ftokmsgget创建消息队列msgctl控制消息队列msgsnd向消息队列发送数据msgrcv从消息队列中读取数据等。 三、system V信号量信号量
下面我们先来引出几个概念
公共资源 被多个进程同时访问的资源访问没有保护的公共资源数据不一致问题。要让不同的进程看到同一份资源是为了通信通信是为了让进程间实现协同而进程之间具有独立性所以为了解决独立性问题要让进程看到同一份资源但是会导致数据不一致的问题。
互斥 任何一个时刻都只允许一个执行流在进行共享资源的访问。各进程间竞争使用这些资源竞争的这种关系为进程的互斥。
临界资源 任何一个时刻都只允许一个执行流在进行访问的共享资源叫做临界资源。
临界区 临界资源是需要通过代码访问的凡是访问临界资源的代码叫做临界区。
原子性 要么不做、要么做完只有两种确定状态的属性叫做原子性。
任何一个执行流想访问临界资源中的一个子资源时不能直接访问。得先申请信号量信号量/信号灯 本质是一个计数器描述资源数量的计数器。 信号量是对临界资源的预定机制。
申请信号量 只要申请信号量成功临界资源内部一定给你预留了你想要的资源申请信号量本质是对临界资源的一种预定机制让信号计数器减减信号量计数器为0时进程申请信号量无法成功。只能阻塞等待其他进程退出才能申请信号量访问临界资源。访问临界资源——进程执行自己的临界区代码释放信号量——信号量计数器加加 我们可以发现共享内存、消息队列、信号量接口相似度非常高获取与删除都是system V标准的进程间通信。
OS如何管理先描述在组织对相关资源的内核数据结构做管理对于共享内存、消息队列、信号量的第一个成员都是ipc_perm:
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 andSHM_LOCKED flags */unsigned short __seq; /* Sequence number */
};虽然内部的属性差别很大但是维护它们的数据结构的第一个成员都是ipc_perm类型的成员变量都可以通过key来标识唯一性。这样设计的好处在操作系统内可以定义一个struct ipc_perm类型的数组此时每当我们申请一个IPC资源就在该数组当中开辟一个这样的结构。((struct shmid_ds*)perms[0]强转此时就可以访问其他剩下的属性)