网站的设计制作流程,网页制作模板怎么做,做网站都有那些步骤,信阳市住房和城乡建设局网站文章目录 1. 进程间通信2. 管道匿名管道命名管道管道的特性管道的应用#xff1a;简易的进程池 3. System V共享内存共享内存的概念共享内存的结构共享内存的使用代码实现 1. 进程间通信 进程间通信#xff08;Inter-Process Communication#xff0c;简称IPC#xff09;是… 文章目录 1. 进程间通信2. 管道匿名管道命名管道管道的特性管道的应用简易的进程池 3. System V共享内存共享内存的概念共享内存的结构共享内存的使用代码实现 1. 进程间通信 进程间通信Inter-Process Communication简称IPC是指不同进程之间进行数据交换和共享信息的机制和技术。在操作系统中每个进程都是独立运行的有自己的地址空间和数据因此进程之间需要一种机制来进行通信以便彼此协调工作、共享数据或者进行同步操作。 进程间通信的前提也是重中之重是让不同的进程看到同一份资源。 由于进程的独立性只有先让不同进程看到同一份资源有了通信的平台才能实现通信。本文重点在于如何搭建进程间通信的平台使得不同进程看到同一份资源。
2. 管道 管道是一种传统的进程间通信方法。管道的本质是一个特殊文件一个进程作为写入端一个进程作为读取段通过写入和读取管道实现通信。 管道分为匿名管道和命名管道它们的使用场景不同。
匿名管道
匿名管道pipe应用于有亲缘关系的进程之间通信如父子进程、兄弟进程。以父子进程为例原理 父进程创建管道并分别以写方式和读方式打开管道此时父进程就拥有了两个新的文件描述符以写方式打开管道的文件描述符称为写端fd以读方式打开管道的文件描述符称为读端fd。 接着创建子进程子进程继承了父进程的文件描述符表二者有了相同的写端fd和读端fd。 然后根据需求关闭不要的文件描述符如父进程写数据给子进程即父进程作为写入端子进程作为读取端那就关闭父进程的读端fd和子进程的写端fd。 此时父子进程已经能看到同一份资源了通信开始父进程调用write写入管道子进程调用read读取管道和文件操作相同。 在这个过程中创建的管道称之为匿名管道。之所以是匿名管道是因为整个过程中用户都无法获知管道的名称等具体信息该管道由OS维护。 ⭕上述过程的逻辑演绎如下 补充 管道是一种特殊的文件它在内存中以缓冲区的形式存在。因此打开管道就和打开文件一样OS也会在内存中创建一个打开文件句柄来维护管道。通过打开文件句柄我们可以引用到管道的缓冲区从而对其进行读写操作。 匿名管道的生命周期随进程。当引用该管道的所有进程退出OS自动关闭并删除匿名管道。打开文件句柄和inode的引用计数问题 因为管道是一种临时的通信机制不像普通文件具有持久性的存储需求所以管道是没有磁盘文件的。那么管道是否像文件一样拥有一个inode呢是的。管道文件的inode主要用于标识和管理管道记录与管道相关的元数据信息并跟踪管道的引用计数。管道文件的inode并不链接实际数据数据是通过内核的缓冲区进行传递和管理的。 管道是一种半双工的通信方式即一端写一端读单向数据流动。 下面是代码分析。
首先是创建匿名管道的接口
int pipe(int pipefd[2]);pipe是一个系统调用接口。当前进程创建匿名管道传入参数pipefd是一个能够存放2个元素的整型数组调用成功后管道的写端fd和读端fd存入pipefd中pipefd[0]是读端fdpipefd[1]是写端fd。 下面是pipe在2号手册中的介绍。
NAMEpipe, pipe2 - create pipeSYNOPSIS#include unistd.hint pipe(int pipefd[2]);
RETURN VALUEOn success, zero is returned. On error, -1 is returned, and errno is set appropriately.下面是使用匿名管道实现进程间通信的一段代码
#include iostream
#include unistd.h
#include cerrno
#include cstring
#include cassert
#include sys/types.h
#include sys/wait.husing namespace std;
const int NUM 1024;// 先创建管道进而创建子进程父子进程使用管道进行通信
// 父进程向管道当中写“i am father”
// 子进程从管道当中读出内容, 并且打印到标准输出int main()
{// 1.创建管道int pipefd[2] {0};int ret pipe(pipefd);if (ret 0){cerr errno : strerror(errno) endl;return 1;}// 2.创建子进程pid_t id fork();assert(id 0);if (id 0){// 子进程读// 3.关闭不要的fdclose(pipefd[1]);// 4.通信char buf[NUM] {0};int n read(pipefd[0], buf, sizeof(buf) - 1);if (n 0){buf[n] \0;cout buf endl;}else if (n 0){cout 读取到文件末尾 endl;}else{exit(1);}close(pipefd[0]);exit(0);}// 父进程写// 3.关闭不要的fdclose(pipefd[0]);// 4.通信const char *msg I am father;write(pipefd[1], msg, strlen(msg));close(pipefd[1]);// 5.等待子进程退出int n waitpid(id, nullptr, 0);if (n -1){cerr errno : strerror(errno) endl;return 1;}return 0;
}⭕执行结果
[ckfVM-8-3-centos Testpipe]$ ./a.out
I am father #子进程成功读取并输出父进程发送的信息命名管道
命名管道named pipe应用于无亲缘关系的进程之间通信。无亲缘关系的两个进程无法通过继承文件描述符表来获得同一个匿名管道因此就需要命名管道。命名管道有特定的文件名多个进程可以通过相同的文件名找到相同的管道进而实现通信。使用命名管道的步骤如下 创建命名管道 创建命名管道的方式有两种通过指令或系统调用。 指令 mkfifo [选项] [name]
OPTION:-m MODE #设置管道的权限系统调用 NAMEmkfifo - make a FIFO special file (a named pipe)SYNOPSIS#include sys/types.h#include sys/stat.hint mkfifo(const char *pathname, mode_t mode);
RETURN VALUEOn success mkfifo() returns 0. In the case of an error, -1 is returned (in which case, errno is set appropriately).进程打开命名管道 进程可以调用open接口以读或写方式打开命名管道此时必须保证命名管道是存在的。注意进程要有命名管道对应的权限才能正确地读取或写入数据权限在创建管道时设定。 通信 关闭管道删除管道 进程调用close关闭管道退出程序。命名管道的生命周期不随进程进程退出命名管道依旧存在。因此需要用户自行删除可以通过指令rm删除命名管道文件也可以在进程中调用unlink接口。 NAMEunlink - delete a name and possibly the file it refers toSYNOPSIS#include unistd.hint unlink(const char *pathname);
RETURN VALUEOn success, zero is returned. On error, -1 is returned, and errno is set appropriately.下面是两个进程使用命名管道实现进程间通信client是写进程负责创建namedpipe和删除namedpipe并向server发送数据数据由用户交互传递。server是读进程只负责读取client发送的数据。
注意 对于打开命名管道的写端调用open时若此时该命名管道没有读端则写端会阻塞等待至少一个读端打开该管道写端才会打开。同理若想打开读端但是没有写端也会阻塞等待。
//client
#include common.hppint main()
{// 1.创建命名管道umask(0);int ret mkfifo(pipename.c_str(), 0666);if (ret 0){std::cerr errno : strerror(errno) std::endl;return 1;}// 2.以写方式打开命名管道int wfd open(pipename.c_str(), O_WRONLY);if (wfd 0){std::cerr errno : strerror(errno) std::endl;return 1;}//3.向管道中写入数据char buf[NUM] {0};std::cout 请输入您想要发送给服务端的信息: std::endl;while (true){char *str fgets(buf, sizeof(buf), stdin);assert(str);(void)str;int n strlen(buf);buf[n - 1] \0; // 消除\nif (strcasecmp(buf, quit) 0)break;int ret write(wfd, buf, sizeof(buf));assert(ret 0);(void)ret;}// 4.退出关闭写端close(wfd);unlink(pipename.c_str());return 0;
}//server
#include common.hppint main()
{// 1.以读方式打开命名管道int rfd open(pipename.c_str(), O_RDONLY);if (rfd 0){std::cerr errno : strerror(errno) std::endl;return 1;}//2.读取管道中的数据char buf[NUM] {0};while (true){int cnt read(rfd, buf, sizeof(buf));if (cnt 0){buf[cnt] \0;std::cout message from client: buf std::endl;}else if (cnt 0){std::cout 通信结束 std::endl;break;}else{return 1;}}// 3.关闭读端close(rfd);return 0;
}//common.hpp
#pragma once
#include iostream
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include cstring
#include cerrno
#include unistd.h
#include cassertconst std::string pipename fifo;
const int NUM 1024;⭕实操演示 管道的特性 作为特殊的文件管道具有一些特性匿名管道和命名管道同时具备 当管道为空时或读进程读完数据时读进程再次读取时会阻塞等待写进程写入数据后才开始读取。当管道为满时读进程没有读取数据写进程会阻塞等待读进程读取出一些数据后再写入数据否则未被读取的数据可能会被覆盖。若所有写进程被关闭读进程仍在读取此时读进程调用的read函数会返回0表示读取到文件末尾即读取结束若所有读进程被关闭写进程再写入数据就无意义了因此OS会发送信号SIGPIPE终止写进程
这种特性也被称为“管道的阻塞机制”。管道的阻塞机制确保了数据在写进程和读进程之间的可靠传递和同步处理提高了数据处理的准确性和效率,为进程之间的通信和数据交换提供了便利和可靠性。 管道的应用简易的进程池 使用匿名管道制作一个简易的进程池大概思路先创建一个父进程然后让这个父进程创建多个子进程通过用户交互的模式让父进程下发指定的任务给不同的子进程。其中”下发任务“这个过程就是利用管道来实现父进程对于每个子进程都有唯一一个管道用以传输“任务”数据。 管理子进程 一个父进程对多个子进程且每个子进程对应一个管道那么肯定要先将多个子进程管理起来。根据“先描述再组织”的管理思想我的设计如下先将子进程描述为一个结构体该结构体中包含子进程pid、子进程对应管道在父进程中的写端fd、以及一个子进程名称自定义格式为了后续方便调试观察。然后在父进程中定义一个容器用以组织这些创建出来的子进程结构体方便后续管理。 //描述子进程结构体
struct ChildProc
{ChildProc(int pid, int write_fd) : _pid(pid), _write_fd(write_fd){_proc_name proc- to_string(_pid) : to_string(_write_fd);}int _pid;int _write_fd;string _proc_name;
};//父进程主函数即整个进程池的框架
int main()
{//定义一个vector容器用以组织ChildProcvectorChildProc child_processes;// 1.创建子进程CreatProcess(child_processes);// 2.父进程下发命令(用户交互式)OrderProcess(child_processes);// 3.进程退出WaitProcess(child_processes);cout 子进程已全部成功退出并被回收 endl;return 0;
}创建子进程 父进程循环创建子进程。每次子进程创建完毕后由于父进程尚且没有向管道写入数据当前子进程read阻塞等待父进程继续创建下一个子进程。父进程每次fork创建完一个子进程要将其描述为ChildProc结构体再插入管理的容器中。 const int child_process_num 3;void CreatProcess(vectorChildProc cps)
{for (int i 0; i child_process_num; i){// 1.创建管道int pipefd[2] {0};int ret pipe(pipefd);if (ret 0){perror(The following error happen:);}// 父进程写子进程读父进程向子进程发送命令// 2.创建子进程一个子进程在父进程中对应一个写端int id fork();assert(id 0);// 子进程if (id 0){// 3.关闭不要的fdclose(pipefd[1]);// 子进程接收并执行命令while (true){int n 0;// 此时管道为空时子进程read阻塞等待父进程下发命令int cnt read(pipefd[0], n, sizeof(int));if (cnt 0){//FuncArray在Tasks.hpp中实现FuncArray[n]();cout endl;} else if (cnt 0){//父进程退出即写端关闭read返回值为0子进程也随之退出cout 读取结束子进程退出 pid: getpid() endl;break;}else{exit(1);}}close(pipefd[0]);exit(0);}// 父进程// 将子进程(子进程pid和写端fd)管理起来父进程才方便下发命令cps.push_back(ChildProc(id, pipefd[1]));close(pipefd[0]);}
}在common.hpp头文件中简单写几个子进程可执行的任务这里没有定义实际任务只是打印语句以表示任务成功执行。后续这块可完善。 #pragma once
#include iostream
#include functional
using namespace std;void TaskWeChat()
{cout wechat is running... endl;
}void TaskChrome()
{cout chrome is running... endl;
}void TaskSteam()
{cout steam is running.. endl;
}const functionvoid() FuncArray[] {TaskWeChat,TaskChrome,TaskSteam};父进程下发命令给子进程 int SelectBoard()
{//用户选择面板cout ######################### endl;cout # 0.wechat 1.chrome # endl;cout # 2.steam 3.quit # endl;cout ######################### endl;cout 请选择你将下发的命令: ;int command 0;cin command;return command;
}void OrderProcess(vectorChildProc cps)
{int num -1;while (true){// 用户交互, 下发命令int command SelectBoard();if (command 3)break;if (command 0 || command 2)continue;// 轮询调用子进程num (num 1) % cps.size();printf(调用了子进程%d号, , num);cout cps[num]._proc_name endl;// 将命令写入对应子进程的管道中write(cps[num]._write_fd, command, sizeof(command));sleep(1);}
}等待子进程进程退出并回收 void WaitProcess(vectorChildProc cps)
{// 先关闭父进程的所有写端根据管道的特性(关闭管道所有写端读端退出)关闭写端让对应的子进程退出// 随后父进程要回收所有的子进程for (auto cp : cps){close(cp._write_fd);waitpid(cp._pid, nullptr, 0);}
}⭕运行程序并进行测试。发现让父进程发送0、1、2命令都正常可当发送3号退出命令让父进程等待并回收子进程时程序卡住了。 这里有一个隐藏的bug。匿名管道我们运用了子进程继承父进程文件描述符表的机制但在进程池中由于利用了这个继承机制又会产生bug。父进程创建0号子进程时是没问题的如我们预期。当创建1号子进程时由于此时父进程文件描述符表有了0号子进程的写端fd被1号子进程继承了所以此时0号子进程的管道有了两个写端fd这并不符合我们的预期我们的设计是让父进程和每个子进程之间有一个独立的管道。若创建三个子进程最后进程池的结构如下 再看看我们刚才写的WaitProcess函数。造成阻塞的原因是close关闭第一个子进程管道的写端时并没有关闭全部写端因此该子进程并没有退出waitpid阻塞等待。
void WaitProcess(vectorChildProc cps)
{for (auto cp : cps){close(cp._write_fd);waitpid(cp._pid, nullptr, 0);}
}解决方法 因为最后一个子进程只有父进程一个写端因此可以先关闭最后一个子进程的写端fd此时该子进程成功退出OS自动关闭其所有文件描述符因此它由于bug链接到其它子进程的管道上的写端fd会被关闭。如此逆向close即可完成。 这种进程池结构并不是我们想要的因此直接在创建子进程时关闭对应管道错误的写端fd形成我们期望的进程池结构才是上策。修改代码如下 void CreatProcess(vectorChildProc cps)
{//创建一个容器wfds用以存放父进程创建一个子进程时已经拥有的写端fdvectorint wfds;for (int i 0; i child_process_num; i){int pipefd[2] {0};int ret pipe(pipefd);if (ret 0){perror(The following error happen:);}// 每次创建管道后将写端fd存入wfdswfds.push_back(pipefd[1]);int id fork();assert(id 0);if (id 0){// 子进程关闭从父进程继承的所有写端包括子进程自己管道的和其它管道的写端fd!! for (auto wfd : wfds){close(wfd);}// 错误写法在当前子进程push写端fd其它子进程看不到写时拷贝问题// wfds.push_back(pipefd[1]);// for (auto wfd : wfds)// {// close(wfd);// cout 关闭fd: wfd endl;// }while (true){int n 0;int cnt read(pipefd[0], n, sizeof(int));if (cnt 0){ FuncArray[n]();cout endl;}else if (cnt 0){cout 读取结束子进程退出 pid: getpid() endl;break;}else{exit(1);}}close(pipefd[0]);exit(0);}cps.push_back(ChildProc(id, pipefd[1]));close(pipefd[0]);}
}此时再次发送quit指令观察到子进程成功退出并被父进程回收。 3. System V共享内存 另一种进程间通信的方式是共享内存。共享内存是最快的进程间通信IPC形式。因为其通信过程中传输数据时不再需要经过内核的“中转”而是直接通过地址的映射获得共享资源。 共享内存的概念
在进程间通信IPC中共享内存是一种特殊的通信机制允许多个进程共享同一块物理内存区域从而实现高效的数据交换和共享。与其他IPC方式相比共享内存的主要优势是数据直接存储在内存中避免了数据在进程之间的复制从而提高了通信的速度和效率。缺点是无法保证数据的安全性。
共享内存的结构 共享内存Shared Memory Segment简称shm,是一段由多个进程共享的物理内存空间各个进程将其通过页表映射到自己的地址空间共享区中。使得多个进程可以访问相同的空间实现交换数据完成IPC。图中struct_shm在真正的内核中并非这个名字是内核中用于管理共享内存的一个结构体每个共享内存对应一个该结构体该结构体中包含了共享内存区的各种属性和元数据如共享内存的大小、权限、关联进程等信息这些结构体也会被OS组织并管理起来。 共享内存 管理共享内存信息的数据结构 真正的共享内存空间
共享内存的使用
以下假设使用共享内存通信的只有两个进程实际上一个共享内存可以连接多个进程。 共享内存的获取 通信双方必须先能看到同一份共享资源才能进行通信。获取的方式是一方负责创建共享内存另一方查找对方创建的共享内存用到的接口是shmget。 NAMEshmget - allocates a System V shared memory segmentSYNOPSIS#include sys/ipc.h#include sys/shm.hint shmget(key_t key, size_t size, int shmflg);RETURN VALUEOn success, a valid shared memory identifier is returned. On error, -1 is returned, and errno is set to indicate the error.参数 key 用于标识唯一的一个共享内存段。多个进程约定同一个key可获取同一份共享内存。key是一个整型可以通过ftok函数获取 key_t ftok(const char *pathname, int proj_id);ftok的参数是一个路径字符串pathname和一个整型值项目idproj_id。内含特定的算法通过这两个参数生成一个重复率较低的key值并作为返回值。只要参数相同生成的key值就相同。 size 共享内存的大小单位是字节byte shmflg 标记位。主要的标记有IPC_CREAT和IPC_EXCL若shmflgIPC_CREAT表示若以key为键值的共享内存不存在创建之。若存在用之即可。若shmflgIPC_CREAT|IPC_EXCL表示若以key为键值的共享内存不存在创建之。若存在报错。IPC_EXCL不能单独使用只与IPC_CREAT一起使用。另外标记位还包含mode_flags它用于定义共享内存的权限格式与open的参数mode相同 指明onwer、group、world运行进程者对于共享内存的权限。 返回值 共享内存描述符shared memory identifier简称shmid用于标识唯一的一段共享内存。 参数key和返回值shmid的区别 key在函数调用时使用意味着共享内存可能尚未存在。key的作用是在进程获取共享内存之前此时共享内存可能还没创建唯一标识一个共享内存段使通信双方能够约定同一个共享内存段。这样一个进程创建以key为键值的shm另一个进程查找以key为键值的shm并获取相同的shmid。shmid用于进程获取共享内存后唯一标识一个共享内存段这个标识符可以用于后续的共享内存操作 。 二者作用大致相同但作用的时间节点不同。 进程与共享内存建立联系 上一步做的事只是让通信双方获知了用哪一块共享内存获取相同的shmid但并没有真正与共享内存建立联系。那么现在就要把进程和共享内存链接起来即在各自的地址空间中映射共享内存段。需要用到的接口是shmat。(shm attach) SYNOPSIS#include sys/types.h#include sys/shm.hvoid *shmat(int shmid, const void *shmaddr, int shmflg);
RETURN VALUEOn success shmat() returns the address of the attached shared memory segment; on error (void *) -1 is returned, and errno is set to indicate the cause of the error.参数 shmid 就是第一步中获得的shmid。 shmaddr 指定共享内存映射到当前进程的地址。一般设置为NULL由OS自动选择映射的地址较为安全可靠。 shmflg 指明链接共享内存的读写模式。设置SHM_RDONLY为只读 否则是即读又写一般设置为0。没有只写的选项。注意进程必须有对应权限才能设定对应的shmflg如设置SHM_RDONLY进程对该共享内存必须有读权限。设置为0进程对该共享内存必须有读权限和写权限。权限在shmget函数中设定。 返回值 一个void*类型的指针指向当前进程地址空间中映射共享内存段的起始地址后续该地址为shmaddr。 开始通信交换数据 不像管道需要调用系统接口写入和读取数据共享内存只需要在映射的地址空间中读写数据这段空间的起始地址在第二步已经获得直接当成数组的起始地址用就行。注意获得的指针shmaddr是void*类型不同场景下可能需要强转成其它类型来使用。 进程与共享内存解除联系 通信结束后通信双方无需再引用共享内存即可先解除与共享内存的联系。因为一个共享内存可能会被多对进程引用而不止一个所以只有当引用该共享内存的进程数量为0时才会删除这个共享内存。解除进程与共享内存的联系用到接口shmdtshm detach SYNOPSIS#include sys/types.h#include sys/shm.hint shmdt(const void *shmaddr);
RETURN VALUEOn success shmdt() returns 0; on error -1 is returned, and errno is set to indicate the cause of the error.传入shmaddr即可返回值无意义只是用作判断函数调用成功与否。 删除共享内存 NAMEshmctl - System V shared memory controlSYNOPSIS#include sys/ipc.h#include sys/shm.hint shmctl(int shmid, int cmd, struct shmid_ds *buf);参数 shmid 要删除的共享内存描述符 cmd 控制指令。删除的指令是IPC_RMID。 buf 用于接收其它指令的返回值。删除时传入NULL即可。
⭕注意进程间通信时创建和删除共享内存的工作最好由一个进程来完成其它进程只是与已创建的共享内存进行连接和断连即可。
除了系统调用还有一些关于共享内存的指令
ipcs -m #查看共享内存信息ipcrm [OPTION] [...] #删除共享内存
OPTION:-M 按key删除-m 按shmid删除代码实现 由于利用共享内存实现IPC时总是有相似的前置工作创建和连接和后置工作断连和删除因此可以将其封装在一个类中将前置工作封装在类的构造函数中后置工作封装在类的析构函数中实现共享内存自动化搭建和销毁。如下代码 //头文件common.hpp
#pragma once
#include iostream
#include sys/types.h
#include sys/stat.h
#include sys/ipc.h
#include sys/shm.h
#include cstring
#include cerrno
#include unistd.h
#include stdlib.h
#include cassertconst std::string pathname .;
const int proj_id 666;
const int shm_size 4096;#define CREATER 0
#define USER 1class smart_init
{
public:smart_init(int type){// 获取共享内存assert(type CREATER || type USER);if (type CREATER)_shmid creatShm(getKey());else if (type USER)_shmid searchShm(getKey());_type type;// 与共享内存建立联系_shm_addr attachShm(_shmid);}~smart_init(){// 与共享内存断开联系detachShm(_shm_addr);if (_type CREATER){remoteShm(_shmid);}}void *get_shmaddr(){return _shm_addr;}private:key_t getKey();int creatShm(key_t k);int searchShm(key_t k);int getShm(key_t k, int flag);void *attachShm(int shmid);void detachShm(const void *shmaddr);void remoteShm(int shmid);private:int _type;int _shmid;void *_shm_addr;
};std::string toHex(int n)
{char buf[64] {0};snprintf(buf, sizeof(buf), 0x%x, n);return std::string(buf);
}key_t smart_init::getKey()
{key_t k ftok(pathname.c_str(), proj_id);if (k -1){std::cerr errno : strerror(errno) std::endl;exit(1);}return k;
}int smart_init::getShm(key_t k, int flag)
{int shmid shmget(k, shm_size, flag);if (shmid -1){std::cerr errno : strerror(errno) std::endl;exit(2);}return shmid;
}int smart_init::creatShm(key_t k)
{umask(0);return getShm(k, IPC_CREAT | IPC_EXCL | 0666);
}int smart_init::searchShm(key_t k)
{umask(0);return getShm(k, 0666);
}void *smart_init::attachShm(int shmid)
{void *shm_ptr shmat(shmid, nullptr, 0);if (shm_ptr (void *)-1){std::cerr errno : strerror(errno) std::endl;exit(3);}return shm_ptr;
}void smart_init::detachShm(const void *shmaddr)
{int ret shmdt(shmaddr);if (ret -1){std::cerr errno : strerror(errno) std::endl;exit(4);}
}void smart_init::remoteShm(int shmid)
{int ret shmctl(shmid, IPC_RMID, nullptr);if (ret -1){std::cerr errno : strerror(errno) std::endl;exit(5);}
}//进程A
#include common.hppint main()
{smart_init si(CREATER);char* shm_ptr (char*)si.get_shmaddr();//通信int cnt 0;const char* msg i am process A;strcpy(shm_ptr,msg);sleep(10);return 0;
}//进程B
#include common.hppint main()
{smart_init si(USER);//通信char* shm_ptr (char*)si.get_shmaddr();printf(message from A: %s\n,shm_ptr);return 0;
}ENDING…