前言😃😃😃
进程间通信的方式
管道 - Linux原生提供
SystemV - 多线程单机通信
posix - 多线程网络通信
这里我们主要是介绍一下SystemV通信方式
一、SystemV原理
首先我们需要知道通信的本质:多个进程能看到同一个资源,即内存。管道是这样的,SystemV同样也是这样的,两者的区别:
管道:通过创建管道文件,让两个进程访问,来实现共享内存,这是一个巧合的方式,刚好文件的特性可以允许支持进程间通信。
SystemV:是由操作系统的设计者,专门为了进程通信设计的系统接口。
SystemV原理介绍
首先我们梳理一遍进程的创建,当我们创建进程A时,操作系统对进程遵循的是先描述在组织的管理方式,要描述就需要创建特定的数据结构来进行存储相应的描述数据,所以操作系统会为A创建对应的数据结构,这其中会创建一个task_struct结构体来记录这个进程的相关数据,同时操作系统会对每一个进程创建一个进程地址空间mm_struct,而task_struct里面有一个指针来指向这个进程地址空间,不清楚进程地址空间的,可以先将它想象成操作系统为这个进程画的大饼,告诉这个进程:你可以使用很多的内存,即虚假的内存。为了和实际的物理内存进行映射,操作系统还会创建一个页表,来进行进程地址空间和物理内存的映射。想要详细了解进程地址空间的可空降(67条消息) <Linux>进程地址空间_绅士·永的博客-CSDN博客
进程地址空间中由上自下依次是->栈区 共享内存 堆区 静态区 全局区 代码区 ,这里我们可以特别注意一下共享内存。
通过以上的认知我们可以说一下原理了,SystmV就是在物理内存中申请一片共享内存。打开文件其实也就是将文件加载在物理内存中。我们得到这个共享内存,在将这个共享内存和我们需要进行通信的进程建立起一种联系,即可通过创建的共享内存进行通信了;
提高:
申请这个共享内存的时候,由我们提供的参数,通过内部的算法会计算出一个唯一的key,进程在进行连接的时候,通过这个唯一的key值,即可找到对应的唯一的共享内存。
至于为什么key是唯一的?OS为了确保key的唯一性,会在内核中创建一个表,记录每一个key值,保证key的唯一性。
二、demo代码
#pragma once
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PATH "/home/mr"
#define PROJ_ID 066
#define SIZE 4096
#include "comm.hpp"
#include "log.hpp"
int main()
{
//获取项目key值
key_t key = ftok(PATH, PROJ_ID);//传入路径,和id,返回一个唯一的key
if (key == -1)
{
perror("ftok");
}
log("get a only of key:") << key << std::endl;
sleep(5);
//创建共享内存
int shmid = shmget(key, SIZE, IPC_CREAT | IPC_EXCL | 0666);//创建共享内存
if (shmid == -1)
{
perror("shmget");
}
log("creat a shm id is:") << shmid << std::endl;
sleep(5);
//挂接到共享内存
char* shmaddr = (char*)shmat(shmid, nullptr, 0);
assert(shmaddr != (void*)-1);
log("link this porcess:") << shmaddr << std::endl;
sleep(5);
//通信处理
//断开挂接
int n = shmdt(shmaddr);
assert(n != -1);
(void)n;
log("Off this porcess:") << n << std::endl;
sleep(5);
//删除共享内存
int s = shmctl(shmid, IPC_RMID, 0);
assert(s != -1);
(void)s;
log("delete this shm:") << s << std::endl;
sleep(5);
return 0;
}
#include "comm.hpp"
int main()
{
// 获取项目key值
key_t key = ftok(PATH, PROJ_ID);
if (key == -1)
{
perror("ftok");
}
// 获取共享内存
int shmid = shmget(key, SIZE, 0);
if (shmid == -1)
{
perror("shmget");
}
//挂接
char* shmaddr = (char*)shmat(shmid, nullptr, 0);
sleep(5);
//断开挂接
int n = shmdt(shmaddr);
assert(n != -1);
(void)n;
return 0;
}
#ifndef _LOG_HPP
#define _LOG_HPP
#include <iostream>
#include <string>
#include <ctime>
std::ostream &log(std::string message)
{
std::cout << (unsigned int)time(nullptr) << ":" << message;
return std::cout;
}
#endif
.PHONY:all
all:shmServe shmclint
shmServe:shmServe.cc
g++ -o $@ $^ -std=c++11
shmclint:shmclint.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f shmclint shmServe
三、总结
共享内存是由操作系统提供的
shmget():创建共享内存
ftok():得到一个随机key值
shmat:挂接进程
shmdt:断开挂接
shmctl:删除共享内存
注意:共享内存是不会自动删除的,需要我们手动删除或者代码删除,手动命令行可以通过ipcrm -m id 删除,ipcs -m查看当前系统的共享内存。