共享内存

共享内存:

多个进程共享的一部分物理内存。共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。

       共享内存允许两个或多个进程进程共享同一块内存(这块内存会映射到各个进程自己独立的地址空间)从而使得这些进程可以相互通信。

在GNU/Linux中所有的进程都有唯一的虚拟地址空间,而共享内存应用编程接口API允许一个进程使用公共内存区段。但是对内存的共享访问其复杂度也相应增加。共享内存的优点是简易性。使用消息队列时,一个进程要向队列中写入消息,这要引起从用户地址空间向内核地址空间的一次复制,同样一个进程进行消息读取时也要进行一次复制。共享内存的优点是完全省去了这些操作。共享内存会映射到进程的虚拟地址空间,进程对其可以直接访问,避免了数据的复制过程。因此,共享内存是GNU/Linux现在可用的最快速的IPC机制。进程退出时会自动和已经挂接的共享内存区段分离,但是仍建议当进程不再使用共享区段时调用shmdt来卸载区段。

注意,当一个进程分支出父进程和子进程时,父进程先前创建的所有共享内存区段都会被子进程继承。如果区段已经做了删除标记(在前面以IPC——RMID指令调用shmctl),而当前挂接数已经变为0,这个区段就会被移除。


共享内存相关函数:

主要有以下几个API:ftok()、shmget()、shmat()、shmdt()及shmctl()。

1.用ftok()函数获得一个ID号.

说明:

在IPC中,我们经常用用key_t的值来创建或者打开信号量,共享内存和消息队列

函数原型:

key_t ftok(const char *pathname, int proj_id)

Keys:1.pathname一定要在系统中存在并且进程能够访问的

    2.proj_id是一个1-255之间的一个整数值,典型的值是一个ASCII值。

当成功执行的时候,一个key_t值将会被返回,否则-1被返回。我们可以使用strerror(errno)来确定具体的错误信息。考虑到应用系统可能在不同的主机上应用,可以直接定义一个key,而不用ftok获得:#define IPCKEY 0x344378

2。shmget()用来开辟/指向一块共享内存的函数

说明:

shmget()用来获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域

函数原型:

int shmget(key_t key, size_t size, int shmflg);

key_t key 是这块共享内存的标识符。如果是父子关系的进程间通信的话,这个标识符用IPC_PRIVATE来代替。如果两个进程没有任何关系,所以就用ftok()算出来一个标识符(或者自己定义一个)使用了

int size 是这块内存的大小。

int flag 是这块内存的模式(mode)以及权限标识。

模式可取如下值:        

IPC_CREAT 新建(如果已创建则返回目前共享内存的id)

IPC_EXCL   与IPC_CREAT结合使用,如果已创建则则返回错误

然后将“模式” 和“权限标识”进行“或”运算,做为第三个参数。

如:    IPC_CREAT | IPC_EXCL | 0640   

例子中的0640为权限标识,4/2/1 分别表示读/写/执行3种权限,第一个0是UID,第一个6(4+2)表示拥有者的权限,第二个4表示同组权限,第3个0表示他人的权限。这个函数成功时返回共享内存的ID,失败时返回-1。

注意:创建共享内存时,shmflg参数至少需要 IPC_CREAT | 权限标识,如果只有IPC_CREAT 则申请的地址都是k=0xffffffff,不能使用;获取已创建的共享内存时,shmflg不要用IPC_CREAT(只能用创建共享内存时的权限标识,如0640),否则在某些情况下,比如用ipcrm删除共享内存后,用该函数并用IPC_CREAT参数获取一次共享内存(当然,获取失败),则即使再次创建共享内存也不能成功,此时必须更改key来重建共享内存。

3.shmat()将这个内存区映射到本进程的虚拟地址空间。

函数原型:

void    *shmat( int shmid , char *shmaddr , int shmflag );

shmat()是用来允许本进程访问一块共享内存的函数。

int shmid是那块共享内存的ID。

char *shmaddr是共享内存的起始地址,如果shmaddr为0,内核会把共享内存映像到调用进程的地址空间中选定位置;如果shmaddr不为0,内核会把共享内存映像到shmaddr指定的位置。所以一般把shmaddr设为0。

int shmflag是本进程对该内存的操作模式。如果是SHM_RDONLY的话,就是只读模式。其它的是读写模式

成功时,这个函数返回共享内存的起始地址。失败时返回-1。

4.shmdt()函数删除本进程对这块内存的使用,shmdt()与shmat()相反,是用来禁止本进程访问一块共享内存的函数。

函数原型:

int shmdt( char *shmaddr );

参数char *shmaddr是那块共享内存的起始地址。成功时返回0。失败时返回-1。

5.shmctl() 控制对这块共享内存的使用

函数原型:

int     shmctl( int shmid , int cmd , struct shmid_ds *buf );

int shmid是共享内存的ID。

int cmd是控制命令,可取值如下:

IPC_STAT        得到共享内存的状态

IPC_SET         改变共享内存的状态

IPC_RMID        删除共享内存

struct shmid_ds *buf是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定。

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

1.创建一个共享内存区,并显示该内存区的信息,最后删除该共享内存区

#include <stdio.h>
#include <unistd.h>  //getpagesize( )
#include <sys/ipc.h>
#include <sys/shm.h>
#define MY_SHM_ID 67483
int main(  )
    {
        //获得系统中页面的大小
        printf( "page size=%d/n",getpagesize(  ) );
        //创建一个共享内存区段
        int shmid,ret;
        shmid=shmget( MY_SHM_ID,4096,0666|IPC_CREAT );
        //创建了一个4KB大小共享内存区段。指定的大小必须是当前系统架构
        //中页面大小的整数倍
        if( shmid>0 )
            printf( "Create a shared memory segment %d\n",shmid );
        //获得一个内存区段的信息
        struct shmid_ds shmds;
        //shmid=shmget( MY_SHM_ID,0,0 );//示例怎样获得一个共享内存的标识符
        ret=shmctl( shmid,IPC_STAT,&shmds );
        if( ret==0 )
            {
                printf( "Size of memory segment is %d\n",shmds.shm_segsz );
                printf( "Numbre of attaches %d\n",( int )shmds.shm_nattch );
            }
        else
            {
                printf( "shmctl() call failed\n" );
            }
        //删除该共享内存区
        ret=shmctl( shmid,IPC_RMID,0 );
        if( ret==0 )
            printf( "Shared memory removed \n" );
        else
            printf( "Shared memory remove failed\n" );
        return 0;
    }

2.共享内存区的共享,脱离和使用

#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <errno.h>
#define MY_SHM_ID 67483
int main(  )
    {
        //共享内存区段的挂载和脱离
        int shmid,ret;
        void* mem;
        shmid=shmget( MY_SHM_ID,4096,0666|IPC_CREAT );
        if( shmid>=0 )
            {
                mem=shmat( shmid,( const void* )0,0 );
                //shmat()返回进程地址空间中指向区段的指针
                if( ( int )mem!=-1 )
                    {
                        printf( "Shared memory was attached in our address space at %p\n",mem );
                        //向共享区段内存写入数据
                        strcpy( ( char* )mem,"This is a test string.\n" );
                        printf( "%s/n",(char*)mem );
                        //脱离共享内存区段
                        ret=shmdt( mem );
                        if( ret==0 )
                            printf( "Successfully detached memory \n" );
                        else
                            printf( "Memory detached failed %d\n",errno );
                    }
                else
                    printf( "shmat(  ) failed\n" );
                
            }
        else
            printf( "shared memory segment not found\n" );
        return 0;
    }

共享内存实例:(转载)

创建两个进程,在A进程中创建一个共享内存,并向其写入数据,通过B进程从共享内存中读取数据

#define TEXT_SZ 2048

struct shared_use_st
{
    int written_by_you;
	char some_text[TEXT_SZ];
};


读取进程(B进程)
/**********************************************************
*功能描述: 本程序申请和分配共享内存,然后轮训并读取共享中的数据,直至
*           读到“end”。
**********************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.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);
}

写入进程(A进程)

/**********************************************************
*功能描述: 本程序申请了上一段程序相同的共享内存块,然后循环向共享中
*           写数据,直至写入“end”。

**********************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.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);
}






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值