本帖最后由 yuanlai2010 于 2014-9-2 13:12 编辑
System V IPC –共享内存
参与Helper2416开发板助学计划心得
这篇帖子主要是关于共享内存的学习心得。
共享内存的定义:
共享内存是由IPC为进程创建的一个特殊的地址范围(具有实际物理内存),他将出现在该进程的地址空间中,其他的进程可以将同一段共享内存连接到他们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像压缩们是由malloc分配的一样,如果某个进程向共享内存写入了数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。
简单的来说,就是从物理地址中划分一段内存出来,然后将这段内存连接到不同的进程的逻辑地址空间中去,然后不管在哪个进程中去访问链接的这段地址空间,实际都是直接去访问那段物理地址的,所以对于不同进程来说,这段内存的数据都是即时的。这也是效率最高的一种通信方式。
共享内存为在多个进程之间共享和传递数据提供了一种有效的方式,由于它并未提供同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问。
如下是共享内存的工作原理图:
从上面的叙述基本上就可以概括出使用共享内存的基本步骤了:
1:分配共享内存(从物理内存中划分一段地址空间出来作为共享的内存)
2:将共享内存连接到进程的逻辑地址空间
3:如果不再使用了,就需要解除和进程间的连接关系咯
4:如果没有进程连接到此共享内存了,就可以把此段物理内存释放了
共享内存函数定义如下:
#include
void*shmat(int shm_id, const void *shm_addr, int shmflg);
intshmctl(int shm_id, int cmd, struct shmid_ds *buf);
intshmdt(const void *shm_addr)
intshmget(key_t key, size_t size, int shmflg)
与信号量一样,通常情况下sys/shm.h文件会自动包含sys/types.h与sys/ipc.h文件
shmget
函数原型: int shmget(key_t key, size_t size,int shmflg)
函数功能: 创建共享内存
函数返回: 如果调用成功,shmget将返回一个非负数,即共享内存标识符。如果调用失败,就返回-1。
参数说明: 该函数的第一个参数是一个用来标识共享内存块的键值。彼此无关的进程可以通过指定同一个键以获取对同一个共享内存块的访问。不幸的是,其它程序也可能挑选了同样的特定值作为自己分配共享内存的键值,从而产生冲突。用特殊常量IPC_PRIVATE作为键值可以保证系统建立一个全新的共享内存块。
该函数的第二个参数指定了所申请的内存块的大小。因为这些内存块是以页面为单位进行分配的,实际分配的内存块大小将被扩大到页面大小的整数倍。
第三个参数是一组标志,通过特定常量的按位或操作来shmget。这些特定常量包括:
IPC_CREAT:这个标志表示应创建一个新的共享内存块。通过指定这个标志,我们可以创建一个具有指定键值的新共享内存块。
IPC_EXCL:这个标志只能与 IPC_CREAT 同时使用。当指定这个标志的时候,如果已有一个具有这个键值的共享内存块存在,则shmget会调用失败。也就是说,这个标志将使线程获得一个“独有”的共享内存块。如果没有指定这个标志而系统中存在一个具有相同键值的共享内存块,shmget会返回这个已经建立的共享内存块,而不是重新创建一个。
模式标志:这个值由9个位组成,分别表示属主、属组和其它用户对该内存块的访问权限。其中表示执行权限的位将被忽略。指明访问权限的一个简单办法是利用中指定,并且在手册页第二节stat条目中说明了的常量指定。例如,S_IRUSR和S_IWUSR分别指定了该内存块属主的读写权限,而 S_IROTH和S_IWOTH则指定了其它用户的读写权限。 下面例子中shmget函数创建了一个新的共享内存块(当shm_key已被占用时则获取对一个已经存在共享内存块的访问),且只有属主对该内存块具有读写权限,其它用户不可读写。
Shmat
函数原型: void *shmat(int shm_id, const void*shm_addr, int shmflg);
函数功能: 将共享内存连接到一个进程的地址空间中
函数返回: 如果shmat调用成功,它返回一个指向共享内存第一个字节的指针。如果调用失败,就返回-1。
参数说明: 第一个参数shm_id室友shmget返回的共享内存标识符。
第二个参数shm_addr指定的是共享内存连接到当前进程中的地址位置。它通常是一个空指针,表示让系统来选择共享内存出现的地址。
第三个参数shmflg是一组位标志。它的来那个个可能取值是SHM_RND(这个标志与shm_addr联合使用,用来控制共享内存连接的地址)和SHM_RDONLY(它是的连接的内存只读)。我们很少需要控制共享内存连接的地址,通常都是让系统来选择一个地址,否则就会使应用程序对硬件的依赖性过高。
Shmdt
函数原型: int shmdt(const void *shm_addr)
函数功能: 将共享内存从当前进程中分离
函数返回: 调用成功返回0,否则会返回-1。
参数说明:参数是shmat返回的地址指针。
Shmctl
函数原型: int shmctl(int shm_id, int cmd,struct shmid_ds *buf);
函数功能: 对共享内存的一些控制操作,(包括删除操作)
函数返回: 调用成功返回0,否则会返回-1。
参数说明:shmid_ds结构至少包含以下成员:
structshmid_ds{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mod;
}
第一个参数shm_id是shmget返回的共享内存标识符。
第二个参数cmd是要采取的动作,它可以取如下三个值
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存
第三个参数buf是一个指针,它指向包含共享内存模式和访问权限的结构。
实践代码
shm_com.h
- #define TEXT_SZ 2048
- struct shared_use_st
- {
- int written_by_you;
- char some_text[TEXT_SZ];
- };
复制代码
shm0.c
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <sys/shm.h>
- #include "shm_com.h"
- int main(void)
- {
- int running=1;
- void *shared_memory=(void *)0;
- struct shared_use_st *shared_stuff;
- int shmid;
- /*创建共享内存*/
- shmid=shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);
- if(shmid==-1)
- {
- fprintf(stderr,"shmget failed\n");
- exit(EXIT_FAILURE);
- }
- /*映射共享内存*/
- shared_memory=shmat(shmid,(void *)0,0);
- if(shared_memory==(void *)-1)
- {
- fprintf(stderr,"shmat failed\n");
- exit(EXIT_FAILURE);
- }
- printf("Memory attached at %X\n",(int)shared_memory);
- /*让结构体指针指向这块共享内存*/
- shared_stuff=(struct shared_use_st *)shared_memory;
- /*控制读写顺序*/
- shared_stuff->written_by_you=0;
- /*循环的从共享内存中读数据,直到读到“end”为止*/
- while(running)
- {
- if(shared_stuff->written_by_you)
- {
- printf("You wrote:%s",shared_stuff->some_text);
- sleep(1); //读进程睡一秒,同时会导致写进程睡一秒,这样做到读了之后再写
- shared_stuff->written_by_you=0;
- if(strncmp(shared_stuff->some_text,"end",3)==0)
- {
- running=0; //结束循环
- }
- }
- }
- /*删除共享内存*/
- if(shmdt(shared_memory)==-1)
- {
- fprintf(stderr,"shmdt failed\n");
- exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
- }
复制代码
shm1.c
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <sys/shm.h>
- #include "shm_com.h"
- int main(void)
- {
- int running=1;
- void *shared_memory=(void *)0;
- struct shared_use_st *shared_stuff;
- char buffer[BUFSIZ];
- int shmid;
- /*创建共享内存*/
- shmid=shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);
- if(shmid==-1)
- {
- fprintf(stderr,"shmget failed\n");
- exit(EXIT_FAILURE);
- }
- /*映射共享内存*/
- shared_memory=shmat(shmid,(void *)0,0);
- if(shared_memory==(void *)-1)
- {
- fprintf(stderr,"shmat failed\n");
- exit(EXIT_FAILURE);
- }
- printf("Memory attached at %X\n",(int)shared_memory);
- /*让结构体指针指向这块共享内存*/
- shared_stuff=(struct shared_use_st *)shared_memory;
- /*循环的向共享内存中写数据,直到写入的为“end”为止*/
- while(running)
- {
- while(shared_stuff->written_by_you==1)
- {
- sleep(1);//等到读进程读完之后再写
- printf("waiting for client...\n");
- }
- printf("Ener some text:");
- fgets(buffer,BUFSIZ,stdin);
- strncpy(shared_stuff->some_text,buffer,TEXT_SZ);
- shared_stuff->written_by_you=1;
- if(strncmp(buffer,"end",3)==0)
- {
- running=0; //结束循环
- }
- }
- /*删除共享内存*/
- if(shmdt(shared_memory)==-1)
- {
- fprintf(stderr,"shmdt failed\n");
- exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
- }
复制代码
运行结果
- [jyxtec@localhost shm]$ ls
- shm0 shm0.c shm1 shm1.c shm_com.h
- [jyxtec@localhost shm]$ ./shm0 & ./shm1
- [1] 2156
- Memory attached at B778D000
- Ener some text:Memory attached at B7737000
- yuanlai
- You wrote:yuanlai
- waiting for client...
- waiting for client...
- Ener some text:Helper2416
- You wrote:Helper2416
- waiting for client...
- waiting for client...
- Ener some text:end
- [jyxtec@localhost shm]$ You wrote:end
- [1]+ 完成 ./shm0
- [jyxtec@localhost shm]$
复制代码
论坛ID:yuanlai2010
发表时间:2014-09-02