Linux下共享内存编程(共享存储空间)

Linux下共享内存编程(共享存储空间)共享存储允许两个或多个进程共享一个给定的存储区 是进程间通信最快的一种方式 不要同时对共享存储空间进行写操作 通常 信号量用于同步共享存储访问 最简单的共享内存的使用流程 ftok 函数生成键值 shmget 函数创建共享内存空间 shmat 函数获取第一个可用共享内存空间的地址 shmdt 函数进行分离 对共享存储段操作结束时的步骤 并不是从系统中删除共享内存和结构 shm

共享存储允许两个或多个进程共享一个给定的存储区,是进程间通信最快的一种方式。

不要同时对共享存储空间进行写操作,通常,信号量用于同步共享存储访问。

最简单的共享内存的使用流程

①ftok函数生成键值

②shmget函数创建共享内存空间

③shmat函数获取第一个可用共享内存空间的地址

④shmdt函数进行分离(对共享存储段操作结束时的步骤,并不是从系统中删除共享内存和结构)

⑤shmctl函数进行删除共享存储空间

1.ftok函数生成键值

每一个共享存储段都有一个对应的键值(key)相关联(消息队列、信号量也同样需要)。

所需头文件:#include

函数原型 :key_t ftok(const char *path ,int id);

path为一个已存在的路径名

id为0~255之间的一个数值,代表项目ID,自己取

返回值:成功返回键值(相当于32位的int)。出错返回-1

例如:key_t key = ftok( “/tmp”, 66);

2.shmget函数创建共享存储空间并返回一个共享存储标识符

所需头文件:#include

函数原型: int shmget(key_t key, size_t size,int flag);

key为ftok生成的键值

size为共享内存的长度,以字节为单位

flag为所需要的操作和权限,可以用来创建一个共享存储空间并返回一个标识符或者获得一个共享标识符。

flag的值为IPC_CREAT:如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间,并返回一个共享存储标识符。如果存在,则直接返回共享存储标识符。

flag的值为 IPC_CREAT | IPC_EXCL:如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间,并返回一个共享存储标识符。如果存在,则产生错误。

返回值:成功返回共享存储ID;出错返回-1

例如:int id = shmget(key,4096,IPC_CREAT|IPC_EXCL|0666);创建一个大小为4096个字节的权限为0666(所有用户可读可写,具体查询linux权限相关内容)的共享存储空间,并返回一个整形共享存储标识符,如果key值已经存在有共享存储空间了,则出错返回-1。

     int id = shmget(key,4096,IPC_CREAT|0666);创建一个大小为4096个字节的权限为0666(所有用户可读可写,具体查询linux权限相关内容)的共享存储空间,并返回一个共享存储标识符,如果key值已经存在有共享存储空间了,则直接返回一个共享存储标识符。

3.shmat函数获取第一个可用共享内存空间的地址

所需头文件:#include

函数原型: void *shmat(int shmid, const void *addr, int flag);

shmid为shmget生成的共享存储标识符

addr指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置

flag为对数据的操作,如果指定为SHM_RDONLY则以只读方式连接此段,其他值为读写方式连接此段。

翻阅linux下shm.c文件得到#define SHM_RDONLY      010000  /* read-only access */

返回值:成功返回指向共享存储段的指针;错误返回-1(打印出指针的值为全F)

例如:char *addr  = shmat(id, NULL, 0);就会返回第一个可用的共享内存地址的指针的值给addr 

4.shmdt函数进行分离

当不需要对此共享内存进行操作时候,调用shmdt函数进行分离,不是删除此共享存储空间哟。

所需头文件:#include

函数原型: int shmdt(const void *addr);

addr为shmat函数返回的地址指针

返回值:成功返回0;错误返回-1

例如:int ret = shmdt(addr);

5.shmctl函数对共享内存进行控制

简单的操作就是删除共享存储空间了,也可以获取和改变共享内存的状态

所需头文件:#include

函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmid就是shmget函数返回的共享存储标识符

cmd有三个,常用删除共享内存的为IPC_RMID;IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中;IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内。(内核为每个共享存储段维护着一个结构,结构名为shmid_ds,这里就不讲啦,里面存放着共享内存的大小,pid,存放时间等一些参数)

buf就是结构体shmid_ds

返回值:成功返回0;错误返回-1

例如:int ret = shmctl(id, IPC_RMID,NULL);删除id号的共享存储空间

 

ps:在Linux下,比如你申请24字节大小的共享存储空间,系统还是会默认给你分配一页的大小,但你还是只能使用这一页上24字节的空间。使用getconf PAGE_SIZE 命令就能显示出一页的大小

Linux下共享内存编程(共享存储空间)

使用ipcs -m可以查看当前系统所有的共享内存空间信息

Linux下共享内存编程(共享存储空间)

如果你的程序创建了一个共享内存段,但没有销毁,可以使用命令ipcrm -m shmid命令删除共享内存段,不然程序再运行有可能出错。

Linux下共享内存编程(共享存储空间)

下面用一个代码例子来使用共享内存

我创建了一个结构体,想让结构体存入共享内存。写了两个程序,service和client,代码基本相同,不同就是service程序的开始创建共享内存。这两个程序是一个死循环,让你选择是存数据还是读数据还是销毁共享内存。代码写的不是很精致,主要是为了练共享内存,见谅哈。

command.c文件,构造想存入的结构体,和共享内存的操作函数

#include 
  
    #include 
   
     #include 
    
      #include 
     
       #include 
      
        #include "Command.h" int sharememory(int ipc_size,int flag) { int id; key_t key=ftok("/tmp",66); if(key < 0) { printf("get key error\n"); return -1; } id = shmget(key,ipc_size,flag); if(id < 0) { printf("get id error\n"); return -1; } return id; } int create_ipc(int ipc_size) { return sharememory(ipc_size,IPC_CREAT|IPC_EXCL|0666); } int get_ipc(int ipc_size) { return sharememory(ipc_size,IPC_CREAT|0666); } int destroy_sharememory(int id) { return shmctl(id,IPC_RMID,NULL); } 
       
      
     
    
  

command.h文件。好让service和client调用嘛,方便。

#define NAME_LEN 20 typedef struct { char name[NAME_LEN]; int age; }ckx; int sharememory(int ipc_size,int flag); int create_ipc(int ipc_size); int get_ipc(int ipc_size);

service.c文件。创建共享内存空间啦,读写等

#include 
  
    #include 
   
     #include 
    
      #include 
     
       #include 
      
        #include 
       
         #include "Command.h" int main() { int id=create_ipc(sizeof(ckx)); int i=0; ckx *p; if(id < 0) { printf("create sharememory error\n"); return 0; } id = 0; while(1) { printf("\n\n1.input data to sharememory\n2.get sharememory data\n\ 3.destroy sharememory\ninput select:"); scanf("%d",&i); if(i > 3 |i< 1) { printf("input error\n"); continue; } id = get_ipc(sizeof(ckx)); if(id < 0) { printf("get sharememory error\n"); break; } p = (ckx *)shmat(id,NULL,0); if(p < 0) { printf("get sharememory addr error\n"); p = NULL; break; } if(i == 1) { char name[NAME_LEN]; int age=0; printf("input name:"); fflush(stdin); getchar(); gets(name); printf("input age:"); scanf("%d",&age); strcpy(p->name,name); p->age = age; printf("write success\n"); if(shmdt(p) == -1) { printf("shmdt error\n"); } id = 0; } if(i == 2) { printf("name:%s \t age:%d\n",p->name,p->age); if(shmdt(p) == -1) { printf("shmdt error\n"); break; } id = 0; } if(i == 3) { if(shmdt(p) == -1) { printf("shmdt error\n"); break; } break; } } if(id !=0) { if(destroy_sharememory(id)<0) { printf("destroy error\n"); } } } 
        
       
      
     
    
  

client.c基本上就和service.c代码差不多啦,只是想体现共享内存嘛,service读写和client读写,观察现象,体现共享内存

#include 
  
    #include 
   
     #include 
    
      #include 
     
       #include 
      
        #include 
       
         #include "Command.h" int main() { int i=0; ckx *p; int id = 0; while(1) { printf("\n\n1.input data to sharememory\n2.get sharememory data\n\ 3.destroy sharememory\ninput select:"); scanf("%d",&i); if(i > 3 |i< 1) { printf("input error\n"); continue; } id = get_ipc(sizeof(ckx)); if(id < 0) { printf("get sharememory error\n"); break; } p = (ckx *)shmat(id,NULL,0); if(p < 0) { printf("get sharememory addr error\n"); p = NULL; break; } if(i == 1) { char name[NAME_LEN]; int age=0; fflush(stdin); getchar(); printf("input name:"); gets(name); printf("input age:"); scanf("%d",&age); strcpy(p->name,name); p->age = age; printf("write success\n"); if(shmdt(p) == -1) { printf("shmdt error\n"); } id = 0; } if(i == 2) { printf("name:%s \t age:%d\n",p->name,p->age); if(shmdt(p) == -1) { printf("shmdt error\n"); break; } id = 0; } if(i == 3) { if(shmdt(p) == -1) { printf("shmdt error\n"); break; } break; } } if(id !=0) { if(destroy_sharememory(id)<0) { printf("destroy error\n"); } } } 
        
       
      
     
    
  

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/230175.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午2:57
下一篇 2026年3月16日 下午2:58


相关推荐

  • oralce入门学习[通俗易懂]

    oralce入门学习[通俗易懂]oracle的认识sql数据库语言关键字distinct关键字null连接符||比较运算符排序单行函数字符函数数值函数日期函数转换函数通用函数条件表达式多行函数

    2022年7月2日
    31
  • drupal安装教程mysql_Drupal8 安装教程

    drupal安装教程mysql_Drupal8 安装教程安装Drupal8需要环境环境:UNIX/Linux,OSX,或者windowsweb服务器:Apache2,nginx,MicrosoftIIS等数据库:推荐数据库Mysql(5.5.3),MariaDB(5.5.20),PerconaServer(5.5.8),支持:PostgreSql(9.1.2),SQLite(3.6.8)开始安装我的环境,OSX,Apache2,Mysql(…

    2022年6月6日
    41
  • 敏捷开发有哪些模式_软件敏捷开发方法的模式

    敏捷开发有哪些模式_软件敏捷开发方法的模式在信息技术高速发展的今天,有很多的开发任何要求开发人员增量交付,迭代式开发,能够持续集成。很显然传统的瀑布开发模式已经不能满足需要了,于是,敏捷开发这种模式就出现了。接触过敏捷开发的朋友可能会知道,

    2022年8月3日
    6
  • 什么意思_html5文字居中代码【转载】display:inline-block兼容ie6/7的写法

    2022年4月22日
    105
  • Jenkins详细教程

    Jenkins详细教程大纲  1.背景  在实际开发中,我们经常要一边开发一边测试,当然这里说的测试并不是程序员对自己代码的单元测试,而是同组程序员将代码提交后,由测试人员测试;  或者前后端分离后,经常会修改接口,然后重新部署;  这些情况都会涉及到频繁的打包部署;  手动打包常规步骤:  1.提交代码  2.问一下同组小伙伴有没有要提交的代码  3.拉取代码并打包(war包,或者jar包)  4.上传到Linux服务器  5.查看当前程序是否在运行  6.关闭当前程序  .

    2022年5月15日
    68
  • ipad分屏功能怎么使用_es6 reflect

    ipad分屏功能怎么使用_es6 reflectpadStart方法使用说明jspadStart是什么padStart()方法用另一个字符串填充当前字符串(如果需要的话,会重复多次),以便产生的字符串达到给定的长度。从当前字符串的左侧开始填充。实例’abc’.padStart(10);//”abc”‘abc’.padStart(10,”foo”);//”foofoofabc”‘abc’.padStart(6,”123465″);//”123abc”‘abc’.padStart(8,”0″)

    2025年10月9日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号