Linux 进程间通讯(IPC)方式有以下几种: 1-》管道(pipe)和有名管道(fifo). 2-》消息队列 3-》共享内存 4-》信号量 5-》信号(signal) 6-》套接字(sicket) 在这里我们看一下第3种====共享内存(share memory )。其它通信方式见其它文章。 顾名思义:共享内存就是两个或多个进程共享一块内存区域。 这种通信方式允许两个不相关的进程能够访问处理同一块内存区域。从而达到进程间数据的交换和处理等。在这些进行通信方式中,共享内存是一种非常高效的通信方式,一种最快的通信方式。为什么快呢?只要我们向一个进程汇总写入的数据,则共享这个内存区域的所有进程都可见,从而达到提醒的目的。可以归为一句话:一处改变,随处可见。 原理: 共享内存是被多个进程共享的一块物理内存,而这些内存就是把共享内存区域映射到自己的虚拟内存中。从而对共享内存进行操作。 ================================================ 共享内存比pipe的优势: ============================================= 接下来我们看一下关于共享内存的操作函数: 主要有: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函数得到,来生成唯一的KEY值ID 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; } 运行结果,当我们在一个终端上输入时,在另一个终端会打印出结果,而当我们输入0,0时两个都会退出.
|