首页 / 资讯中心 / 文章详情

Linux系统——进程间通信之system V

Linux系统——进程间通信之system V ★ FEATURED ARTICLE
‍♂️个人主页进击的荆棘作者其它专栏《数据结构与算法》《算法》《C起始之路》《Linux》目录1.进程间通信介绍2.管道3.匿名管道4.命名管道5.system V共享内存6.system V消息队列7.system V信息量8.内核是如何组织管理IPC资源的5.system V共享内存共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间这些进程间数据传递不再涉及到内核即进程不再通过执行进入内核的系统调用来传递彼此的数据。5.1共享内存示意图5.2共享内存数据结构truct 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 currentattaches */ unsigned short shm_unused; /* compatibility */ void shm_unused2; / ditto - used byDIPC */ void shm_unused3; / unused */ };5.3共享内存函数shmget函数功能⽤来创建共享内存 原型 int shmget(key_t key, size_t size, int shmflg); 参数 key:这个共享内存段名字 size:共享内存⼤⼩ shmflg:由九个权限标志构成它们的⽤法和创建⽂件时使⽤的mode模式标志是⼀样的 取值为IPC_CREAT共享内存不存在创建并返回共享内存已存在获取并返回。 取值为IPC_CREAT | IPC_EXCL共享内存不存在创建并返回共享内存已存在 出错返回。 返回值成功返回⼀个⾮负整数即该共享内存段的标识码失败返回-1shmat函数功能将共享内存段连接到进程地址空间 原型 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)shmflgSHM_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实例1-共享内存实现进程间通信Makefile.PHONY:all all:server client server:server.cc g -o $ $^ client:client.cc g -o $ $^ .PHONY:clean clean: rm -f server clientcomm.h#ifndef _COMM_H_ #define _COMM_H_ # include stdio.h # include sys/types.h # include sys/ipc.h # include sys/shm.h # define PATHNAME . # define PROJ_ID 0x6666 int createShm(int size); int destroyShm(int shmid); int getShm(int size); # endifcomm.c#include comm.h static int commShm(int size, int flags) { key_t key ftok(PATHNAME, PROJ_ID); if(key 0){ perror(ftok); return -1; } int shmid 0; if( (shmid shmget(key, size, flags)) 0){ perror(shmget); return -2; } return shmid; } int destroyShm(int shmid) { if(shmctl(shmid, IPC_RMID, NULL) 0){ perror(shmctl); return -1; } return 0; } int createShm(int size) { return commShm(size, IPC_CREAT|IPC_EXCL|0666); } int getShm(int size) { return commShm(size, IPC_CREAT); }server.cc#include comm.h int main() { int shmid createShm(4096); char *addr shmat(shmid, NULL, 0); sleep(2); int i 0; while(i26){ printf(client# %s\n, addr); sleep(1); } shmdt(addr); sleep(2); destroyShm(shmid); return 0; }client.cc#include comm.h int main() { int shmid getShm(4096); sleep(1); char *addr shmat(shmid, NULL, 0); sleep(2); int i 0; while(i26){ addr[i] Ai; i; addr[i] 0; sleep(1); } shmdt(addr); sleep(2); return 0; }运行结果ctrlc终止进程再次启动# ./server shmget: File exists # ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x66026a25 688145 root 666 4096 0 # ipcrm -m 688145 #删除shm ipc资源注意不是必须通过⼿动来删除这⾥只为演⽰相关指 令删除IPC资源是进程该做的事情注:共享内存没有进行同步与互斥。共享内存缺乏访问控制会带来并发问题实例2-借助管道实现访问控制版的进程通信comm.hpp#pragma once #include cstdio #include cstdlib #define ERR_EXIT(n) \ do{ \ perror(n); \ exit(EXIT_FAILURE); \ }while(0) \fifo.hpp#pragma once #include iostream #include cstdio #include sys/types.h #include sys/stat.h #include unistd.h #include fcntl.h #include string #include Comm.hpp #define PATH . #define FIFONAME fifo class FileFifo{ public: FileFifo(const std::string path,const std::string name) :_path(path),_name(name) { _pathname_path/_name; umask(0); //创建管道文件 int nmkfifo(_pathname.c_str(),0666); if(n0){ std::cerrmkfifo errorstd::endl; ERR_EXIT(mkfifo); } } ~FileFifo(){ //删除管道文件 int nunlink(_pathname.c_str()); if(n0){ std::coutunlink successstd::endl; } else{ std::cerrremove fifo failedstd::endl; ERR_EXIT(unlink); } } private: std::string _path; std::string _name; std::string _pathname; }; class FifoOper{ public: FifoOper(const std::string path,const std::string name) :_path(path),_name(name),_fd(-1) { _pathname_path/_name; } void OpenForRead(){ //打开文件 //当write没有执行open时read会在open内部进行阻塞 //直到有人把管道文件打开了open才会返回 _fdopen(_pathname.c_str(),O_RDONLY); if(_fd0){ std::cerropen mkfifo failedstd::endl; ERR_EXIT(open); } std::coutopen fifo successstd::endl; } void OpenForWrite(){ //打开文件 _fdopen(_pathname.c_str(),O_WRONLY); if(_fd0){ std::cerropen mkfifo failedstd::endl; ERR_EXIT(open); } std::coutopen fifo successstd::endl; } bool Wait(){ //读取文件内容 char c; int numberread(_fd,c,1); if(number0){ printf(醒来:%d\n,number); return true; } return false; } void WakeUp(){ //向文件写入内容 char cc; int nwrite(_fd,c,1); printf(尝试唤醒:%d\n,n); } void Close(){ if(_fd0) close(_fd); } ~FifoOper(){} private: std::string _path; std::string _name; std::string _pathname; int _fd; };shm.hpp#pragma once #include iostream #include sys/shm.h #include sys/ipc.h #include Comm.hpp const int gdefult -1; const int gsize 4096; const std::string pathname .; const int projid 0x66; const int gmode 0666; #define CREATER creater #define USER user class Shm { private: // 不同的用户需要不同的权限比如CREATER我希望它有创建shm和读的权限但不希望它有写的权限 // 所以让一些方法私有禁止用户直接访问 void CreateHelper(int flg) { printf(key is 0x%x\n, _key); // 需要给读写执行权限否则创建好共享内存后可能无法读写等 _shmid shmget(_key, _size, flg); if (_shmid 0) { ERR_EXIT(shmget); } printf(shemget success,id:%d\n, _shmid); } void Creat() { CreateHelper(IPC_CREAT | IPC_EXCL | gmode); } void Get() { CreateHelper(IPC_CREAT); } void Attch() { _start_mm shmat(_shmid, nullptr, 0); if ((long long)_start_mm 0) { ERR_EXIT(shmat); } std::coutattach successstd::endl; } void Detach() { int n shmdt(_start_mm); if (n 0) { ERR_EXIT(shmdt); } std::cout detach success std::endl; } void Destory() { Detach(); if (_usertype CREATER) { int n shmctl(_shmid, IPC_RMID, nullptr); if (n 0) { ERR_EXIT(shmctl); } std::cout delete shm:_shmid successful std::endl; } } public: Shm(const std::string pathname,int projid,const std::string usertype) : _size(gsize), _shmid(gdefult), _start_mm(nullptr), _usertype(usertype) { _key ftok(pathname.c_str(), projid); if (_key 0) { ERR_EXIT(ftok); } if (_usertype CREATER) { Creat(); } else if (_usertype USER) { Get(); } else { } Attch(); } void *VirtualAddr() { printf(virtual addr:%p\n,_start_mm); return _start_mm; } //输出共享内存的各种属性 void Attr() { struct shmid_ds ds; int nshmctl(_shmid,IPC_STAT,ds); printf(shm_segsz:%ld\n,ds.shm_segsz); printf(key:0x%x\n,ds.shm_perm.__key); } int Size() { return _size; } ~Shm() { Destory(); } private: int _size; key_t _key; int _shmid; void *_start_mm; std::string _usertype; };server.cc#include fifo.hpp #include shm.hpp int main(){ Shm sh(pathname,projid,CREATER); sh.Attr(); //因为虚拟内存没有保护机制它写的数据会直接被读出 //所以我们使用管道来保护它的数据完整性 FileFifo fifo(PATH,FIFONAME); FifoOper readerfile(PATH,FIFONAME); readerfile.OpenForRead(); //读写共享内存不用使用系统调用使用它就像使用我们从堆栈上申请的一样 char *mem(char*)sh.VirtualAddr(); while(true){ if(readerfile.Wait()){ printf(%s\n,mem); } else break; } readerfile.Close(); return 0; }client.cc#include fifo.hpp #include shm.hpp int main(){ FifoOper writerfile(PATH,FIFONAME); writerfile.OpenForWrite(); Shm sh(pathname,projid,USER); char * mem(char*)sh.VirtualAddr(); int index0; for(char cA;cZ;c){ sleep(1); mem[index]c; mem[index1]c; sleep(1); mem[index2]0; index2; writerfile.WakeUp(); } writerfile.Close(); return 0; }6.system V消息队列●消息队列提供了一个从一个进程向另一个进程发送一块数据的方法●每个数据块都被认为有一个类型接收者进程接收的数据块可以有不同的类型值●特性▢IPC资源必须删除否则不会自动清除除非重启因为system V IPC资源的生命周期随内核7.system V信号量信号量主要用于同步和互斥7.1并发编程-前导●多个执行流进程能看到的同一份公共资源共享资源●被保护起来的共享资源叫做临界资源●保护的方式常见互斥和同步●任何时刻只允许一个执行流访问资源叫做互斥●多个执行流访问临界资源的时候具有一定的顺序性就叫做同步●系统中某些资源一次只允许一个进程使用称这样的资源为临界资源或互斥资源●在进程中涉及到互斥资源的程序段叫临界区。我们写的代码访问临界区资源的代码临界区不访问临界区资源的代码非临界区●所谓对共享资源进行保护本质是对访问共享资源的代码进行保护约束7.2信号量特性●IPC资源必须删除否则不会自动清除除非重启因为system V IPC资源的生命周期随内核理解●信号量是一个计数器作用●保护临界区本质●信号量本质是对资源的预定机制操作●申请资源计数器--P操作●释放资源计数器V操作8.内核是如何组织管理IPC资源的●参考Linux2.6.18源码
阅读完成 · 觉得有帮助?
咨询建站