• systemV的工作原理+原理代码


    概念

    我们知道进程间的通信有管道的方式进程通信管道制作_云的小站的博客-CSDN博客

    但是我们的管道通信其实属于一种取巧的方式,利用了打开的文件可读写的特性上,两个进程对此分别进行读写操作就会产生所谓的通信现象,但是外面的管道依旧得再磁盘上创建一个文件,然后文件打开加载在内存上才可以使用,那么有什么方法可以直接加载在内存,而无需对磁盘操作呢?

    ---------利用systemV共享内存通信

    利用系统函数shmget在内存中开辟一块空间用于进程通信的交互点。因为两个进程之间不可以直接通信,所以需要加一个中间层。

     

    学计算机,当两个东西不通的时候,我们最经常干的事情就是加一个中间层,既保护了独立性,又处理了我们需要完成的事情

    工具

    shmget(创建共享内存)

    shmid_t shmget(key_t key, size_t size, int shmflg);
    

     返回值:

    失败返回-1,成功返回一个shmid唯一的共享内存标识符。

    key:

    唯一值,两个进程需要同一个key值才可以访问到同一共享内存空间。

    size:

    设置共享内存大小

    shmflg

    共享内存创建选项,IPC_CREAT | IPC_EXCL|0666 

    IPC_CREAT:如果内存存在key标识符的共享内存直接使用,如果不存在就创建

    IPC_EXCL:如果内存存在key标识符的共享内存直接失败退出。

    0666:共享内存的使用权限。

    shmctl(操作共享内存)

    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    

    返回值:

    如果操作成功返回0,操作失败返回-1;

    shmid:

    对该shmid的共享内存进行操作;

    cmd:

    对共享内存的操作选项,一般我们删除共享内存用的使用:IPC_RMID

    buf:

    获取该共享内存的数据结构地址。(共享内存也需要被描述组织)。

    shmat(进程的挂接操作)

    void *shmat(int shmid, const void *shmaddr, int shmflg);

    返回值:

    成功返回共享内存的地址,失败返回-1,并设置errno错误原因。

    shmid:

    挂接在shmid标识符的共享内存上。

    shmaddr:

    理论上可以自己设置将共享内存挂接在自己指定的位置,但是不建议。所以一般设置为nullptr,

    shmflg

    设置访问方式只读、只写等等。设置为0,读写操作。

    shmdt(进程的拆除操作)

    int shmdt(const void *shmaddr);

    返回值:

    成功拆除返回0,失败返回-1;

    shmaddr:

    将进程地址空间shmaddr的共享内存,进行拆除。

    ftok(生成一个key值为shmget服务)

    key_t ftok(const char *pathname, int proj_id);

    返回值:

    返回有个随机数字。

    pathname:

    传一个文件路径,使用文件的inode值,对了文件必须要有访问权限。

    proj_id:

    项目id,0~255,其实可以随便写,会截断。

    ps:ftok不进行系统调用,他只是一个算法

    代码

    来来来,工具说完了直接上代码:

    头文件

    日志+需要的接口

    日志

    1. 1 #ifndef _LOG_H_
    2. 2 #define _LOG_H_
    3. 3 #include
    4. 4 #include
    5. 5
    6. 6 #define Debug 0
    7. 7 #define Notice 1
    8. 8 #define Warning 2
    9. 9 #define Error 3
    10. 10
    11. 11 std::string mgs[] = {
    12. 12 "Debug",
    13. 13 "Notice",
    14. 14 "Warning",
    15. 15 "Error"};
    16. 16
    17. 17 std::ostream &Log(std::string message, int level)
    18. 18 {
    19. 19 std::cout << " | " << (unsigned)time(nullptr)
    20. 20 << " | " << mgs[level] << " | "<
    21. 21 return std::cout;
    22. 22 }
    23. 23
    24. 24 #endif
    25. ~

    所需头文件

    1. 1 #pragma once
    2. 2 #include
    3. 3 #include
    4. 4 #include
    5. 5 #include
    6. 6 #include
    7. 7 #include"Log.hpp"
    8. 8 #include
    9. 9 #include
    10. 10 using namespace std;
    11. 11 #define SIZE 4096
    12. 12 #define PATH_NAME "."
    13. 13 #define PROJ_ID 0x66
    14. 14
    15. 15
    16. 16 string TransToHex(key_t k)
    17. 17 {
    18. 18 char buffer[32];
    19. 19 snprintf(buffer,sizeof(buffer),"0x%x",k);
    20. 20 return buffer;
    21. 21 }

    接收方代码

    1. #include"comm.hpp"
    2. 2
    3. 3 int main()
    4. 4 {
    5. 5 // 1.创建公共key
    6. 6 key_t k=ftok(PATH_NAME,PROJ_ID);
    7. 7 assert(k!=-1);
    8. 8 Log("create key done",Debug)<<" sgmrevice key: "<< TransToHex(k)<
    9. 9 sleep(10);
    10. 10 //2.创建共享内存
    11. 11
    12. 12 int shmid = shmget(k,SIZE,IPC_CREAT | IPC_EXCL|0666 );
    13. 13 if(shmid==-1)
    14. 14 {
    15. 15 perror("shmger fail!!\n");
    16. 16 exit(-1);
    17. 17 }
    18. 18
    19. 19 Log("create shm done",Debug)<<" sgmrevice shmid: "<
    20. 20
    21. 21
    22. 22 sleep(10);
    23. 23 //挂接内存
    24. 24 char*shmaddr=(char*)shmat(shmid,nullptr,0);
    25. 25 if(*shmaddr==-1)
    26. 26 {
    27. 27 perror("shmat fail!\n");
    28. 28 }
    29. 29 Log("shmat shm seccese",Debug)<<" sgmrevice shmid: "<
    30. 30 sleep(10);
    31. 31
    32. 32 //工作
    33. 33
    34. 34 //拆除链接
    35. 35 int n=shmdt(shmaddr);
    36. 36 assert(n!=-1);
    37. 37 (void)n;
    38. 38 Log("shmdt shm done",Debug)<<" sgmrevice shmid: "<
    39. 39
    40. 40 sleep(10);
    41. 41 // n.删除共享内存
    42. 42 n=shmctl(shmid,IPC_RMID,nullptr);
    43. 43 assert(n!=-1);
    44. 44 (void)n;
    45. 45 Log("delete shm done",Debug)<<" sgmrevice shmid: "<
    46. 46
    47. 47 return 0;
    48. 48 }
    49. ~

    发送方代码

    1. 1 #include"comm.hpp"
    2. 2 int main()
    3. 3 {
    4. 4 //获取k值
    5. 5 key_t k=ftok(PATH_NAME,PROJ_ID);
    6. 6 assert(k!=-1);
    7. 7 (void)k;
    8. 8 Log("create key done 郑建云",Debug)<<"key:"<<TransToHex(k)<
    9. 9 sleep(10);
    10. 10 //创建共享内存
    11. 11 //由于在receive已经创建共享内存,所以我们主要是找到内存挂接
    12. 12 //IPC_CREAT==0
    13. 13 int shmid = shmget(k,SIZE,IPC_CREAT);
    14. 14 if(!shmid)
    15. 15 {
    16. 16 perror("shmget fail!!\n");
    17. 17 exit(-1);
    18. 18 }
    19. 19
    20. 20 Log("create shm domw",Debug)<<" shmid: "<
    21. 21 int cnt=10;
    22. 22 while(cnt)
    23. 23 {
    24. 24 printf("time:%d\r",cnt--);
    25. 25 sleep(1);
    26. 26 fflush(stdout);
    27. 27 }
    28. 28 printf("\n");
    29. 29 //挂接,shmaddr,不要自己写,避免把其他数据覆盖
    30. 30 char*shmaddr=(char*)shmat(shmid,nullptr,0);
    31. 31 if(shmaddr==nullptr)
    32. 32 {
    33. 33 perror("shmat fail\n");
    34. 34 exit(-1);
    35. 35 }
    36. 36 Log("shmat sueecss done",Debug)<<"shmid: "<
    37. 37 sleep(10);
    38. 38
    39. 39 //工作
    40. 40
    41. 41 //拆除共享内存
    42. 42 int n=shmdt(shmaddr);
    43. 43 printf("n=%d\n",n);
    44. 44 assert(n==0);
    45. 45 (void)n;
    46. 46 Log("shmdt success done",Debug)<<" shmid "<
    47. 47 printf("%p\n",shmaddr);
    48. 48 return 0;
    49. 49 }

    执行顺序必须先执行接收代码,因为接收代码必须先创建好共享内存,然后发送方才可以链接。

    否者,发送方先建立了,在执行中接收方将检查出内存中已经存在该key的共享内存,直接报错,返回一个-1到shmid。

  • 相关阅读:
    450-深信服面经1
    远程服务器配置 Anaconda 并安装 PyTorch 详细教程
    抖音短视频实操:矩阵号之为什么要做矩阵号和如何做矩阵号(中)
    面试突击77:Spring 依赖注入有几种?各有什么优缺点?
    JavaScript的forEach循环和作用域
    你的服务器还安全吗?用户数据是否面临泄露风险?
    二阶段day1
    6234. 最小公倍数为 K 的子数组数目
    【SpringMVC】RESTFul风格设计和实战 第三期
    [2022 强网杯] house_of_cat 战战兢兢的复现
  • 原文地址:https://blog.csdn.net/2201_75352618/article/details/131138733