现在的位置: 首页 > 综合 > 正文

linux学习之二十二—进程间通信共享内存

2016年08月08日 ⁄ 综合 ⁄ 共 4401字 ⁄ 字号 评论关闭

共享内存

1.共享内存就是分配一块能被其他进程访问的内存。每个共享内存段在内核中维护着一个内部结构shmid_ds(和消息队列、信号量一样),该结构体定义在头文件linux/shm.h中。

2.共享内存的创建

linux下使用函数shmget来创建一个共享内存区,或者访问一个已存在的共享内存区。该函数定义在头文件linux/shm.h中,原型如下:
int shmget(key_t key,size_t size,int shmflg);
函数中:参数key是由ftok()得到的键值;参数size以字节为单位指定内存的大小;shmflg为操作标志位,它的值为一些宏。

shmflg取:
IPC_CREAT:调用shmget时,系统将为此值与其他所有共享内存区的键值进行比较,如果存在相同的key,说明共享内存区已经存在,此时返回该共享内存区的标志符,否则新建一个共享内存区并返回其标志符。
IPC_EXCL:该宏必须和IPC_CREAT一起使用,否则没有意义。当shmflg取IPC_CREAT|IPC_EXCL时,表示如果发现信号集已经存在,则返回-1,错误吗为EEXIST。

注意:档创建一个新的共享内存区时,size值必须大于0;如果是访问一个已经存在的共享内存区,置size为0。

3.共享内存区的操作

(1)在使用共享内存区前,必须通过shmat函数将其附加到进程的地址空间。进程与共享内存就建立了连接。shmat调用成功后就会返回一个指向共享内存区的指针,使用该指针就可以访问共享内存区了,如果失败返回-1。该函数声明在linux/shm.h文件中,原型如下:
void *shmat(int shmid,const void* shmaddr,int shmflg);
参数shmid为shmget的返回值;
参数shmflg为存取权限标志;
参数shmaddr为共享内存的附加点。参数shmaddr不同的取值情况的含义说明如下:

如果为空,则由内核选择一个空闲的内存区;
如果非空,返回地址取决于调用者是否给shmflg参数指定了SHM_RND值,如果没有指定,则共享内存区附加到由shmaddr指定的地址;否则附加地址为shmaddr向下舍入一个共享内存低端边界地址后的地址(SHMLAB,一个常址)。
通常将参数shmaddr设置为NULL。

(2)当进程结束使用共享内存区时,要通过函数shmdt断开与共享内存区的连接。该函数声明在sys/shm.h文件中,原型如下:
int shmdt(const void* shmaddr);
参数shmaddr为shmat函数的返回值。该函数调用成功后,返回0,否则返回-1。进程脱离共享内存区后,数据结果shmid_ds中的shm_mattch就会减1。但是共享内存段依然存在,只有shm_mattch为0后,即没有任何进程再使用该共享内存区,共享内存区才在内核中被删除。一般来说,当一个进程终止时,它所附加的共享内存区都会自动脱离。

4.共享内存区的控制

Linux对共享内存区的控制是通过调用函数shmctl来完成的,该函数定义在头文件sys/shm.h中,原型如下:
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
函数中:
参数shmid为共享内存区的标志符;
参数buf为指向shmid_ds结构体的指针;
参数cmd为操作标志位。支持以下3中控制操作:
IPC_RMID:从系统中删除由shmid标识的共享内存区;
IPC_SET:设置共享内存区的shmid_ds结构;

IPC_STAT:读取共享内存区的shmid_ds结构,并将其存储到buf指向的地址。

5.示例代码

sharemem.h

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include<errno.h>

#define SHM_SIZE 1024

union semun{
      int             val;
      struct semid_ds *buf;
      unsigned        *array;
};

/*创建信号量函数*/
int createsem(const char* pathname,int proj_id,int members,int init_val)
{
     key_t msgkey;
     int index,sid;
     union semun semopts;
     
     if((msgkey=ftok(pathname,proj_id))==-1)
     {
         perror("ftok error!\n");
         return -1;
     }   
     
     if((sid=semget(msgkey,members,IPC_CREAT|0666))==-1)
     {
         perror("semget call failed.\n");
         return -1;
     }
    
     /*初始化操作*/
     semopts.val=init_val;
     for(index=0;index<members;index++)
     {
          semctl(sid,index,SETVAL,semopts);
     }
     return (sid);
} 

/*打开信号量函数*/
int opensem(const char *pathname,int proj_id)
{
     key_t msgkey;
     int   sid;
     
     if((msgkey=ftok(pathname,proj_id))==-1)
     {
          perror("ftok error!\n");
          return -1;
     }
     
     if((sid=semget(msgkey,0,IPC_CREAT|0666))==-1)
     {
          perror("semget call failed,\n");
          return -1;
     }
     
     return (sid);
}

/*P操作函数*/
int sem_p(int semid,int index)
{
     struct sembuf buf={0,-1,IPC_NOWAIT};
     
     if(index<0)
     {
         perror("index of array cannot equals a minus value!\n");
         return -1;
     } 
    
     buf.sem_num=index;
     if(semop(semid,&buf,1)==-1)
     {
         perror("a wrong operation to semaphore occurred!\n");
         return -1;   
     }
     return 0;
}

/*V操作函数*/
int sem_v(int semid,int index)
{
     struct sembuf buf={0,1,IPC_NOWAIT};
     
     if(index<0)
     {
         perror("index of array cannot equals a minus value!\n");
         return -1;
     }
     
     buf.sem_num=index;
     if(semop(semid,&buf,1)==-1)
     {
          perror("a wrong operation to semaphore occurred!\n");
          return -1;
     }
     
     return 0;   
}

/*删除信号集函数*/
int sem_delete(int semid)
{
     return (semctl(semid,0,IPC_RMID));
}

/*等待信号为1*/
int wait_sem(int semid,int index)
{
     while(semctl(semid,index,GETVAL,0)==0)
     {
          sleep(1);
     }
     return 1;
}

/*创建共享内存函数*/
int createshm(char *pathname,int proj_id,size_t size)
{
     key_t shmkey;
     int   sid;
    
     /*获取键值*/
     if((shmkey=ftok(pathname,proj_id))==-1)
     {
          perror("ftok eror!\n");
          return -1;
     }

     if((sid=shmget(shmkey,size,IPC_CREAT|0666))==-1)
     {
          perror("shmget call failed.\n");
          return -1;
     }
     return (sid);
}   

writer.c

#include"sharemem.h"
#include<string.h>

int main()
{
    int semid,shmid;
    char *shmaddr;
    char write_str[SHM_SIZE];
   
    if((shmid=createshm(".",'m',SHM_SIZE))==-1)
    {
       exit(1);
    }
    
    if((shmaddr=shmat(shmid,(char*)0,0))==(char*)-1)
    {
       perror("attach shared memory error!\n");
       exit(1); 
    }
   
    if((semid=createsem(".",'s',1,1))==-1)
    {
       exit(1);
    }
    
    while(1)
    {
         wait_sem(semid,0);
         sem_p(semid,0);
      
         printf("writer:");
         fgets(write_str,1024,stdin);
         int len=strlen(write_str)-1;
         write_str[len]='\0';
         strcpy(shmaddr,write_str);
         sleep(10);
         
         sem_v(semid,0);
         sleep(10);  
    }
}

reader.c

#include"sharemem.h"
#include<string.h>

int main()
{
     int semid,shmid;
     char *shmaddr;
         
     if((shmid=createshm(".",'m',SHM_SIZE))==-1)
     {
         exit(1);
     } 
     
     if((shmaddr=shmat(shmid,(char*)0,0))==(char*)-1)
     {
         perror("attach shared memory error!\n");
         exit(1);
     }
    
     if((semid=opensem(".",'s'))==-1)
     {
         exit(1);
     }
     
     while(1)
     {
         printf("reader: ");
         wait_sem(semid,0);
         sem_p(semid,0);
          
         printf("%s\n",shmaddr);
         sleep(10);
         
         sem_v(semid,0);
         sleep(10);       
     }  
}

运行结果分析:

运行后发现,writer进程与reader进程是同步的,writer写入的信息被reader完成正确的读出。

注意:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如前面说到的信号量。本例就采用信号量来实现对临界资源的互斥访问。

抱歉!评论已关闭.