您的位置:首页 > 理论基础 > 数据结构算法

IPC 共享内存

2009-09-21 14:05 190 查看
共享内存(Shared Memory)是最简单的进程间通信方式,它允许多个进程访问相同的内存,一个进程改变其中的数据后,其他的进程都可以看到数据的变化。

共享内存是进程间最快速的通信方式:

①进程共享同一块内存空间。

②访问共享内存和访问私有内存一样快。

③不需要系统调用和内核入口。

④不造成不必要的内存复制。

内核不对共享内存的访问进行同步,因此程序员必须自己提供同步。

使用共享内存:

①某个进程分配内存段。

②使用这个内存段的进程要连接(attach)这个内存段。

③每个进程使用完共享内存段后,要分离(detach)这个内存段。

④在某个地方,必须有一个进程来销毁这个内存段。

Linux的内存模型:

①每个进程的虚拟内存被分为页(page)。

②每个进程维护自己的内存地址到虚拟内存页之间的映射。

③实际的数据存在于进程的内存地址上。

④尽管每个进程有自己的地址空间,多个进程的映射还是可以指向相同的页。

所有的共享内存段的大小,都是Linux内存页大小的整数倍。

Linux的页大小是4KB,不过程序员应该使用getpagesize函数来获得这个值。

 

 

分配:shmget

函数原型:shmget(key_t key, int size, int shmflg);

①:shmget用来取得参数key所关联的内存识别代码。如果参数key为IPC_PRIVATE,则会建立新的共享内存,大小是由size来决定的,

实际分配的字节数会舍弃多余部分到页大小的整数倍。

②:如果参数key不是IPC_PRIVATE,也不是已经建立的IPC key, 那么系统会根据shmflg是否有IPC_CREAT位(shmflg | IPC_CREAT)

为真来决定IPC key为参数的共享内存。

③:如果参数包含了IPC_CREAT 和 IPC_EXECL位, 那么如果key标识的共享内存已经存在, 那么会报EEXIST的错误。

④:参数也用来决定共享内存的存取权限, 相当于open()的 mode参数。

 

内存共享attach:shmat

函数原型: void* shmat(int shmid, const void* shmaddr, int shmflg);

①:shmat()用来将参数shmid指定的共享内存和目前进程相连(attach)

②:在经过fork()后, 子进程将继承已连接的共享内存地址,

③:在经过exec()函数后, 已连接的共享内存地址会自动脱离(detach)

④:进程结束后,已连接的共享内存会自动脱离

 

 

脱离:shmdt

函数原型:shmdt(const void *shmaddr);

①:shmdt()用来脱离先前shmat()过的共享内存。

 

 

控制共享内存的操作 shmctl

函数原型:shmctl(int shmid, int cmd, struct shmid_ds *buf);

①:参数cmd 有下面几种操作

    1:IPC_STAT:把共享内存的 shmid_ds的结构copy到 buf 中。

    2:IPC_SET: 把参数buf所指的shmid_ds结构中的 shm_perm.uid, shm_perm.gid, shm_perm.mode 拷贝到共享内存的

shmid_ds结构中。

    3:IPC_RMID:删除共享内存和其数据结构。

 

 

看例子:

 

typedef struct {

    u_char      *addr;

    size_t       size;

    ngx_log_t   *log;

} ngx_shm_t;

 

ngx_int_t

ngx_shm_alloc(ngx_shm_t *shm)

{

    int  id;

/* 用ipc_private 来新建共享内存 */

    id = shmget(IPC_PRIVATE, shm->size, (SHM_R|SHM_W|IPC_CREAT));

    if (id == -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "shmget(%uz) failed", shm->size);

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, shm->log, 0, "shmget id: %d", id);

  /* 每次有进程shmat,那么在这个共享内存中的shmid_ds 结构中的shm_nattach就会加一 */

    shm->addr = shmat(id, NULL, 0);

    if (shm->addr == (void *) -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, "shmat() failed");

    }

 

/* 只有在id所标识的共享内存中的shmid_ds结构中的shm_nattach为0的情况下才能被删除。否者无效*/

    if (shmctl(id, IPC_RMID, NULL) == -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "shmctl(IPC_RMID) failed");

    }

    return (shm->addr == (void *) -1) ? NGX_ERROR : NGX_OK;

}

void

ngx_shm_free(ngx_shm_t *shm)

{

  /* 每次有进程shmdt,那么在这个共享内存中的shmid_ds 结构中的shm_nattach就会减一 */

    if (shmdt(shm->addr) == -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "shmdt(%p) failed", shm->addr);

    }

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息