nginx源码分析1———进程间的通信机制四(System V内存共享)

相关介绍

SystemV共享内存区在概念上类似于前面提到的mmap,我们将先使用shmget,然后再调用shmat,其中shmget类似于openshmat类似于mmap

相关系统调用

shmget函数创建一个新的共享内存区,或者访问一个已存在的内存共享区。

#include <sys/shm.h>
int shmget(key_t key, size_t size, int oflag);

1,返回值是一个被称为共享内存区标识符的整数,在其他的shmXXX函数中,可以用它来指代这个内存区(可以理解为一个轻量级的文件描述符)。
2,key既可以是ftok的返回值,也可以是IPC_PRIVATE,关于ftok(返回IPC的键),内容比较多,有兴趣可以自己查看。
3,size看字就知道是内存共享区的大小。创建一个新的,必须指定不为0的size,如果访问一个新的,那么size将为0。
4,oflag是如下所示的读写权限值得组合。它还可以与IPC_CREATE或IPC_CREATE | IPC_EXCL按位或。

数字值符号值说明
0400SHM_R由用户(属主)读
0200SHM_W由用户(属主)写
0040SHM_R >> 3由(属)组成员读
0020SHM_W >> 3由(属)组成员写
0004SHM_R >> 6由其他用户读
0002SHM_W >> 6由其他用户写

5,指定key为IPC_PRIVATE能保证创建一个唯一的IPC对象,相关IPC创建或者打开逻辑

oflag参数值key不存在key已经存在
无特殊标志出错,errno = ENOENT成功,引用已存在对象
IPC_CREAT成功,创建新对象成功,引用已存在对象
IPC_CREAT或IPC_EXCL成功,创建新对象出错,errno = EEXIST

由shmget创建或者打开一个共享内存区后,通过调用shmat把它附接到调用进程的地址空间。

#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int flag);

1,shmid是由shmget返回的标识符。shmat的返回值是所指定的共享内存区在调用进程内的起始地址。
2,shmaddr如果是一个空指针,那么系统替调用者选择地址。如果不是,那么返回地址取决于调用者是否给flag参数指定了SHM_RND的值
3,flag如果指定为SHM_RND,那么相应的共享内存区附接到由shmaddr参数指定的地址向下舍入一个SHMLBA的常值。
4,flag被指定为SHM_RDONLY的时候,它只能只读访问。

此函数通过ctl后缀,就可以猜出它的作用,它提供对共享内存区的多种操作。

#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buff)

1,IPC_RMID 从系统中删除由shmid标识的共享内存区并拆除它。
2,IPC_SET 给指定的共享内存区设置其shmid_ds结构的以下三个成员:shm_perm.uid , shm_perm.gid , shm_perm.mode, 它们的值来自于buff参数指向的结构中的相应成员。
3,IPC_STAT 向调用者返回指定共享内存区当前的shmid_ds结构。(ps:必须也是通过最后一个参数buff)

当一个进程完成某一个共享内存区的使用时,可以调用shmdt断接这个内存区。

#include <sys/shm.h>
int shmdt(const void * shmaddr)

相关代码

ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
    int  id;
    //没有采用ftok返回,直接采用IPC_PRIVATE返回唯一的IPC对象
    id = shmget(IPC_PRIVATE, shm->size, 
    //可读可写可创建可引用,不过已经指定IPC_PRIVATE,相当于创建一个新的,拥有可读可写权限
    (SHM_R|SHM_W|IPC_CREAT));
    //shmget出错将返回-1
    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将其附加到应用进程地址,由于shmaddr指定为NULL,那么将由系统为调用者选择地址。
    shm->addr = shmat(id, NULL, 0);
    if (shm->addr == (void *) -1) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, "shmat() failed");
    }
    //拆除内存共享区
    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;

相信各位看官都会有疑问,为何刚刚申请好的共享内存,却要“拆除”。
1,首先,我得普及一下知识,System v的内存共享区的生命周期是随内核的,也就是说共享内存不会随着程序结束而自动消除,要么我们调用shmctl删除,要么自己用手敲命令去删除,否则永远留在系统中。
2,其次,IPC_RMID 命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,拆除操作要等到指定共享内存区的引用计数变为0才会进行,所以再拆除以后,我们还可以使用。
3,最后当某个shmdt调用时,发现所制定的共享内存区的引用计数变为0,也顺便拆除它,这就是shmctl的IPC_RMID命令先于最后一个shmdt调用时会发生情形。

最后附上free代码压压惊,free代码比较简单,调用shmdt就ok了。

void
ngx_shm_free(ngx_shm_t *shm)
{
    if (shmdt(shm->addr) == -1) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,"shmdt(%p) failed", shm->addr);
    }
}
#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值