1.概述緩存
共享內存區是IPC中最快的,當內存區映射到共享它的進程的地址空間,進程間數據的傳遞就再也不涉及內核。異步
可是這須要某種形式的同步,最經常使用的是信號量。函數
再也不涉及內核:進程再也不經過執行任何進入內核的系統調用來彼此傳遞數據。內核必須創建容許各個進程共享該內存區的內存映射關係,而後一值管理該內存區。oop
管道,FIFO和消息隊列的問題是,兩個進程要交換信息時,這些信息必須經由內核傳遞。post
共享內存區能夠繞過這個問題,可是通常必須同步數據。ui
使用內存映射文件的特性,全部的I/O都再也不有內核直接參與IO,咱們毫不調用read,write,lseek,能夠簡化咱們的代碼。spa
不是全部的文件都能進行內存映射,例如:訪問終端或套接字描述符就不能夠,這些類型的描述符必須使用read和write來訪問。線程
2.相關函數code
mmap把文件或Posix共享內存區對象映射到調用進程的地址空間。對象
三個目的:
(1) 使用普通文件,內存映射IO, open,mmap()方式,也適用於無親緣關係的進程間。
(2) 使用特殊文件,匿名內存映射,mmap(O_ANON)或open(/dev/zero), mmap()方式,只適用於有親緣關係的進程間。
(3) 使用shm_open提供無親緣關係的進程間的Posix共享內存區,shm_open, mmap方式,適用於無親緣關係的進程間。
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
成功:被映射區的起始地址; 出錯:MAP_FAILED
addr: 指定fd描述符設置到進程內空間的起始地址,一般NULL,由內核選擇起始地址。
len: 映射到調用進程地址空間的字節數。從offset計數。
offset:一般0。文件映射內存的偏移量。
prot: 內存映射區的保護,一般PROT_READ | PROT_WRITE
flags: 能夠指定MAP_PRIVATE:調用進程對被映射數據的修改只對該進程可見。
MAP_SHARED: 對共享該數據的全部進程可見。
mmap成功後,fd能夠關閉,映射已經創建。
從進程地址空間刪除一個映射關係
#include <sys/mman.h>
int munmap(void *addr, size_t len);
使硬盤上的文件與內存映射區的內容一致
int msync(void *addr, size_t len, int flags);
成功:0; 失敗:-1
flags: MS_ASYNC:異步寫
MS_SYNC: 同步寫
MS_INVALIDATE: 使高速緩存失效
3.匿名映射
若調用mmap目的是提供一個將穿越fork由父子進程共享的映射內存區,即在只是在有親緣關係的內存間共享內存,可使用匿名映射。
(1) 4.4BSD提供了匿名內存內存映射,避免了文件的建立和打開。
經過flags指定MAP_SHARED | MAP_ANON, fd = -1, offset = 0。這樣的內存區初始化爲0。
int *ip = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, –1, 0);
(2) SVR4 提供/dev/zero設備文件,open它以後可在mmap中使用獲得的描述符。
從設備讀時返回的字節全是0,寫往該設備的任何字節則被丟棄。許多BSD的也支持這個。
int fd = open(「/dev/zero」, O_RDWR);
int *ip = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
示例:
父子進程給共享內存區的數字加1
同步使用了:有名信號量,無名信號量,互斥鎖
共享內存使用了:內存映射文件,匿名共享內存區
總結:對於這種親緣關係的進程間通訊,最好使用:匿名共享共享內存,無名信號量。
#include "unp.h"
struct shared { // sem_t mutex; //信號量互斥
pthread_mutex_t mutex; //互斥鎖互斥
int count; } shared; int main(int argc, char **argv) { if (argc != 3) { err_quit("Usage: a.out <pathname> <#loop>"); } int nloop = atoi(argv[2]); // int fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE); // //改變文件大小的可移植的方式 // Write(fd, &shared, sizeof(shared)); // struct shared *ptr = (struct shared*)Mmap(NULL, sizeof(shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // Close(fd); //使用/dev/zero內存映射 //int fd = Open("/dev/zero", O_RDWR, FILE_MODE); //struct shared *ptr = (struct shared*)Mmap(NULL, sizeof(shared), // PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //使用匿名映射 //fd = -1 //添加MAP_ANON //只適用於親緣進程間
struct shared *ptr = (struct shared*)Mmap(NULL, sizeof(shared), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); //使用有名信號量實現互斥 //sem_unlink("mysem"); //sem_t *mutex = Sem_open("mysem", O_CREAT | O_EXCL, FILE_MODE, 1); //Sem_unlink("mysem"); //使用無名信號量(基於內存)實現互斥 //第二個1:進程間共享。0:線程間共享 //第三個1:初值 //Sem_init(&ptr->mutex, 1, 1); //使用互斥量實現互斥 //互斥量用於進程間互斥, //須要設置共享
pthread_mutexattr_t mattr; Pthread_mutexattr_init(&mattr); Pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED); Pthread_mutex_init(&ptr->mutex, &mattr); //設置標準輸出無緩衝
setbuf(stdout, NULL); if (Fork() == 0) { for (int i = 0; i < nloop; ++i) { //有名信號量 //Sem_wait(mutex); //無名信號量 //Sem_wait(&ptr->mutex); //互斥量
Pthread_mutex_lock(&ptr->mutex); printf("child: %d\n", (ptr->count)++); Pthread_mutex_unlock(&ptr->mutex); //Sem_post(&ptr->mutex); //Sem_post(mutex);
} exit(0); } for (int i = 0; i < nloop; ++i) { //Sem_wait(mutex); //Sem_wait(&ptr->mutex);
Pthread_mutex_lock(&ptr->mutex); printf("parent: %d\n", (ptr->count)++); Pthread_mutex_unlock(&ptr->mutex); //Sem_post(&ptr->mutex); //Sem_post(mutex);
} exit(0); }
4. Posix共享內存區
open——mmap:posix內存映射文件;
shm_open——mmap: posix共享內存區對象。
這二者叫作:Posix內存區對象。
4.1. 打開,刪除
posix共享內存區需兩個步驟:
(1) 指定名字參數調用shm_open,以建立一個新的共享內存區對象或打開一個已存在的共享內存區對象。
(2) 調用mmap把這個共享內存區映射到進程的地址空間。
#include <sys/mman.h>
int shm_open(const char *name, int oflag, mode_t mode);
成功:非負描述符; 出錯:-1;
刪除一個共享內存區對象的名字。不會影響對於其底層支撐對象的現有應用,直到對於該對象的應用所有關閉爲止。
int shm_unlink(const char *name);
改變普通文件或共享內存區對象的大小
#include <unistd.h>
int ftruncate(int fd, off_t length);
已存在的共享內存區對象,或取對象大小
#include <sys/type.h>
#include <sys/stat.h>
int fstat(int fd, struct stat *buf);
struct stat {
mode_t st_mode;
uid_t st_uid;
gid_t st_gid;
off_t st_size; //size in bytes
};
示例:
兩個獨立的進程給共享內存區數據加1。
創建共享內存區使用:Posix內存映射文件,Posix共享內存區對象
同步使用:無名信號量,固然其餘也可已。
建立共享內存區程序:
#include "unp.h"
struct shmstruct { int count; sem_t mutex; } shm; int main(int argc, char **argv) { //Posix 共享內存區對象 // shm_unlink("myshm"); // int fd = Shm_open("myshm", O_RDWR | O_CREAT | O_EXCL, FILE_MODE); // Ftruncate(fd, sizeof(struct shmstruct)); // struct shmstruct *ptr = (struct shmstruct*)Mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE, // MAP_SHARED, fd, 0); // Close(fd); //Posix內存映射文件
int fd = Open("myopenshm", O_RDWR | O_CREAT, FILE_MODE); Write(fd, &shm, sizeof(shm)); struct shmstruct *ptr = (struct shmstruct*)Mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); Close(fd); Sem_init(&ptr->mutex, 1, 1); exit(0); }
給共享內存區加1程序:
#include "unp.h"
struct shmstruct { int count; sem_t mutex; }; int main(int argc, char **argv) { if (argc != 2) { err_quit("Usage: a.out <#loops>"); } int nloops = atoi(argv[1]); //int fd = Shm_open("myshm", O_RDWR, FILE_MODE); //struct shmstruct *ptr = (struct shmstruct*)Mmap(NULL, sizeof(struct shmstruct), // PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //Close(fd);
int fd = Open("myopenshm", O_RDWR, FILE_MODE); struct shmstruct *ptr = (struct shmstruct*)Mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); Close(fd); pid_t pid = getpid(); for (int i = 0; i < nloops; ++i) { Sem_wait(&ptr->mutex); printf("pid = %ld: %d\n", (long)pid, ptr->count++); Sem_post(&ptr->mutex); } exit(0); }