</pre><span style="font-size:18px"></span><p></p><p> <span style="white-space:pre"></span>共享內存是linux下提供的最基本的進程間通信方法,它通過mmap或者shmget系統調用在內存中創建了一塊連續的線性地址空間,而通過munmap和shmdt系統調用可以釋放這塊內存.使用共享內存的好處是當多個進程使用同一塊共享內存時,在任何一個進程修改了共享內存中的內容後,其他進程通過訪問這段共享內存都能夠得到修改後的內容.爲了支持跨平臺,nginx提供了三種共享內存的實現,分別爲不映射文件使用mmap分配共享內存,以/dev/zero文件使用mmap映射共享內存,用shmget調用來分配共享內存 </p><p>頭文件源碼解析:</p><pre name="code" class="cpp">
typedef struct {
u_char *addr; //指向共享內存的起始地址
size_t size; //共享內存的長度
ngx_str_t name; //這塊共享內存的名稱
ngx_log_t *log; //記錄日誌的ngx_log_t對象
ngx_uint_t exists; /* unsigned exists:1; *///表示共享內存是否已經分配過的標誌爲,爲1時表示已經存在
} ngx_shm_t;
ngx_int_t ngx_shm_alloc(ngx_shm_t *shm); //分配共享內存
void ngx_shm_free(ngx_shm_t *shm); //釋放已經存在的共享內存</span>
實現文件源碼解析:
#include <ngx_config.h>
#include <ngx_core.h>
//mmap函數詳細知識可看apue中文第三版p423
#if (NGX_HAVE_MAP_ANON) //不映射文件用mmap分配共享內存
//若mmap函數參數中的flags參數加入MAP_ANON,表示不使用文件映射方式,fd參數和offset參數沒有意義,此時mmap方法和ngx_shm_alloc的功能幾乎一樣
ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
//開闢一塊shm->size大小的內存,首地址存放在shm-addr中,同時可讀寫
shm->addr = (u_char *) mmap(NULL, shm->size,
PROT_READ|PROT_WRITE,
MAP_ANON|MAP_SHARED, -1, 0);
if (shm->addr == MAP_FAILED) { //申請失敗
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);
return NGX_ERROR;
}
return NGX_OK;
}
void
ngx_shm_free(ngx_shm_t *shm) //釋放共享內存
{
//使用ngx_shm_t中的shm->addr和shm->size參數釋放共享內存
if (munmap((void *) shm->addr, shm->size) == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"munmap(%p, %uz) failed", shm->addr, shm->size);
}
}
#elif (NGX_HAVE_MAP_DEVZERO) //以/dev/zero文件使用mmap映射共享內存
ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
ngx_fd_t fd;
fd = open("/dev/zero", O_RDWR);//以讀寫方式打開文件
if (fd == -1) { //打開失敗
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"open(\"/dev/zero\") failed");
return NGX_ERROR;
}
//申請內存,內存地址爲shm->addr,大小爲shm->size,可讀寫
shm->addr = (u_char *) mmap(NULL, shm->size, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
//申請出錯
if (shm->addr == MAP_FAILED) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"mmap(/dev/zero, MAP_SHARED, %uz) failed", shm->size);
}
//關閉文件失敗
if (close(fd) == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"close(\"/dev/zero\") failed");
}
//返回是否成功標誌
return (shm->addr == MAP_FAILED) ? NGX_ERROR : NGX_OK;
}
void
ngx_shm_free(ngx_shm_t *shm) //釋放內存
{
if (munmap((void *) shm->addr, shm->size) == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"munmap(%p, %uz) failed", shm->addr, shm->size);
}
}
#elif (NGX_HAVE_SYSVSHM) //用shmget調用來分配共享內存
#include <sys/ipc.h>
#include <sys/shm.h>
ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
int id;
//創建一個新的共享存儲段,返回共享存儲id
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將此段共享內存鏈接到它的地址空間,成功返回共享存儲段的指針
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;
}
void
ngx_shm_free(ngx_shm_t *shm) //釋放內存
{
//這並不從系統中刪除其標識符及其相關數據結構,直到某個進程調用shmctl刪除它
if (shmdt(shm->addr) == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"shmdt(%p) failed", shm->addr);
}
}