快捷搜索:

当一个进程改变了这块地址中的内容的时候,由

来源:http://www.fab119.com 作者:关于我们 人气:110 发布时间:2020-01-05
摘要:/*    共享内存的写程序 1.首先调用ftok()函数将一个路径名转化为key 2.调用shmget();函数,来返回一个共享内存地址 3.调用shmat()函数映射共享内存到本进程内存空间 4.调用shmdt()函数删除共

/*
   共享内存的写程序
1.首先调用ftok()函数将一个路径名转化为key
2.调用shmget();函数,来返回一个共享内存地址
3.调用shmat()函数映射共享内存到本进程内存空间
4.调用shmdt()函数删除共享内存.
*/
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SIZE 4096

深入理解进程间通信之共享内存,深入理解进程

  共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。是针对其他通信机制运行效率较低而设计的。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。

  采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

概述

共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。

澳门新葡亰平台9411 1

共享内存的特点:
1)共享内存是进程间共享数据的一种最快的方法。

 一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。

 

2)使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。

若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。

 

 

每个进程各自有不同的用户地址空间,任何一个进 程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲 区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)

typedef struct
{
    char name[4];
    int age;
}people;

系统V共享内存原理

  进程间需要共享的数据被放在一个叫做IPC共享内存区域的地方,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。系统V共享内存通过shmget获得或创建一个IPC共享内存区域,并返回相应的标识符。内核在保证shmget获得或创建一个共享内存区,初始化该共享内存区相应的shmid_kernel结构体的同时,还将在特殊文件系统shm中,创建并打开一个同名文件,并在内存中建立起该文件的相应dentry及inode结构,新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。所有这一切都是系统调用shmget完成的。

注:每一个共享内存区都有一个控制结构struct shmid_kernel,shmid_kernel是共享内存区域中非常重要的一个数据结构,它是存储管理和文件系统结合起来的桥梁,定义如下:

struct shmid_kernel /* private to the kernel */
{    
    struct kern_ipc_perm shm_perm; /* operation permission structure */
    struct file *shm_file; /* pointer in kernel */
    unsigned long shm_nattch; /* number of current attaches */
    unsigned long shm_segsz; /* size of segment in bytes */
    time_t shm_atim; /* last-attach time */
    time_t shm_dtim; /* last-detach time */
    time_t shm_ctim; /* last-change time */
    pid_t shm_cprid; /* pid of creator */
    pid_t shm_lprid; /* pid of last shmop() */
};

 澳门新葡亰平台9411 2

  

  正如消息队列和信号灯一样,内核通过数据结构struct ipc_ids shm_ids维护系统中的所有共享内存区域。上图中的shm_ids.entries变量指向一个ipc_id结构数组,而每个ipc_id结构数组中有个指向kern_ipc_perm结构的指针。到这里读者应该很熟悉了,对于系统V共享内存区来说,kern_ipc_perm的宿主是 shmid_kernel结构,shmid_kernel是用来描述一个共享内存区域的,这样内核就能够控制系统中所有的共享区域。同时,在 shmid_kernel结构的file类型指针shm_file指向文件系统shm中相应的文件,这样,共享内存区域就与shm文件系统中的文件对应起来。

  在创建了一个共享内存区域后,还要将它映射到进程地址空间,系统调用shmat()完成此项功能。由于在调用shmget()时,已经创建了文件系统 shm中的一个同名文件与共享内存区域相对应,因此,调用shmat()的过程相当于映射文件系统shm中的同名文件过程,原理与mmap()大同小异。

常用函数

1)创建共享内存

所需头文件:

#include <sys/ipc.h>

#include <sys/shm.h>

 

 

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

功能:

创建或打开一块共享内存区。

 

参数:

key:进程间通信键值,ftok() 的返回值。

size:该共享存储段的长度(字节)。

shmflg:标识函数的行为及共享内存的权限,其取值如下:

 

IPC_CREAT:如果不存在就创建

IPC_EXCL:  如果已经存在则返回失败
位或权限位:共享内存位或权限位后可以设置共享内存的访问权限,格式和 open() 函数的 mode_t 一样(open() 的使用请点此链接),但可执行权限未使用。

 

 

返回值:

成功:共享内存标识符。

失败:-1。

 

 

示例代码如下:

 

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <unistd.h>  
  5. #include <sys/types.h>  
  6. #include <sys/ipc.h>  
  7. #include <sys/shm.h>  
  8.   
  9. #define BUFSZ 1024  
  10.   
  11. int main(int argc, char *argv[])  
  12. {  
  13.     int shmid;  
  14.     key_t key;  
  15.       
  16.     key = ftok("./", 2015);   
  17.     if(key == -1)  
  18.     {  
  19.         perror("ftok");  
  20.     }  
  21.       
  22.     //创建共享内存  
  23.     shmid = shmget(key, BUFSZ, IPC_CREAT|0666);   
  24.     if(shmid < 0)   
  25.     {   
  26.         perror("shmget");   
  27.         exit(-1);   
  28.     }   
  29.   
  30.     return 0;  
  31. }  

运行结果如下:

澳门新葡亰平台9411 3

 

2)共享内存映射

所需头文件:

#include <sys/types.h>

#include <sys/shm.h>

 

 

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

功能:

将一个共享内存段映射到调用进程的数据段中。简单来理解,让进程和共享内存建立一种联系,让进程某个指针指向此共享内存。

 

参数:

shmid:共享内存标识符,shmget() 的返回值。

shmaddr:共享内存映射地址(若为 NULL 则由系统自动指定),推荐使用 NULL

shmflg:共享内存段的访问权限和映射条件( 通常为 0 ),具体取值如下:

0:共享内存具有可读可写权限。

SHM_RDONLY:只读。
SHM_RND:(shmaddr 非空时才有效)

 

 

返回值:

成功:共享内存段映射地址( 相当于这个指针就指向此共享内存 )

失败:-1

 

 

3)解除共享内存映射

所需头文件:

#include <sys/types.h>

#include <sys/shm.h>

 

 

int shmdt(const void *shmaddr);

功能:

将共享内存和当前进程分离( 仅仅是断开联系并不删除共享内存,相当于让之前的指向此共享内存的指针,不再指向)。

 

参数:

shmaddr:共享内存映射地址。

 

返回值:

成功:0

失败:-1

 

 

4)共享内存控制

所需的头文件:

#include <sys/ipc.h>

#include <sys/shm.h>

 

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

功能:

共享内存属性的控制。

 

参数:

*shmid:共享内存标识符。*

cmd:函数功能的控制,其取值如下:

IPC_RMID:删除。(常用 )

IPC_SET:设置 shmid_ds 参数,相当于把共享内存原来的属性值替换为 buf 里的属性值。
IPC_STAT:保存 shmid_ds 参数,把共享内存原来的属性值备份到 buf 里。
SHM_LOCK:锁定共享内存段( 超级用户 )。
SHM_UNLOCK:解锁共享内存段。

 

SHM_LOCK 用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。这样做的优势在于让共享内存一直处于内存中,从而提高程序性能。

 *buf:shmid_ds 数据类型的地址(具体类型请点此链接 ),用来存放或修改共享内存的属性。*

 

返回值:

成功:0

失败:-1

如下图所示:

int main(int argc,char **argv)
{
    int shm_id,i,fd;
    key_t key;
    char temp;
    people *p_map;
    char *name="/home/program/shm/shm.txt";
    if((fd=open("/home/program/shm/shm.txt",O_RDWR|O_CREAT))<0)//保证文件的存在,否则ftok()会出错
    {
        perror("open");
        exit(1);
    }
    key=ftok(name,0);
    if(key==-1)
    {
        perror("ftok error");
        exit(1);
    }
    shm_id=shmget(key,SIZE,IPC_CREAT);
    if(shm_id==-1)
    {
        perror("shmget");
        exit(1);
    }
    p_map=(people *)shmat(shm_id,NULL,0);
    temp='a';
    for(i=0;i<10;i++)
    {
        memcpy((*(p_map+i)).name,&temp,1);
        (*(p_map+i)).age=20+i;
        temp+=1;
    }
    if(shmdt(p_map)==-1)
        perror("detach error");
    return 0;
}
/*程序 read_shm.c*/
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#define SIZE 4096
typedef struct
{
    char name[4];
    int age;
}people;

系统V共享内存API

  头文件:
    #include <sys/ipc.h>
    #include <sys/shm.h>

  shmget()用来获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。shmat()把共享内存区域映射到调用进程的地址空间中去,这样,进程就可以方便地对共享区域进行访问操作。shmdt()调用用来解除进程对共享内存区域的映射。shmctl实现对共享内存区域的控制操作。

实战示例

接下来我们做这么一个例子:创建两个进程,在 A 进程中创建一个共享内存,并向其写入数据,通过 B 进程从共享内存中读取数据。

 

写端代码如下:

 

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <unistd.h>  
  5. #include <sys/types.h>  
  6. #include <sys/ipc.h>  
  7. #include <sys/shm.h>  
  8.  
  9. #define BUFSZ 512  
  10.   
  11. int main(int argc, char *argv[])  
  12. {  
  13.     int shmid;  
  14.     int ret;  
  15.     key_t key;  
  16.     char *shmadd;  
  17.       
  18.     //创建key值  
  19.     key = ftok("../", 2015);   
  20.     if(key == -1)  
  21.     {  
  22.         perror("ftok");  
  23.     }  
  24.       
  25.     //创建共享内存  
  26.     shmid = shmget(key, BUFSZ, IPC_CREAT|0666);   
  27.     if(shmid < 0)   
  28.     {   
  29.         perror("shmget");   
  30.         exit(-1);   
  31.     }  
  32.       
  33.     //映射  
  34.     shmadd = shmat(shmid, NULL, 0);  
  35.     if(shmadd < 0)  
  36.     {  
  37.         perror("shmat");  
  38.         _exit(-1);  
  39.     }  
  40.       
  41.     //拷贝数据至共享内存区  
  42.     printf("copy data to shared-memoryn");  
  43.     bzero(shmadd, BUFSZ); // 共享内存清空  
  44.     strcpy(shmadd, "how are you, lhn");  
  45.       
  46.     return 0;  
  47. }  

读代码:

 

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <unistd.h>  
  5. #include <sys/types.h>  
  6. #include <sys/ipc.h>  
  7. #include <sys/shm.h>  
  8.  
  9. #define BUFSZ 512  
  10.   
  11. int main(int argc, char *argv[])  
  12. {  
  13.     int shmid;  
  14.     int ret;  
  15.     key_t key;  
  16.     char *shmadd;  
  17.       
  18.     //创建key值  
  19.     key = ftok("../", 2015);   
  20.     if(key == -1)  
  21.     {  
  22.         perror("ftok");  
  23.     }  
  24.       
  25.     system("ipcs -m"); //查看共享内存  
  26.       
  27.     //打开共享内存  
  28.     shmid = shmget(key, BUFSZ, IPC_CREAT|0666);  
  29.     if(shmid < 0)   
  30. 澳门新葡亰平台9411,    {   
  31.         perror("shmget");   
  32.         exit(-1);   
  33.     }   
  34.       
  35.     //映射  
  36.     shmadd = shmat(shmid, NULL, 0);  
  37.     if(shmadd < 0)  
  38.     {  
  39.         perror("shmat");  
  40.         exit(-1);  
  41.     }  
  42.       
  43.     //读共享内存区数据  
  44.     printf("data = [%s]n", shmadd);  
  45.       
  46.     //分离共享内存和当前进程  
  47.     ret = shmdt(shmadd);  
  48.     if(ret < 0)  
  49.     {  
  50.         perror("shmdt");  
  51.         exit(1);  
  52.     }  
  53.     else  
  54.     {  
  55.         printf("deleted shared-memoryn");  
  56.     }  
  57.       
  58.     //删除共享内存  
  59.     shmctl(shmid, IPC_RMID, NULL);  
  60.       
  61.     system("ipcs -m"); //查看共享内存  
  62.       
  63.     return 0;  
  64. }  

运行结果:

 

澳门新葡亰平台9411 4

 

澳门新葡亰平台9411 5

int main(int argc,char **argv)
{
    int shm_id,i;
    key_t key;
    char temp;
    people *p_map;
    char *name="/home/program/shm/shm.txt";
    key=ftok(name,0);
    if(key==-1)
        perror("ftok error");
    shm_id=shmget(key,SIZE,IPC_CREAT);
    p_map=(people *)shmat(shm_id,NULL,0);
    for(i=0;i<10;i++)
    {
        printf("name:%s n",(*(p_map+i)).name);
        printf("age %d n",(*(p_map+i)).age);
    }
    if(shmdt(p_map)==-1)
        perror("detach error");
    return 0;
}

系统V共享内存限制

  在/proc/sys/kernel/目录下,记录着系统V共享内存的一下限制,如一个共享内存区的最大字节数shmmax,系统范围内最大共享内存区标识符数shmmni等,可以手工对其调整,但不推荐这样做。

进程间通信共七种方式:

系统V共享内存范例

澳门新葡亰平台9411 6/***** testwrite.c *******/ #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <unistd.h> typedef struct{ char name[4]; int age; } people; main(int argc, char** argv) { int shm_id,i; key_t key; char temp; people *p_map; char* name = "/dev/shm/myshm2"; key = ftok(name,0); if(key==-1) perror("ftok error"); shm_id=shmget(key,4096,IPC_CREAT); if(shm_id==-1) { perror("shmget error"); return; } p_map=(people*)shmat(shm_id,NULL,0); temp='a'; for(i = 0;i<10;i++) { temp+=1; memcpy((*(p_map+i)).name,&temp,1); (*(p_map+i)).age=20+i; } if(shmdt(p_map)==-1) perror(" detach error "); } /********** testread.c ************/ #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <unistd.h> typedef struct{ char name[4]; int age; } people; main(int argc, char** argv) { int shm_id,i; key_t key; people *p_map; char* name = "/dev/shm/myshm2"; key = ftok(name,0); if(key == -1) perror("ftok error"); shm_id = shmget(key,4096,IPC_CREAT); if(shm_id == -1) { perror("shmget error"); return; } p_map = (people*)shmat(shm_id,NULL,0); for(i = 0;i<10;i++) { printf( "name:%sn",(*(p_map+i)).name ); printf( "age %dn",(*(p_map+i)).age ); } if(shmdt(p_map) == -1) perror(" detach error "); } View Code

  

共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。是针对其他通信机制运行...

第一类:传统的unix通信机制:

# 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
# 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

# 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

 

第二类:System V IPC: 
# 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
# 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
# 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。

第三类:BSD 套接字:

# 套接字( socket ) : 套解字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。

澳门新葡亰平台9411 7

 

本文介绍IPC之共享内存:

共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。进程可以直接读取共享内存,不需要拷贝数据。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。

步骤:

本文由澳门新葡亰平台9411发布于关于我们,转载请注明出处:当一个进程改变了这块地址中的内容的时候,由

关键词:

最火资讯