`
primer_2004
  • 浏览: 127983 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

IPC通信--共享内存

    博客分类:
  • unix
阅读更多

共享内存(Shared Memory,下简称SHM)是指由一个进程创建并可与其他进程共享的内存,UNIX系统中利用SHM可以实现进程间通信(IPC)。为了对系统共享资源(包括SHM)进行访问的互斥控制,就要用到信号量(Semaphore)机制。系统要求进程在存取共享内存之前应该先获得相应的信号量的控制。在编程中,共享内存常常要与信号量产生相应的关系。

 

一. 共享内存的创建与控制
      UNIX
系统开发软件包提供了一系列的函数来实现共享内存的创建与控制。比如调用shmget函数用于创建共享内存;调用shmctl函数用于控制共享内存。例程如下:

 

 #define SHMKEY Ox1688 
 #define NODENUM 20

 int SHM_id;
 struct shmid_ds shm_f;

 if((SHM_id=shmget((ket_t)SHMKEY,NODENUM*sizeof(struct pidtos),IPC_CREAT|IPC_EXCL|0660))<0) 
        prinf("Create Shared Memory Fail!"); 

shm_f.shm_perm.uid=220; /*有效用户主标识*/ shm_f.shm_perm.gid=110; /*有效用户组标识*/ shm_f.shm_perm.mode=0660; /*操作许可*/ if(shmctl(SHM_id,IPC_SET,&shm_f)<0)   printf("Set Shard Memory Error");

 

 

函数shmget的调用格式如下:
            int shmget(key,size,shmflg)
            key_t  key;   /*SHM
关键值
*/
            unsigned  int size;  /*SHM
长度
*/
            int  shmflg;  /*
标志
*/
    作用是创建共享内存或获取已创建的共享内存的标识符。实参shmflg的值必须是一个
整数,且规定下列内容:(1)访问权限,(2)执行方式,(3)控制字段。在创建SHM,该标志可设IPC_CREAT(创建,值是01000) | IPC_EXCL(限制唯一创建,值是02000) | 0660(访问权限)。成功时返回创建的共享内存标识符,失败时返回-1。实参size的值应大于SHMMIN且小于SHMMAX,否则该系统调用失败。当shmflg=0,用于获取已存在的SHM的标识符。
      函数shmctl的调用格式如下:
               int shmctl(shmid,cmd,sbuf)
               int shmid;  /*
shmget获取的SHM的标识符
*/
               int cmd;  /*
将对SHM进行的控制命令
*/
               struct shmid_ds *sbuf;  /*
存放操作数
*/
    作用是对共享内存进行由cmd指定的控制操作。cmd的值比较有用的是下面两个:

             IPC_SET
对于规定的shmid,设置有效用户和组标识及操作权限。

             IPC_RMID
连同其相关的SHM段数据结构一起,删除规定的shmid

执行IPC_SETIPC_RMID的进程必须具有Owner/Creator或超级用户的有效用户标识。
    系统创建的SHM仅仅是内存中一块连续的区域,本身并没有结构。用户进程对共享内存不能直接进行存取,需要将共享内存附接在进程的数据段上,进程才能对其进行存取,实现方法是:用户进程可以根据需要自行定义一个数据结构(pidtos),然后将共享内存的地址用函数shmat赋值给指向结构pidtos的指针buf,相当于是给指针变量分配内存,buf指向共享内存的起始处。然后就可用数组的方法,按数据结构的长度等分共享内存。这个过程可称之为共享内存的"结构化"。例程如下:

 

 struct pidtos{ 
      char rhostname[10];
      long pidsc; 
}*buf;
 int i; 

if((buf=(struct pidtos*)shmat(SHM_idm,(char*)0,0))<0) 
       printf("Access SHM Error!");

 for(i=0;i<NODENUM;I++){
        strcpy(buf[i].rhostname,""); 
        buf[i].pidsc=0;
}  /*如果有必要,就初始化SHM*/

 shmdt((char*)buf);  /*拆接SHM,释放指针buf*/ 

 

   函数shmatshmdt的调用格式:
              char *shmat(shmid,shmaddr,shmflg)
              int shmid;   /*SHM
标识符
*/
              char  *shmaddr;  /*
相当于偏移量
*/
              int shmflg;  /*
标志
*/
   作用是将共享内存附接到进程的数据段上。实际上是将共享内存在主存中的地址
+shmaddr后赋值给进程中的某一指针。shmaddr相当于偏移量,相对于共享内存在主存中的起始地址。调用失败时返回(char*)-1。shmflg可取值为0,或者是SHM_RNDSHM_EDONLY中的一个或两者的或。
           int shmdt(shmaddr)
          char *shmaddr; /*
采用shmat函数的返回值*/
     作用是拆接共享内存段,成功时返回0,失败时返回-1

 

#define SEMKEY 0x1680 
int SEM_id; 
struct semid_ds sem_f; 
union semun{ 
      int val; 
      struct semid_ds *buf; 
      unsigned short array[1]; /*[]中的值应根据信号量数目具体定义*/ 
}arg; 

if((SEM_id=semget((key_t)SEMKEY,1,IPC_CREAT|IPC_EXCL;0660)<0) 
        printf("Creat Semaphore Fail!");

 arg.val=0; 
if(semctl(SEM_id,0,SETVAL,arg.val)<0) 
       printf("Access Semaphore Error!");  /*信号量的值必须初始化。将编号为0的信号量的值初始化为0*/ 

arg.buf=&sem_f; 
sem_f.sem_perm.uid=220; /*有效用户主标识*/ shm_f.shm_perm.gid=110; /*有效用户组标识*/ shm_f.shm_perm.mode=0660; /*操作许可*/

if(semctl(SEM_id,IPC_SET,arg)<0)   printf("Set Semaphore Error!"); 

  

上述例程首先设置一个信号量组的关键值(SEMKEY),然后调用semgetr利用该关键值创建只有一个信号量的信号量组。其中第三个参数的含义与函数shmget第三个参数的含义一样。
      函数semget的调用格式:
               int semget(key,nsems,semflg)
               key_t  key;  /*
信号量组的关键值
*/
               int  nsems;   /*
信号量个数
*/
               int semflg;   /*
信号量组的创建标志
*/
    用来创建一个信号量组,其中包含nsems个信号量,编号从0nsems-1;创建方式及访问
权限由semflg指定。成功时初始化相应的控制块信息,并返回创建的信号量组的标识符,错时返回-1。当semflg=0时用于获取已存在的信号量的标识符。
        函数semctl的调用格式:
                int semctl(semid,semnum,cmd,arg)
                int semid;/*
信号量组的标识符
*/
                int semnum;/*
信号量的编号
*/
                int cmd;/*
控制命令
*/
               union semun{
                      int val;
                      struct semid_ds *buf;
                      unsigned short array[nsems];  /*nsems
具体根据信号量的数目定义
*/
               }arg;  /*
操作数
*/
     作用是对指定的信号量组或组中编号为semnum的信号量进行由cmd指定的控制操作。比
较有用的cmd命令如下:
      SETVAL 将信号量(semid,semnm)的当前值置为arg.val的值,常用于初始化某个信号量。
      IPC_SET
将信号量组的状态信息设置成arg.buf中的内容。
      IPC_RMID
删除信号量组标识符semid 。

 

struct sembuf{ 
      int sem_num; /*信号量编号*/
      int sem_op; /*信号量操作数*/
      int sem_flg; /*操作标志*/ 
}; 

static struct sembuf sem_lock[2]=(0,0,0,0,1,0); 

if(semop(SEM_id,&sem_lock[0],2)<0) 
      printf("Access Semaphore Error!"); /*加锁,其中结构sembuf由系统定义*/ 

  

数组sem_lock[2]可以看作是sem_lock[0]=(0,0,0)sem_lock[1]=(0,1,0)的组合。
上面的例程实际上是两步合为一步来做,是对同一个信号量(编号为0)做两次不同的操作。
经过这一步后,实际上可以看作是已经将SHM加锁,进程接下来就可以创建或存取SHM了。如
果其他进程此时想占用SHM,就必须等待。当操作完成后,为了其他进程可以存取SHM,就要释放资源,将信号量的值重新清零,即一个解锁的过程。

 

static struct sembuf sem_unlock[1]=(0,-1,IPC_NOWAIT);

if(semop(SEM_id,&sem_unlock[0],1)<0)
       printf("Access Semaphore Error!");/*解锁*/

 

        在此之前,信号量的值为1,小于等于-1的绝对值,于是就将信号量减1,重置为0,表示资源已经释放。如果信号量的当前值为0,小于-1的绝对值,因为标志设为IPC_NOWAIT,就会立即返回,此时因为信号量的值为0,表示资源空闲,也就无所谓解锁了。

 

显然对于共享内存的互斥控制采用的是VP算法。该算法还有另一种实现方式:用信号量
1表示资源空闲,信号量为0表示资源占用,正好与上面的做法相反。在这种情况下,应将信号量的值初始化为1。例程如下:

 

static struct sembuf sem_lock[1]=(0,-1,0),sem_unlock[1]=(0,1,0);

semop(SEM_id,&sem_lock[0],1);/*解锁*/

 

 

 

     加锁时,如果信号量当前值为1(资源空闲),那么就减1,这时信号量的值变为0,表示资源已经被占用。如果信号量当前值为0(资源占用),因为0小于-1的绝对值,于是进程开始睡眠,直到该信号量变为1,才被系统唤醒。解锁时,如果信号量当前值为0,那么就加1,这时信号量的值为1,表示资源空闲。这种方式更加简单一些。
        函数semop的调用格式:
                 int semop(semid,sops,nsops)
                 int  semid;   /*
信号量组标识符
*/
                 struct sembuf  **sops;  /*
指向信号操作量数缓冲区
*/
                 unsigned  nsops;   /*
操作的信号量信数
*/
        功能是完成对标识符为semid的信号量组中若干信号量的操作,根据sops[i].sem_flg
操作数sops[i].sem_op,对编号为sops[i].sem_num的信号量进行操作。一共要做nsops次这样的操作,这个过程可称之为信号量的"块操作",类似批处理方法。

 

利用上面的三个函数,可以实现对共享资源的互斥访问,也可设计出具有复杂同步操作要求的并发程序。

 

,共享内存的实际应用 

 

     我们假设一个初始化程序(程序init)已经完成了SHM及信号量组的创建过程。这时不的进程可用相同的关键值(SHMKEY)去存取共享内存,互斥访问也用相同的信号量去控制,键值是SEMKEY。我们假设进程(程序progl)是一个与远程主机连接的通信进程,其所要连接的远程主机名由命令行参数argv[1]指定。进程将远程主机名和本身的进程号在HM中登记或更新。如: 

 

SEM_id=semget((key_t)SEMKEY,1,0);
semop(SEM_id,&sem_lock[0],2);  /*加锁*/

SHM_id=shmget((key_t)SHMKEY,NODENUM*sizeof(struct pidtos),0);
buf=(struct pidtos*)shmat(SHM_id,(char*)0,0);

for(i=0;i<NODENUM;i++){
       if(srcmp(buf[i].rhostname,argv[1]==0) break;
       if(buf[i].pidsc==0) break;
       if(i==NODENUM) return(-1);
       strcpy(buf[i].rhostname,argv[1]);
       buf[i].pidsc=getpid();
}  /*操作部分结束*/

shmdt((char *)buf);   /*拆接SHM*/
semop(SEM_id,&sem_unlock[0],1);  /*解锁*/

 

这样相同的一批进程带不同的命令行参数运行后,就在SHM中登记了一批远程主机名和与之相连的通信进程的进程号。下面我们就可让进程(程序prog2)依据远程主机名(Remote Host)SHM中查出与该主机相连的通信进程的进程号,以实现对这些通信进程的管理。只要将上面例程中的操作部分换成下面的程序段即可。

for(i=0;i<NODENUM;i++){
       if(strcmp(buf[i].rhostname,RemoteHost)==0)    break;
       if(i==NODENUM)  pid=-1;
       else   pid=buf[i].pidsc;
}

 

    

总之,不同进程通过相同的关键值SHMKEYSEMKEY,来获取共享内存及信号量的标识符,然后使用标识符分别对它们进行操作。相同的关键值是实现不同进程共享资源的基础与前提

 

 

 

,有关安全性的问题

 
    所谓安全性问题指的是在一个进程将共享内存加锁以后,由于某种原因该进程停止运,没能执行解锁操作,从而使SHM一直处于被锁状态,致使其他进程无法使用SHM。为了尽量避免出现这种情况,我们可以将一些系统信号忽略掉。具体做法是先定义一些函数指针,原来系统对这些信号的处理功能暂放函数指针中,然后设置对这些信号的处理方式为SIG_IGN()。如:

int (*f1)();
int (*f2)();
int (*f3)();
int (*f4)();

f1=signal(SIGINT,SIG_IGN);
f2=signal(SIGTERM,SIG_IGN);
f3=signal(SIGQUIT,SIG_IGN);
f4=signal(SIGHUP,SIG_IGN);

 

 

    在对SHM操作后,无论成功与否都要将对这些系统信号原来的处理功能恢复过来。如:
    在对SHM操作后,无论成功与否都要将对这些系统信号原来的处理功能恢复过来。如:

 

    在对SHM操作后,无论成功与否都要将对这些系统信号原来的处理功能恢复过来。如:

 

    在对SHM操作后,无论成功与否都要将对这些系统信号原来的处理功能恢复过来。如:

 

    在对SHM操作后,无论成功与否都要将对这些系统信号原来的处理功能恢复过来。如:

 

signal(SIGINT,f1); 
signal(SIGTERM,f2);
signal(SIGQUIT,f3); 
signal(SIGHUP,f4);

 

 

分享到:
评论

相关推荐

    VB进程通信-内存共享

    - **通信**:进程通信(IPC, Inter-Process Communication)是指不同进程间交换信息的方式,包括共享内存、管道、消息队列、信号量、套接字等。 2. **内存共享机制**: - 共享内存是一种高效的IPC方法,因为它...

    C#进程间通信-共享内存代码实例

    在C#中,实现进程间通信有多种方法,如管道、套接字、消息队列等,而共享内存是其中一种高效且直接的方式。本篇文章将深入探讨C#中的共享内存实现,并通过一个具体的代码实例来阐述其工作原理。 共享内存是一种让多...

    cpp-IPC是一个C库它使用Windows上的共享内存提供进程间通信

    IPC是一个C 库,它使用Windows上的共享内存提供进程间通信

    windows-RTX.rar_IPC内存共享_RTX 共享内存_RTX与Windows_WINDOWS R_rtx ipc

    在Windows系统中,可以使用内存映射文件(Memory-Mapped Files)或者创建共享内存段来实现这一功能。这种方式的优点在于高效、低开销,尤其适用于频繁的数据交换。 在Windows RTX环境下,内存共享可能需要额外的...

    C#进程间通信之共享内存

    本文将重点探讨在C#环境中如何利用共享内存进行进程间通信,这对于理解和优化多进程应用的性能至关重要。 共享内存是一种高效的IPC机制,它允许多个进程直接读写同一块内存区域,而无需通过任何中间媒介。在C#中,...

    共享内存的进程间通信

    共享内存是一种高效的进程间通信(IPC)机制,它允许不同的进程访问同一块内存空间,从而实现数据的快速交换。在Windows环境下,如VC6.0这样的开发工具可以用来编写和测试这种通信方式的代码。 首先,我们要理解...

    IPC inter-process-communicate

    而对于大量数据或需要复杂数据结构的通信,消息队列和共享内存可能更为合适。 ### 5. 性能与安全考虑 在实现 IPC 时,不仅要关注性能,还要注意安全性。例如,确保数据的完整性,防止未经授权的访问,以及正确处理...

    linux通过共享内存实现进程之间的通信

    共享内存作为一种高效的过程间通信(IPC)方式,允许多个进程直接访问同一段物理内存。这种方法的优势在于减少了数据复制的开销,使得进程间的数据交换更为快速。然而,由于多个进程能够直接访问相同的内存区域,因此...

    IPC库:在Linux/Windows上使用共享内存的高性能进程间通信。- mutouyun / cpp-ipc

    IPC库:在Linux/Windows上使用共享内存的高性能进程间通信。- mutouyun / cpp-ipc-源码

    Linux多进程通信-信号量,共享内存示例

    本示例通过信号量和共享内存这两种IPC机制,展示了如何在多个进程中有效地协同工作。信号量用于同步对共享资源的访问,而共享内存则提供了进程间直接读写的共享数据区域。 信号量是一种同步机制,它是一个整型变量...

    进程间通信 IPC :共享内存机制

    进程间通信 IPC:共享内存机制 进程间通信(IPC)是指在多个进程之间交换数据和信息的机制。共享内存机制是一种常用的IPC方法,它允许多个进程共享同一个内存区域,实现进程之间的数据交换。 1. fork() 系统调用:...

    arm5718的ipc-dsp,ipc-ipu通讯

    常见的IPC机制包括共享内存、管道、消息队列、套接字等。在arm5718中,由于需要高速传输和低延迟,可能会使用优化过的消息队列或共享内存方案。例如,"ex02_messageq_dsp1.out"可能就是展示了如何使用优化的消息队列...

    linux讲解通过共享内存实现进程间的通信

    ### Linux共享内存实现进程间通信详解 #### 一、引言 在Linux系统中,进程间的通信(Inter-Process Communication, IPC)是一项重要的技术,它允许不同进程之间交换数据和同步执行。其中一种高效的进程间通信方法是...

    linux进程间通信--IPC

    在Linux环境下,进程间通信支持多种机制,包括但不限于管道、有名管道、信号量、消息队列、共享内存、信号以及套接字等。每种通信方式都有其特定的应用场景和优势。 #### 管道 管道是一种半双工的通信方式,意味着...

    VC++实现两个进程间的通信--DLL

    Windows提供了多种IPC机制,如管道、消息队列、共享内存、套接字、命名管道、事件、信号量等。 - 在这个示例中,DLL作为通信的媒介,可能使用了消息队列、共享内存或其他方法来传递数据。 3. VC++中的DLL开发: -...

    深刻理解Linux进程间通信(IPC)-详解.doc

    在Linux系统中,IPC主要包括多种通信手段,如管道(Pipe)、有名管道(Named Pipe)、信号(Signal)、消息队列、共享内存、信号量(Semaphore)以及套接字(Socket)。 1. **管道**和**有名管道**: - 管道是一种...

    进程间通讯---共享内存的使用方法

    共享内存是一种高效的进程间通信(IPC,Inter-Process Communication)方式,它允许不同的进程直接读写同一块内存区域,无需通过任何中介。这种方式避免了数据复制,因此在速度上具有显著优势。本教程将深入探讨如何...

    PHP与C++进行IPC命名管道共享内存通信

    1. 锁管理:由于共享内存可能被多个进程同时访问,因此需要使用互斥锁(mutex)或者信号量(semaphore)来确保数据的一致性。 2. 错误处理:在处理管道和共享内存时,可能出现各种错误,如创建失败、读写错误等,...

    共享内存,进程通信,进程同步 代码

    共享内存是操作系统中进程间通信(IPC,Inter-Process Communication)的一种有效机制,它允许不同的进程访问同一块存储区域,从而实现数据的快速交换。在Windows系统中,Visual Studio 2005提供了丰富的API来支持...

Global site tag (gtag.js) - Google Analytics