Linux进程间通信之共享内存

来源:互联网 发布:上日本女人的体验知乎 编辑:程序博客网 时间:2024/04/27 19:32
      共享内存区是可用IPC形式中最快的。一旦这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传递就不再涉及内核(这里说的不涉及内核的含 义是:进程不再通过执行任何进入内核的系统调用来彼此传递数据)。然而往该共享内存区存放信息或从中取走信息的进程间通常需要某种形式的同步,同步的方式 有多种,比如:信号量、互斥锁等等。
 
以下两图分别描述了读写消息时,一个要进入内核,而一个不进入内核的情况:

对于System V共享内存区,内核维护如下的信息结构,它定义在<sys/shm.h>头文件中:

struct shmid_ds{

struct ipc_perm      shm_perm;    /* operation permission struct */

size_t              shm_segsz;     /* segment size */


};

 

有了以上的知识,那么如何来对共享内存进行操作呢,以下就开始讲解如何来操作:

创建一个新的共享内存区,或者访问一个已存在的共享内存区。 
#include <sys/shm.h>

int shmget(key_t key, size_t size, into flag);


     该函数的第一个参数是一个用来标识共享内存块的键值。彼此无关的进程可以通过指定同一个键以获取对同一个共享内存块的访问。不幸的是,其它程序也可能挑选了同样的特定值作为自己分配共享内存的键值,从而产生冲突。用特殊常量 IPC_PRIVATE 作为键值可以保证系统建立一个全新的共享内存块。 该函数的第二个参数指定了所申请的内存块的大小。因为这些内存块是以页面为单位进行分配的,实际分配的内存块大小将被扩大到页面大小的整数倍。第三个参数是一组标志,通过特定常量的按位或操作来 shmget。

     size以字节为单位指定内存区的大小。当实际操作为创建一个新的共享内存区时,必须指定一个不为0的size值。如果实际操作为访问一个已存在的共享内存区,那么size应为0。
     flag为读写权限值的组合。它还可以与IPC_CREAT或IPC_CREAT|IPC_EXCL按位或。
     当实际操作为创建一个共享内存区时,该内存区被初始化为size字节的0。

由shmget创建或打开一个共享内存区后,通过调用shmat把它附接到调用进程的地址空间。 
#include <sys/shm.h>

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

shmid是由shmget返回的标识符。Shmat的返回值是所指定的共享内存区在调用进程内的起始地址。确定这个地址的规则如下:

如果shmaddr是一个空指针,那么系统替调用者选择地址。(这个是推荐的方法) 
如果shmaddr是一个非空指针,那么返回地址取决于调用者是否给flag参数指定了SHM_RND: 
如果没有指定SHM_RND,那么相应的共享内存区附接到由shmaddr参数指定的地址; 
如果指定了SHM_RND,那么相应的共享内存区附接到由shmaddr参数指定的地址向下舍入一个SHMLBA常值。 
当一个进程完成共享内存区的使用时,它可调用shmdt断接这个内存区。 
#include <sys/shm.h>

int shmdt(const void *shmaddr);

当一个进程终止时,它当前附接着的所有共享内存区都自动断接掉。

注意:本函数调用并不删除所指定的共享内存区。

shmctl提供了对一个共享内存区的多种操作。 
#icnldue <sys/shm.h>

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

该函数提供了三个命令:

IPC_RMID  从系统中删除由shmid标识的共享内存区并拆除它。

IPC_SET    给所描写的共享内存区设置其shmid_ds结构的某些成员。

IPC_STAT   向调用者返回所指定共享内存区当前的shmid_ds结构。

 

以下是共享内存结合信号量进行操作的部份代码:

 

发关进程的部份代码: 
//创建一个共享内存区

if((shmid1 = shmget(shmkey1, MAX_SHEARE_MEM_SIZE, IPC_CREAT|0666)) < 0)

{

           perror("shmget");

}

//把共享内存区附接到调用进程的地址空间

if((sharmem = shmat(shmid1, NULL, 0)) < 0)

{

           perror("shmat");

}

 

while(1)

{

           sem_p(semid1);   //semid1进行p操作,保护共享区

           memset(buff, 0, MAX_SHEARE_MEM_SIZE);

           printf("Input ou want to say with you friend!\n");

           fgets(buff, MAX_SHEARE_MEM_SIZE, stdin);

           if(strncmp(buff, "quit", 4))

           {

                    //往共享内存区放入数据

                    strncpy(sharmem, buff, MAX_SHEARE_MEM_SIZE);

                    sem_v(semid2);  //semid2进行v操作,释放对共享区的保护

           }

           else

           {

                    strncpy(sharmem, buff, MAX_SHEARE_MEM_SIZE);

                    sem_v(semid2);

                    break;

           }

}

接收进程部份代码: 
while(1)

{

           sem_p(semid2);  //semid2进行p操作,保护共享区

           memset(buff, 0, MAX_SHEARE_MEM_SIZE);

           strncpy(buff, sharmem, MAX_SHEARE_MEM_SIZE); //从共享内存区取出数据

           printf("receive from you friend : %s\n", buff);

           if(!strncmp(buff, "quit", 4))

           {

                    del_sem(semid1);

                    del_sem(semid2);             

                    break;

           }

           sem_v(semid1);  //semid1进行v操作,释放对共享区的保护

}

//删除共享内存区

if((shmctl(shmid1, IPC_RMID, NULL)) < 0)

{

           perror("shmctl");

}


//------------------------------------自己写的程序------------------------------------

#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>//strcpy()
#define KEY 1234
#define SIZE 1024

int main(void)
{
    int shmid;
    char* shmaddr;
    struct shmid_ds buf;
    shmid = shmget(KEY,SIZE,IPC_CREAT|0600);//建立共享内存
    if(fork()==0)
    {
        shmaddr = (char*)shmat(shmid,0,0);
        strcpy(shmaddr,"Hi!I'm child process!");
        shmdt(shmaddr);
        return 1;
    }
    else
    {
        sleep(1);
        shmctl(shmid,IPC_STAT,&buf);/*取得共享内存的状态*/
        printf("shm_segsz=%dbytes\n", buf.shm_segsz);
                printf("shm_cpid=%d\n", buf.shm_cpid);
                printf("shm_lpid=%d\n", buf.shm_lpid);
                shmaddr = (char*) shmat(shmid, 0, SHM_RDONLY);
        printf("Father:%s\n", shmaddr);        /*显示共享内存内容*/
                shmdt(shmaddr);
                shmctl(shmid, IPC_RMID, NULL);         /*删除共享内存*/
    }
}

原创粉丝点击