Linux 进程间通讯(IPC)方式 ------- 共享内存

 

Linux 进程间通讯(IPC)方式有以下几种:

1-》管道(pipe)和有名管道(fifo.

2-》消息队列

3-》共享内存

4-》信号量

5-》信号(signal)

6-》套接字(sicket)

在这里我们看一下第3====共享内存(share memory )。其它通信方式见其它文章。

顾名思义:共享内存就是两个或多个进程共享一块内存区域。

这种通信方式允许两个不相关的进程能够访问处理同一块内存区域。从而达到进程间数据的交换和处理等。在这些进行通信方式中,共享内存是一种非常高效的通信方式,一种最快的通信方式。为什么快呢?只要我们向一个进程汇总写入的数据,则共享这个内存区域的所有进程都可见,从而达到提醒的目的。可以归为一句话:一处改变,随处可见。

原理:

共享内存是被多个进程共享的一块物理内存,而这些内存就是把共享内存区域映射到自己的虚拟内存中。从而对共享内存进行操作。

================================================

共享内存比pipe的优势:
1.用在任何进程,不比考虑进程关系,
2.读走后还存在数据
3.可以随机读取所需要的数据

=============================================

接下来我们看一下关于共享内存的操作函数:

主要有:shmget();shmat();shmdt();shmctl();

在使用这些函数需引进头文件#include<sys/ipc.h>#include<sys/shm.h>

我们都知道在计算机的世界里什么都必须先声明后使用,对此也不例外。

要实现共享内存通信,必须现有共享内存的区域。

下面我们看一下shmget函数。

函数原型:int shmget(key_t    _key,     size_t   _size,   int  _shmflg);

功能:创建共享内存

返回值:返回共享内存的标识码。失败返回-1

参数说明:

   _key  :标识共享内存的键值。有IPC _PRIVATE选项表示该内存块为该进程私有,一般不用。

  _size :表示要建立共享内存的长度。我们一般用getpagesize();函数来设定,此函数的返回值为4096个字节。

  _shmflg: 标志,是权限或创建方式。

           创建方式有:IPC_CREAT如果内存不存在则创建一个共享内存,否则打开。

                       IPC_EXCL 只有共享内存不存在的时候,新的共享内存才会被创建,否则会产生错误。

下面我们简单地看一个小例子:

    /*

      demo shmfet

*/

#include<stdio.h>

#include<fcntl.h>

#include<stdlib.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include <unistd.h>

int main(int argc ,char *argv[])

{

   int shmid;

   shmid = shmget(IPC_PRIVATE ,getpagesize(),IPC_CREAT | IPC_EXCL | 0666);

//getpagesize() return 4096.

      if(shmid == -1)

   {

       perror("shmget error:");

       exit(EXIT_FAILURE);

   }

   printf("shmid=%d\n",shmid);//在此我们打印创建的内存表识ID

   return 0;

}

运行结果为:我们可以用ipcs  -m(查看系统共享内存块信息 的命令)来查看是否被创建成功。


当然也可以删除删除共享内存命令为: ipcrm   shm   shmid列的列号(共享内存的标识ID)。

eg:   ipcrm shm 4816896

通常情况下,该_key值通过ftok函数得到,来生成唯一的KEYID

int   key_t key;

key=ftok("/etc/passwd",1234);

int   shmid=shmget(key,getpagesize(),IPC_CREAT | IPC_EXCL | 0666);

  注: #include <sys/types.h>

#include <sys/ipc.h>

如下:

key_t ftok( char * fname, int id )

fname就是你指定的文件名(已经存在的文件名),一般使用当前目录,如:

key_t key;

key = ftok(".", 1); 这样就是将fname设为当前目录。

id是子序号。

在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。

如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。

查询文件索引节点号的方法是: ls -i

当删除重建文件后,索引节点号由操作系统根据当时文件系统的使用情况分配,因此与原来不同,所以得到的索引节点号也不同。

如果要确保key_t值不变,要么确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t

int    shmid=shmget(6666 ,getpagesize(),IPC_CREAT | IPC_EXCL | 0666);

下面看个例子:

   /*

      demo shmfet   2

*/

#include<stdio.h>

#include<fcntl.h>

#include<stdlib.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include <unistd.h>

int main(int argc ,char *argv[])

{

   int shmid;

int  key_t    key;

key=ftok(“.”,1234);

Shmid=shmget(key ,getpagesize(),IPC_CREAT |  IPC_EXCL | 0666);

  

//getpagesize() return 4096.

      if(shmid == -1)

   {

       perror("shmget error:");

       exit(EXIT_FAILURE);

   }

   printf("shmid=%d\n",shmid);//在此我们打印创建的内存表识ID

   return 0;

}

运行结果:

会提示file    exits  因为我们已经指定一个固定的key_t值。


====================================================================

下面我们看第二个函数

#include<sys/shm.h>

#include<sys/ipc.h>

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

功能:允许进程访问共享内存。

返回值:成功返回共享内存的起始地址,失败返回-1.

参数:

_Shmid   共享内存的ID ,也就是shmget();函数的返回值。

_Shmaddr 共享内存的起始地址。一般指定为0.

_ shmflg  是本进程对共享内存的操作模式。一般指定为0,即可读可写。

          如果指定为SHM_RDONLY 的话就是只读模式。

提示:后卖弄我们会看个例子。

    下面我们看第3个函数

使用完了内存我们要进行释放操作。

#include<sys/shm.h>

#include<sys/ipc.h>

Int shmdt(  _const  void *_shmaddr);

功能:释放共享内存,虽然释放不用了,但是共享内存仍然存在。

参数:共享内存的起始地址;就是shmat();函数的返回值。

返回值:成功返回0,失败返回-1.

下面我们看第4个函数;

#include<sys/shm.h>

#include<sys/ipc.h>

Int   shmctl(int _shmid,int _cmd ,struct shmid _ds *buf);

功能:共享内存的控制函数

返回值:成功返回0,失败返回-1.

参数:_shmid     共享内存的ID

_cmd    允许的存在,最常用的有IPC_RMID 删除共享内存的内存段。

buf   不错内存模式状态和访问权限的数据结构,通常为0.

=====================================================================================================

 

Ok,看例子,让程序验证真理

我们来实现BMI 指数:体重指数来衡量肥胖我们以

20~25为正常

<20 偏瘦 >25偏胖计算,可能不是这个值。但主要是通信(*^__^*)嘻嘻……

===================================================================================================

源文件1

#include<stdio.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include<stdlib.h>

int main(int argc ,char *argv[])

{

int shmid;

float *addr;

float h,w,flag=1.0f;//flag 作为判断循环的标志

shmid= shmget(ftok("./file",1000),getpagesize(),IPC_CREAT | IPC_EXCL | 0666);

if(shmid==-1)

{

perror("shmget error:");

exit(EXIT_FAILURE);

}

addr=shmat(shmid,0,0);

if(-1==*addr)

{

perror("shmat error:");

exit(EXIT_FAILURE);

}

while(1)

{

printf(" enter you height(CM) and height(KG):\n");

scanf("%f%f",addr,addr+1);

*(addr+2)=flag;

if(*addr==0||*(addr+1)==0 ) break; //if 输入其中一个的为0,跳出循环结束程序

}

return 0;

}

源文件2

#include<stdio.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include<stdlib.h>

int main(int argc ,char *argv[])

{

int shmid;

float *addr;

float h,w;

shmid= shmget(ftok("./file",1000),getpagesize(), 0666);

if(shmid==-1)

{

perror("shmget error:");

exit(EXIT_FAILURE);

}

addr =shmat(shmid,0,0);

if(-1== *addr)

{

perror("shmat error:");

exit(EXIT_FAILURE);

}

while(1)

{

if(*(addr+2)!=1.0) //如果还没有输入新的数据那么就跳过此次循环,直到再次输入新的数据。

{

sleep(1);

continue;

}

*(addr+2)=0;

h=*addr;

w=*(addr+1);

if(h==0 || w==0) break; //if 输入其中一个的为0,跳出循环结束程序

int ret=w/(h*h/10000);

if(ret>=20 &&ret <=25)

{

printf("ok!\n");

}else if(ret <20)

{

printf("thin!\n");

} else

{

printf("fat!\n");

}

}

if(-1==shmdt(addr))

{

perror("shmdt error:");

exit(EXIT_FAILURE);

}

if (shmctl(shmid,IPC_RMID,0)==-1)

{

perror("shctl error:");

exit(EXIT_FAILURE);

}

return 0;

}

运行结果,当我们在一个终端上输入时,在另一个终端会打印出结果,而当我们输入00时两个都会退出.


 

 

 

 

你可能感兴趣的:(数据结构,linux,cmd,终端,Signal,通讯)