Linux通信代码实现.doc_第1页
Linux通信代码实现.doc_第2页
Linux通信代码实现.doc_第3页
Linux通信代码实现.doc_第4页
Linux通信代码实现.doc_第5页
已阅读5页,还剩20页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Linux进程间通信-共享内存方式 (含例子)2008-07-22 15:04共享内存实现分为两个步骤:一、创建共享内存,使用shmget函数。二、映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat函数由于这种方式是直接对用户空间的内存进行映射,故效率较高,使用起来也方便;但需要注意的是,多个进程并发访问共享内存时的同步控制问题,可用信号量操作解决。系统调用:shmget( ) ;原型:int shmget ( key_t key, int size, int shmflg ); 返回值:如果成功,返回共享内存段标识符。 如果失败,则返回- 1: errno = EINVAL (无效的内存段大小) EEXIST (内存段已经存在,无法创建) EIDRM (内存段已经被删除) ENOENT (内存段不存在) EACCES (权限不够) ENOMEM (没有足够的内存来创建内存段)函数中的key_t如何获得已经在上一篇文章中说明了系统调用:shmat();原型:int shmat ( int shmid, char *shmaddr, int shmflg); 返回值:如果成功,则返回共享内存段连接到进程中的地址。 如果失败,则返回- 1: errno = EINVAL (无效的IPC ID 值或者无效的地址) ENOMEM (没有足够的内存) EACCES (存取权限不够)当一个进程不在需要共享的内存段时,它将会把内存段从其地址空间中脱离。系统调用:shmdt();调用原型:int shmdt ( char *shmaddr ); 返回值:如果失败,则返回- 1: errno = EINVAL (无效的连接地址)举例:shm1.c#include #include #include #include #include #include #include #include #include #include int main() key_t ipckey; int shmid; int i; char *addr_c; char srcbuf8=abcdefg; ipckey=ftok(/home/yds/tmp/shmipc,0); shmid=shmget(ipckey,1024,IPC_CREAT|0666); if(shmid=-1) printf(creat shm error!n); return -1; addr_c=(char *)shmat(shmid,0,0); if(int)addr_c=-1) printf(attach shm error!n); return -1; while(1) for(i=0;istrlen(srcbuf);i+) srcbufi=srcbufi+1; strcpy(addr_c,srcbuf); sleep(1); shmdt(addr_c); return 0; shm2.c#include #include #include #include #include #include #include #include #include #include int main() key_t ipckey; int shmid; int i; char *addr_c; ipckey=ftok(/home/yds/tmp/shmipc,0); shmid=shmget(ipckey,1024,IPC_CREAT|0666); if(shmid=-1) printf(creat shm error!n); return -1; addr_c=(char *)shmat(shmid,0,0); if(int)addr_c=-1) printf(attach shm error!n); return -1; while(1) sleep(2); printf(string(shm) is:%sn,addr_c); shmdt(addr_c); return 0; 以上两个程序shm1.c先运行,shm2.c后运行 由于没有采用信号量机制对进程进行同步,所以只能先人为规定一下执行顺序了,只在说明问题,不在实用。进程shm1负责每秒钟向共享内存中写入数据, shm2负责每两秒从共享内存中读数据,这个例子中key_t ipckey 保证操作的共享内存是同一个。上一篇文章有关于key_t的解释标题:Linux共享内存和信号量的实例2011-03-09 17:25:51#include #include#include#include#include#include#include#define MAXSHM 5 /定义缓冲区数组的下标变量个数/* 定义3个信号量的内部标识 */int fullid;int emptyid;int mutexid;/* 主函数 */int main()/* 定义2个共享内存的ID */int arrayid;int getid;/* 定义共享内存虚拟地址 */int *array;int *get;/* 创建共享内存 */arrayid=shmget(IPC_PRIVATE,sizeof(int) *MAXSHN,IPC_CREAT|0666);getid=shmget(IPC_PRIVATE,sizeof(int),IPC_CREAT|0666);/* 初始化共享内存 */array=(int *) shmat(arrayid,0,0);get=(int *) shmat(getid,0,0);*get=0;/* 定义信号量数据结构 */struct sembuf P,V;union semun arg;/* 创建信号量 */fullid=semget(IPC_PRIVATE,1,IPC_CREAT|0666);emptyid=semget(IPC_PRIVATE,1,IPC_CREAT|0666);mutexid=semget(IPC_PRIVATE,1,IPC_CREAT|0666);/*初始化信号量 */arg.val=0; /初始时缓冲区中无数据if(semctl(fullid,0,SETVAL,arg)=-1)perror(“semctl setval error”);arg.val=MAXSHM; /初始时缓冲区中有5个空闲的数组元素if(semctl(emptyid,0,SETVAL,arg)=-1)perror(“semctl setval error”);arg.val=1; /初始时互斥信号为1,允许一个进程进入if(semctl(mutexid,0,SETVAL,arg)=-1)perror(“semctl setval error”);/* 初始化 P V操作 */P.sem_num=0;P.sem_op=-1;P.sem_flg=SEM_UNDO;V.sem_num=0;V.sem_op=1;V.sem_flg=SEM_UNDO;/* 生产者进程 */if(fork()=0)int i=0;int set=0;while(i10)semop(emptyid,&P,1); /对 emptyid执行P操作semop(mutexid,&P,1); /对 mutexid执行 P操作arrayset%MAXSHM=i+1;printf(“Producer put number %d to No.%dn”,arrayset%MAXSHM,set%MAXSHM);set+; /写计数加1semop(mutexid,&V,1); /对mutexid执行 V 操作semop(fullid,&V,1); /对fullid执行 V 操作i+;sleep(3); /SLEEP 3秒,等待消费者进程执行完毕printf(“Poducer if overn”);exit(0);else/* 消费者A进程 */if(fork()=0)while(1)if(*get=10)break;semop(fullid,&P,1); /对fullid执行 P 操作semop(mutexid,&P,1); /对mutexid执行 P 操作printf(“The ConsumerA get number from No.%dn”,(*get)%MAXSHM);(*get)+; /读计数加1semop(mutexid,&V,1); /对mutexid执行 V 操作semop(emptyid,&V,1); /对fullid执行 V 操作sleep(1);printf(“ConsunerA is overn”);exit(0);else/*消费者B进程 */if(fork()=0)while(1)if(*get=10)break;semop(fullid,&P,1); /对fullid执行 P 操作semop(mutexid,&P,1); /对mutexid执行 P 操作printf(“The ConsumerA get number from No.%dn”,(*get)%MAXSHM);(*get)+; /读计数加1semop(mutexid,&V,1); /对mutexid执行 V 操作semop(emptyid,&V,1); /对emptyid执行 V 操作sleep(1);printf(“ConsunerB is overn”);exit(0);/* 父进程返回后回收3个子进程 */wait(0);wait(0);wait(0);/* 断开并撤消2个共享内存 */shmdt(array);shmctl(arrayid,IPC_RMID,0);shmctl(get);shmctl(getid,IPC_RMID,0);/* 撤消3个信号量集 */semctl(emptyid,IPC_RMID,0);semctl(fullid,IPC_RMID,0);semctl(mutexid,IPC_RMID,0);exit(0);1. /*server.c:向共享内存中写入People*/2. #include 3. #include 4. #include 5. #include 6. 7. intmain()8. 9. structPeople10. charname10;11. intage;12. ;13. 14. intsemid;15. intshmid;16. key_tsemkey;17. key_tshmkey;18. semkey=ftok(server.c,0);19. shmkey=ftok(client.c,0);20. 21. /*创建共享内存和信号量的IPC*/22. semid=semget(semkey,1,0666|IPC_CREAT);23. if(semid=-1)24. printf(creatsemisfail/n);25. shmid=shmget(shmkey,1024,0666|IPC_CREAT);26. if(shmid=-1)27. printf(creatshmisfail/n);28. 29. /*设置信号量的初始值,就是资源个数*/30. unionsemun31. intval;32. structsemid_ds*buf;33. ushort*array;34. sem_u;35. 36. sem_u.val=1;37. semctl(semid,0,SETVAL,sem_u);38. 39. /*将共享内存映射到当前进程的地址中,之后直接对进程中的地址addr操作就是对共享内存操作*/40. 41. structPeople*addr;42. addr=(structPeople*)shmat(shmid,0,0);43. if(addr=(structPeople*)-1)44. printf(shmshmatisfail/n);45. 46. /*信号量的P操作*/47. voidp()48. 49. structsembufsem_p;50. sem_p.sem_num=0;51. sem_p.sem_op=-1;52. if(semop(semid,&sem_p,1)=-1)53. printf(poperationisfail/n);54. 55. 56. /*信号量的V操作*/57. voidv()58. 59. structsembufsem_v;60. sem_v.sem_num=0;61. sem_v.sem_op=1;62. if(semop(semid,&sem_v,1)=-1)63. printf(voperationisfail/n);64. 65. 66. /*向共享内存写入数据*/67. p();68. strcpy(*addr).name,xiaoming);69. /*注意:此处只能给指针指向的地址直接赋值,不能在定义一个structPeoplepeople_1;addr=&people_1;因为addr在addr=(structPeople*)shmat(shmid,0,0);时,已经由系统自动分配了一个地址,这个地址与共享内存相关联,所以不能改变这个指针的指向,否则他将不指向共享内存,无法完成通信了。70. 注意:给字符数组赋值的方法。刚才太虎了。*/71. (*addr).age=10;72. v();73. 74. /*将共享内存与当前进程断开*/75. if(shmdt(addr)=-1)76. printf(shmdtisfail/n);77. 78. cpp:nogutter view plaincopyprint?1. /*client.c:从共享内存中读出People*/2. #include 3. #include 4. #include 5. #include 6. 7. intmain()8. 9. intsemid;10. intshmid;11. key_tsemkey;12. key_tshmkey;13. semkey=ftok(server.c,0);14. shmkey=ftok(client.c,0);15. 16. structPeople17. charname10;18. intage;19. ;20. 21. /*读取共享内存和信号量的IPC*/22. semid=semget(semkey,0,0666);23. if(semid=-1)24. printf(creatsemisfail/n);25. shmid=shmget(shmkey,0,0666);26. if(shmid=-1)27. printf(creatshmisfail/n);28. 29. /*将共享内存映射到当前进程的地址中,之后直接对进程中的地址addr操作就是对共享内存操作*/30. structPeople*addr;31. addr=(structPeople*)shmat(shmid,0,0);32. if(addr=(structPeople*)-1)33. printf(shmshmatisfail/n);34. 35. /*信号量的P操作*/36. voidp()37. 38. structsembufsem_p;39. sem_p.sem_num=0;40. sem_p.sem_op=-1;41. if(semop(semid,&sem_p,1)=-1)42. printf(poperationisfail/n);43. 44. 45. /*信号量的V操作*/46. voidv()47. 48. structsembufsem_v;49. sem_v.sem_num=0;50. sem_v.sem_op=1;51. if(semop(semid,&sem_v,1)=-1)52. printf(voperationisfail/n);53. 54. 55. /*从共享内存读出数据*/56. p();57. printf(name:%s/n,addr-name);58. printf(age:%d/n,addr-age);59. v();60. 61. /*将共享内存与当前进程断开*/62. if(shmdt(addr)=-1)63. printf(shmdtisfail/n);64. 65. /*IPC必须显示删除。否则会一直留存在系统中*/66. if(semctl(semid,0,IPC_RMID,0)=-1)67. printf(semctldeleteerror/n);68. if(shmctl(shmid,IPC_RMID,NULL)=-1)69. printf(shmctldeleteerror/n);70. 这里是比较通俗的解析:2.3.3 共享内存 共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的将是实际的物理内存,在Linux系统下,这只有通过限制Linux系统存取的内存才可以做到,这当然不太实际。常用的方式是通过shmXXX函数族来实现利用共享内存进行存储的。 首先要用的函数是shmget,它获得一个共享存储标识符。 #include #include #include int shmget(key_t key, int size, int flag); 这个函数有点类似大家熟悉的malloc函数,系统按照请求分配size大小的内存用作共享内存。Linux系统内核中每个IPC结构都有的一个非负整数的标识符,这样对一个消息队列发送消息时只要引用标识符就可以了。这个标识符是内核由IPC结构的关键字得到的,这个关键字,就是上面第一个函数的key。数据类型key_t是在头文件sys/types.h中定义的,它是一个长整形的数据。在我们后面的章节中,还会碰到这个关键字。 当共享内存创建后,其余进程可以调用shmat()将其连接到自身的地址空间中。 void *shmat(int shmid, void *addr, int flag); shmid为shmget函数返回的共享存储标识符,addr和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地址,进程可以对此进程进行读写操作。 使用共享存储来实现进程间通信的注意点是对数据存取的同步,必须确保当一个进程去读取数据时,它所想要的数据已经写好了。通常,信号量被要来实现对共享存储数据存取的同步,另外,可以通过使用shmctl函数设置共享存储内存的某些标志位如SHM_LOCK、SHM_UNLOCK等来实现。 2.3.4 信号量 信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。一般说来,为了获得共享资源,进程需要执行下列操作: (1) 测试控制该资源的信号量。 (2) 若此信号量的值为正,则允许进行使用该资源。进程将进号量减1。 (3) 若此信号量为0,则该资源目前不可用,进程进入睡眠状态,直至信号量值大于0,进程被唤醒,转入步骤(1)。 (4) 当进程不再使用一个信号量控制的资源时,信号量值加1。如果此时有进程正在睡眠等待此信号量,则唤醒此进程。 维护信号量状态的是Linux内核操作系统而不是用户进程。我们可以从头文件/usr/src/linux/include/linux/sem.h中看到内核用来维护信号量状态的各个结构的定义。信号量是一个数据集合,用户可以单独使用这一集合的每个元素。要调用的第一个函数是semget,用以获得一个信号量ID。 #include #include #include int semget(key_t key, int nsems, int flag); key是前面讲过的IPC结构的关键字,它将来决定是创建新的信号量集合,还是引用一个现有的信号量集合。nsems是该集合中的信号量数。如果是创建新集合(一般在服务器中),则必须指定nsems;如果是引用一个现有的信号量集合(一般在客户机中)则将nsems指定为0。 semctl函数用来对信号量进行操作。 int semctl(int semid, int semnum, int cmd, union semun arg); 不同的操作是通过cmd参数来实现的,在头文件sem.h中定义了7种不同的操作,实际编程时可以参照使用。 semop函数自动执行信号量集合上的操作数组。 int semop(int semid, struct sembuf semoparray, size_t nops); semoparray是一个指针,它指向一个信号量操作数组。nops规定该数组中操作的数量。 下面,我们看一个具体的例子,它创建一个特定的IPC结构的关键字和一个信号量,建立此信号量的索引,修改索引指向的信号量的值,最后我们清除信号量。在下面的代码中,函数ftok生成我们上文所说的唯一的IPC关键字。cpp:nogutter view plaincopyprint?1. #include 2. #include 3. #include 4. #include 5. voidmain()6. key_tunique_key;/*定义一个IPC关键字*/7. intid;8. structsembuflock_it;9. unionsemunoptions;10. inti;11. 12. unique_key=ftok(.,a);/*生成关键字,字符a是一个随机种子*/13. /*创建一个新的信号量集合*/14. id=semget(unique_key,1,IPC_CREAT|IPC_EXCL|0666);15. printf(semaphoreid=%d/n,id);16. options.val=1;/*设置变量值*/17. semctl(id,0,SETVAL,options);/*设置索引0的信号量*/18. 19. /*打印出信号量的值*/20. i=semctl(id,0,GETVAL,0);21. printf(valueofsemaphoreatindex0is%d/n,i);22. 23. /*下面重新设置信号量*/24. lock_it.sem_num=0;/*设置哪个信号量*/25. lock_it.sem_op=-1;/*定义操作*/26. lock_it.sem_flg=IPC_NOWAIT;/*操作方式*/27. if(semop(id,&lock_it,1)=-1)28. printf(cannotlocksemaphore./n);29. exit(1);30. 31. 32. i=semctl(id,0,GETVAL,0);33. printf(valueofsemaphoreatindex0is%d/n,i);34. 35. /*清除信号量*/36. semctl(id,0,IPC_RMID,0);37. Linux进程间通信源码剖析,共享内存(shmget函数详解) 分类: 嵌入式/Linux/Wince/OSE 2010-12-22 23:18 2058人阅读 评论(1) 收藏 举报 shmgetint shmget(key_tkey, size_tsize, intflag);key: 标识符的规则size:共享存储段的字节数flag:读写的权限返回值:成功返回共享存储的id,失败返回-1key_t key- key标识共享内存的键值:0/IPC_PRIVATE。 当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中设置了IPC_PRIVATE这个标志,则同样将创建一块新的共享内存。 在IPC的通信模式下,不管是使用消息队列还是共享内存,甚至是信号量,每个IPC的对象(object)都有唯一的名字,称为“键”(key)。通过“键”,进程能够识别所用的对象。“键”与IPC对象的关系就如同文件名称之于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够共用一个文件。而在IPC的通讯模式下,通过“键”的使用也使得一个IPC对象能为多个进程所共用。 Linux系统中的所有表示System V中IPC对象的数据结构都包括一个ipc_perm结构,其中包含有IPC对象的键值,该键用于查找System V中IPC对象的引用标识符。如果不使用“键”,进程将无法存取IPC对象,因为IPC对象并不存在于进程本身使用的内存中。 通常,都希望自己的程序能和其他的程序预先约定一个唯一的键值,但实际上并不是总可能的成行的,因为自己的程序无法为一块共享内存选择一个键值。因此,在此把key设为IPC_PRIVATE,这样,操作系统将忽略键,建立一个新的共享内存,指定一个键值,然后返回这块共享内存IPC标识符ID。而将这个新的共享内存的标识符ID告诉其他进程可以在建立共享内存后通过派生子进程,或写入文件或管道来实现。int size(单位字节Byte)- size是要建立共享内存的长度。所有的内存分配操作都是以页为单位的。所以如果一段进程只申请一块只有一个字节的内存,内存也会分配整整一页(在i386机器中一页的缺省大小PACE_SIZE=4096字节)这样,新创建的共享内存的大小实际上是从size这个参数调整而来的页面大小。即如果size为1至4096,则实际申请到的共享内存大小为4K(一页);4097到8192,则实际申请到的共享内存大小为8K(两页),依此类推。int shmflg- shmflg主要和一些标志有关。其中有效的包括IPC_CREAT和IPC_EXCL,它们的功能与open()的O_CREAT和O_EXCL相当。 IPC_CREAT 如果共享内存不存在,则创建一个共享内存,否则打开操作。 IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。 如果单独使用IPC_CREAT,shmget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。如果将IPC_CREAT和IPC_EXCL标志一起使用,shmget()将返回一个新建的共享内存的标识符;如果该共享内存已存在,或者返回-1。IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。对于用户的读取和写入许可指定SHM_R和SHM_W,(SHM_R3)和(SHM_W3)是一组读取和写入许可,而(SHM_R6)和(SHM_W6)是全局读取和写入许可。返回值-成功返回共享内存的标识符;不成功返回-1,errno储存错误原因。 EINVAL 参数size小于SHMMIN或大于SHMMAX。 EEXIST 预建立key所致的共享内存,但已经存在。 EIDRM 参数key所致的共享内存已经删除。 ENOSPC 超过了系统允许建立的共享内存的最大值(SHMALL )。 ENOENT 参数key所指的共享内存不存在,参数shmflg也未设IPC_CREAT位。 EACCES 没有权限。 ENOMEM 核心内存不足。struct shmid_ds- shmid_ds数据结构表示每个新建的共享内存。当shmget()创建了一块新的共享内存后,返回一个可以用于引用该共享内存

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论