• 2、【Linux系统编程】共享内存


    共享内存的本质:

      多个进程访问同一个逻辑内存

      字节访问内存,不用read()/write()非常方便

    1. POSIX 共享内存

    1.1分类

    (1)分类

    注意:共享内存的大小 = 文件大小

    (2)匿名内存映射(亲缘进程)

    (3)共享内存区对象(非亲缘进程)

    1.2 接口

      头文件: #include <sys/mman.h> 

      库: rt

    1.3函数

      POSIX 共享内存有5个函数。

     

      此外,在使用内存映射文件,经常要用到以下两个文件操作函数。

     

    获取文件信息:

    int fstat(int fd,struct stat *buf)

    参数:

    1 fd: 文件描述符
    2   buf:struct stat
    3   struct stat
    4     st_mode:  权限
    5     st_size:     大小
    6     st_uid:      属性ID
    7     st_guid:   组ID

    返回值:

      出错: -1     成功: 0

    实例:

      获取共享内存的大小

    1     stat(fd, &stat)
    2     stat.st_size

    【示例】

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <string.h>
     4 #include <sys/stat.h>
     5 #include <sys/types.h>
     6 #include <fcntl.h>
     7 #include <iostream>
     8 using namespace std;
     9 //命令行程序,获取一个文件的大小
    10 int main(int argc, char * argv[])
    11 {
    12     int fd = open(argv[1], O_RDONLY, 0644);//打开一个文件
    13 
    14     struct stat file_stat;
    15   //获取文件信息
    16     fstat(fd, &file_stat);
    17 
    18     cout << "file : " << argv[1] << "  size : " << file_stat.st_size << endl;
    19 
    20     return 0;
    21 }

    修改文件大小:

    int ftruncate(int fd, off_t  length)

    参数:

      fd: 文件描述符

      length:文件大小,如果原来的文件大小比参数length大, 超过的部分将会被删除

    返回值:

      成功: 0    失败: -1

    实例:

    设置共享内存的大小:

    ftruncate(fd,len)

    【示例】

      命令行程序, 实现对文件的大小缩放

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <string.h>
     5 #include <sys/stat.h>
     6 #include <sys/types.h>
     7 #include <fcntl.h>
     8 #include <iostream>
     9 using namespace std;
    10 
    11 int main(int argc, char * argv[])
    12 {
    13     int fd = open(argv[1], O_RDWR, 0644);//打开一个文件
    14   
    15     struct stat file_stat;
    16   //获取文件信息
    17     fstat(fd, &file_stat);
    18     cout << "file : " << argv[1] << " size : " << file_stat.st_size << endl;
    19   //定义新的文件大小
    20     int new_size= atoi(argv[2]);
    21     ftruncate(fd, new_size);//更新文件大小
    22     fstat(fd, &file_stat);
    23     cout << "new size : " << file_stat.st_size << endl;
    24 
    25     return 0;
    26 }

    问题

    1. 文件扩大后,新增文件内容是什么?
    2. 文件缩小后,文件内容是否会被截取?
    3. 文件大小是否可以变为0?

     1.3.1 创建

    int shm_open(const char *name, int oflag, mode_t mode)

    参数:

      name:POSIX IPC名字,格式为/somename

      oflag: 标志

      mode:权限

    返回值:

      出错: -1    共享内存描述符:其他

     【示例】

     1 //共享内存的创建
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <unistd.h>
     5 #include <sys/mman.h>
     6 #include <sys/types.h>
     7 #include <sys/stat.h>
     8 #include <fcntl.h>
     9 #include <string.h>
    10 #include <iostream>
    11 using namespace std;
    12 
    13 int main(int argc, char * argv[])
    14 {
    15     int fd = shm_open(argv[1], O_CREAT | O_RDWR, 0644);
    16 
    17     ftruncate(fd, atoi(argv[2]));
    18 
    19     void * buf = NULL;
    20     if((buf = mmap(NULL, BUFSIZ, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)
    21     {
    22         perror("mmap error
    ");
    23         return 1;
    24     }
    25     return 0;
    26 }

    1.3.2 删除

    int shm_unlink(const char *name)

    参数:

      name: POSIX IPC名字

    返回值:

      成功: 0   出错: -1

    【示例】

     1 //共享内存的删除
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <unistd.h>
     5 #include <sys/mman.h>
     6 #include <sys/types.h>
     7 #include <sys/stat.h>
     8 #include <fcntl.h>
     9 #include <string.h>
    10 #include <iostream>
    11 using namespace std;
    12 
    13 int main(int argc, char * argv[])
    14 {
    15     shm_unlink(argv[1]);
    16     return 0;
    17 }

    1.3.3 建立内存映射

    1 void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset)

    参数:

    内存保护标志:

    映射对象的类型:

    返回值:

    【示例】

    写共享内存

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/mman.h>
     5 #include <sys/stat.h>
     6 #include <fcntl.h>
     7 #include <string.h>
     8 #include <iostream>
     9 
    10 using namespace std;
    11 
    12 int main(int argc, char * argv[])
    13 {
    14     int fd = shm_open(argv[1], O_RDWR, 0);
    15     void * buf = NULL;
    16 
    17     if((buf = mmap(NULL, BUFSIZ, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)
    18     {
    19         perror("mmap error 
    ");
    20         return -1;
    21     }
    22     memcpy(buf, argv[2]);
    23     munmap(buf, BUFSIZ);
    24 
    25     return 0;
    26 }

    读共享内存

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/mman.h>
     5 #include <sys/stat.h>
     6 #include <fcntl.h>
     7 #include <string.h>
     8 #include <iostream>
     9 
    10 using namespace std;
    11 
    12 int main(int argc, char * argv[])
    13 {
    14     int fd = shm_open(argv[1], O_RDONLY, 0);
    15     void * buf = NULL;
    16 
    17     if((buf = mmap(NULL, BUFSIZ, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)
    18     {
    19         perror("mmap error 
    ");
    20         return -1;
    21     }
    22     cout << buf << endl;
    23     munmap(buf, BUFSIZ);
    24 
    25     return 0;
    26 }

    1.3.4 关闭内存映射

    int munmap(void *start,size_t length)

    参数:

      start:映射内存起始地址

      length:内存大小

    返回值:

      成功: 0  失败:-1

     注意
    关闭mmap中的文件描述符不能删除内存映射。

     1.4 示例

    (1)内存映射文件

    创建文件并且写入数据

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/mman.h>
     5 #include <sys/stat.h>
     6 #include <fcntl.h>
     7 #include <string.h>
     8 #include <iostream>
     9 
    10 using namespace std;
    11 int main()
    12 {
    13     int fd = open("./mmap.txt",O_CREAT|O_RDWR,0644);
    14     char str[] = "hello mmap
    ";
    15     ftruncate(fd,sizeof(str));//改变共享内存大小
    16     void* buf = NULL;
    17     struct stat file_stat;
    18     fstat(fd, &file_stat);//获取文件大小信息
    19     if(( buf = mmap(NULL,sizeof(str),PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED)
    20     {
    21         perror("mmap error
    ");
    22         return 1;
    23     }
    24     memcpy(buf, str, file_stat.st_size);
    25     munmap(buf,sizeof(str));//关闭内存映射
    26     close(fd);
    27 }

    读取数据并且重新写入数据

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/mman.h>
     5 #include <sys/stat.h>
     6 #include <fcntl.h>
     7 #include <string.h>
     8 #include <iostream>
     9 
    10 using namespace std;
    11 
    12 int main(int argc,char* argv[])
    13 { 
    14     int fd = open("./mmap.txt",O_RDWR); 
    15     void* buf = NULL; 
    16     struct stat file_stat;
    17     fstat(fd, &file_stat);
    18     if(( buf = mmap(NULL,BUFSIZ,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED)
    19     { 
    20         perror("mmap error
    "); 
    21         return 1; 
    22     } 
    23     cout << buf << endl;
    24     memcpy(buf, "this sdfdsfdsfdsfdsfdsfdsfdsfdsfdsf
    ", file_stat.st_size); 
    25     munmap(buf,BUFSIZ); 
    26     close(fd); 
    27 }

    亲缘进程读写数据

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/mman.h>
     5 #include <sys/stat.h>
     6 #include <fcntl.h>
     7 #include <string.h>
     8 #include <iostream>
     9 
    10 using namespace std;
    11 
    12 int main(int argc,char* argv[])
    13 {
    14     int fd = open("./mmap.txt",O_CREAT|O_RDWR,0644);
    15     void* buf = NULL;
    16     struct stat file_stat;
    17     fstat(fd, &file_stat);
    18     if(( buf = mmap(NULL,BUFSIZ,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED)
    19     {
    20         perror("mmap error
    ");
    21         return 1;
    22     }
    23     if(fork())
    24     {
    25         memcpy(buf,argv[1],file_stat.st_size);
    26     }
    27      else
    28     {
    29          printf("%s
    ",buf);
    30     }
    31     munmap(buf,BUFSIZ);
    32     close(fd);
    33 }

    非亲缘进程读写进程

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/mman.h>
     5 #include <sys/stat.h>
     6 #include <fcntl.h>
     7 #include <string.h>
     8 #include <iostream>
     9 
    10 using namespace std;
    11 
    12 int main(int argc,char* argv[])
    13 {
    14     int fd = open("./mmap.txt",O_CREAT|O_RDWR,0644);
    15     void* buf = NULL;
    16     struct stat file_stat;
    17     fstat(fd, &file_stat);
    18     if(( buf = mmap(NULL,BUFSIZ,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED)
    19     {
    20         perror("mmap error
    ");
    21         return 1;
    22     }
    23     memcpy(buf,argv[1],file_stat.st_size);
    24     munmap(buf,BUFSIZ);
    25     close(fd);
    26 }

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/mman.h>
     5 #include <fcntl.h>
     6 #include <string.h>
     7 #include <iostream>
     8 
     9 using namespace std;
    10 
    11 int main(int argc,char* argv[])
    12 {
    13     int fd = open("./mmap.txt",O_CREAT|O_RDWR,0644);
    14     void* buf = NULL;
    15     if(( buf = mmap(NULL,BUFSIZ,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED)
    16     {
    17         perror("mmap error
    ");
    18         return 1;
    19     }
    20     cout << buf << endl;
    21     munmap(buf,BUFSIZ);
    22     close(fd);
    23 }

    (2)匿名内存映射(亲缘进程)

    Systerm V风格

     1 //Systerm V风格
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <unistd.h>
     5 #include <sys/mman.h>
     6 #include <fcntl.h>
     7 #include <string.h>
     8 #include <iostream>
     9 
    10 using namespace std;
    11 
    12 int main(int argc,char* argv[])
    13 { 
    14     int fd = open("/dev/zero",O_RDWR); 
    15     void* buf = NULL; 
    16     struct stat file_stat;
    17     fstat(fd, &file_stat);
    18     if(( buf = mmap(NULL,BUFSIZ,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED)
    19     { 
    20         perror("mmap error
    "); 
    21         return 1; 
    22     }
    23     if(fork())
    24     { 
    25         memcpy(buf,argv[1],file_stat.st_size); 
    26     }
    27     else
    28     { 
    29         printf("%s
    ",buf); 
    30     } 
    31     munmap(buf,BUFSIZ); 
    32     close(fd); 
    33 }

    BSD风格

     1 //BSD风格
     2 //Systerm V风格
     3 #include <stdio.h>
     4 #include <stdlib.h>
     5 #include <unistd.h>
     6 #include <sys/mman.h>
     7 #include <fcntl.h>
     8 #include <string.h>
     9 #include <iostream>
    10 
    11 using namespace std;
    12 
    13 int main(int argc,char* argv[])
    14 { 
    15     int fd = open("/dev/zero",O_RDWR); 
    16     void* buf = NULL; 
    17     struct stat file_stat;
    18     fstat(fd, &file_stat);
    19     if(( buf = mmap(NULL,BUFSIZ,PROT_WRITE|PROT_READ,MAP_SHARED|MAP_ANON,-1,0)) == MAP_FAILED
    20     { 
    21         perror("mmap error
    "); 
    22         return 1; 
    23     }
    24     if(fork())
    25     { 
    26         memcpy(buf,argv[1],file_stat.st_size); 
    27     }
    28     else
    29     { 
    30         printf("%s
    ",buf); 
    31     } 
    32     munmap(buf,BUFSIZ); 
    33     close(fd); 
    34 }

    非亲缘进程

     1 //Systerm V风格
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <unistd.h>
     5 #include <sys/mman.h>
     6 #include <sys/stat.h>
     7 #include <fcntl.h>
     8 #include <string.h>
     9 #include <iostream>
    10 
    11 using namespace std;
    12 
    13 int main(int argc,char* argv[])
    14 {
    15     int fd = open(argv[1],O_RDWR);
    16     void* buf = NULL;
    17     struct stat file_stat;
    18     fstat(fd, &file_stat);
    19     if(( buf = mmap(NULL,BUFSIZ,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED)
    20     {
    21         perror("mmap error
    ");
    22         return 1;
    23     }
    24     int i;
    25     for(i = 2; i < argc; i++)
    26     {
    27         memcpy(buf, argv[1], file_stat.st_size);
    28         sleep(3);
    29     }
    30 
    31     munmap(buf,BUFSIZ);
    32     close(fd);
    33 }

     1 //Systerm V风格
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <unistd.h>
     5 #include <sys/mman.h>
     6 #include <fcntl.h>
     7 #include <string.h>
     8 #include <iostream>
     9 
    10 using namespace std;
    11 
    12 int main(int argc,char* argv[])
    13 {
    14     int fd = open(argv[1],O_RDWR);
    15     void* buf = NULL;
    16     
    17     if(( buf = mmap(NULL,BUFSIZ,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED)
    18     {
    19         perror("mmap error
    ");
    20         return 1;
    21     }
    22     sleep(1);
    23     for(;;)
    24     {
    25         cout << buf << endl;
    26         sleep(3);
    27     }
    28 
    29     munmap(buf,BUFSIZ);
    30     close(fd);
    31 }

    3. 使用mmap容易出现的问题

    1. 现象:总线错误Bus Error
      原因:映射文件的大小为0
      解决:使用ftruncate()扩展文件的大小,stat.st_size
    2. 现象:段错误
      原因:munmap()释放内存的大小大于申请内存大小
      解决:释放内存大小与申请内存大小保持一直
    3. 现象:Permisstion denied
      原因:文件打开权限与文件映射对象访问权限不一致。
      解决:只读PROT_READ的文件映射对象使用O_RDONLY打开文件;读写PROT_READ|PROT_WRITE的文件映射对象使用O_RDWR打开文件
  • 相关阅读:
    第三节课 字符串拼接、格式化输出、深浅复制
    第四节课 集合、字典、运算符
    python-模块系列
    python正则表达式
    python第二天
    Python-第一天
    SQL SERVER 最近查询过的语句
    razor page 页面
    RAZOR显示表格数据
    邮件模板 C#
  • 原文地址:https://www.cnblogs.com/Long-w/p/9628696.html
Copyright © 2020-2023  润新知