使用共享内存实现一个进程写文件,两个进程读文件

主要功能:读取任意的文件,大小不限(不超过共享内存设置的大小,一般为8k,但可手动重新设置,这已经很大了。),执行后两个或多个读进程可同时读取该文件并在终端打印。

要实现该功能,首先你得了解共享内存的搭建,有四个函数,分别为shmget,shmat,shmdt,shmctl,他们的作用如下:

1、shmget函数
该函数用来创建共享内存,它的原型为:
[cpp]  view plain  copy
 print ?
  1. int shmget(key_t key, size_t size, int shmflg);  
第一个参数,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.

不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget函数的返回值),只有shmget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。

第二个参数,size以字节为单位指定需要共享的内存容量

第三个参数,shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

2、shmat函数
第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。它的原型如下:
[cpp]  view plain  copy
 print ?
  1. void *shmat(int shm_id, const void *shm_addr, int shmflg);  
第一个参数,shm_id是由shmget函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
第三个参数,shm_flg是一组标志位,通常为0。

调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.

3、shmdt函数
该函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。它的原型如下:
[cpp]  view plain  copy
 print ?
  1. int shmdt(const void *shmaddr);  
参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.

4、shmctl函数
与信号量的semctl函数一样,用来控制共享内存,它的原型如下:
[cpp]  view plain  copy
 print ?
  1. int shmctl(int shm_id, int command, struct shmid_ds *buf);  
第一个参数,shm_id是shmget函数返回的共享内存标识符。

第二个参数,command是要采取的操作,它可以取下面的三个值 :
    IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
    IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
    IPC_RMID:删除共享内存段

第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。
shmid_ds结构至少包括以下成员:
[cpp]  view plain  copy
 print ?
  1. struct shmid_ds  
  2. {  
  3.     uid_t shm_perm.uid;  
  4.     uid_t shm_perm.gid;  
  5.     mode_t shm_perm.mode;  
  6. };  

具体思路:先写后读,写进程先打开文件,然后往shmemory共享内存内写入文件,读进程直接从共享内存中读文件,并打印出来。

三个文件:shmwrite.c (写进程) shmread.c(读进程,可以有多个)  sharemry.h(头文件)

先看头文件sharemry.h:


定义了flag是否可读的标志,size写入文件的大小,free_status是否最后一个读进程,text是一个可变数组,根据打开的文件大小开辟内存。无论是端还是写端,都必须先挂载到同一共享内存上。

可变数组:

text[0]结构

经常遇到的结构形状如下:

struct buffer
{
    int text_len;   //长度
    char text[0];  //起始地址
};

  在这个结构中,text是一个数组名;但该数组没有元素;该数组的真实地址紧随结构体buffer之后,而这个地址就是结构体后面数据的地址(如果给这个结构体分配的内容大于这个结构体实际大小,后面多余的部分就是这个text的内容);这种声明方法可以巧妙的实现C语言里的数组扩展。


下图是shmwrite.c写进程源码:


挂载到共享内存之后,开始读文件并写入共享内存


写入完成,将程序和共享内存卸载,也即脱离。


最后是shmread.c:


接上图


这里就会用到flag,有两个或多个进程同时读同一个共享内存时,每个读进程都会去挂载这块创建好的共享内存,但是有个隐藏的bug,共享内存是公用的一块,如果每个读进程都去shmctl共享内存的话,会出现错误,类似于内存泄露。所以标志flag保证最后一个读进程退出时才删除共享内存。

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值