1、Posix提供了兩種在無親緣關系進程間共享內存區的方法:
(1)內存映射文件:先有open函數打開,然後調用mmap函數把得到的描述符映射到當前進程地址空間中的一個文件(上一篇博文所用到的就是)。
(2)共享內存區對象:先有shm_open打開一個Posix IPC名字(也可以是文件系統中的一個路徑名),然後調用mmap將返回的描述符映射到當前進程的地址空間。這兩種方法都需要調用mmap,差別在於作為mmap的參數之一的描述符的獲取手段。
2、Posix共享內存區對象
Posix共享內存區涉及以下兩個步驟要求:
(1)指定一個名字參數調用shm_open,以創建一個新的共享內存區對象或打開一個已經存在的共享內存區對象。
(2)調用mmap把這個共享內存區映射到調用進程的地址空間。
注意:mmap用於把一個內存區對象映射到調用進程地址空間的是該對象的一個已經打開描述符。
關於Posix共享內存區對象的API如下:
#include#include /* For mode constants */ #include /* For O_* constants */ int shm_open(const char *name, int oflag, mode_t mode); int shm_unlink(const char *name); int ftruncate(int fd, off_t length); int fstat(int fd, struct stat *buf);
int shm_open(const char *name, int oflag, mode_t mode);
參數:
name: 共享內存名字;
oflag: 與open函數類型, 可以是O_RDONLY, O_WRONLY, O_RDWR, 還可以按位或上O_CREAT, O_EXCL, O_TRUNC.
mode: 此參數總是需要設置, 如果oflag沒有指定O_CREAT, 則mode可以設置為0;
返回值:
成功: 返回一個文件描述符;
失敗: 返回-1;
注意-Posix IPC名字限制:
1. 必須以”/”開頭, 並且後面不能還有”/”, 形如:/file-name;
2. 名字長度不能超過NAME_MAX
3. 鏈接時:Link with -lrt.
/** 示例: 共享內存的打開與關閉 **/ int main(int argc,char *argv[]) { int shmid = shm_open("/xyz", O_RDWR|O_CREAT, 0666); if (shmid == -1) err_exit("shmget error"); cout << "share memory open success" << endl; close(shmid); }2. 修改共享內存大小
int ftruncate(int fd, off_t length);該函數不僅可用於修改共享內存大小, 而且可以用於修改文件大小
/** 示例: 修改共享內存大小 將其修改為一個Student結構體的大小 **/ struct Student { char name[32]; int age; }; int main(int argc,char *argv[]) { int shmid = shm_open("/xyz", O_RDWR|O_CREAT, 0666); if (shmid == -1) err_exit("shmget error"); if (ftruncate(shmid, sizeof(Student)) == -1) err_exit("ftruncate error"); cout << "share memory change size success" << endl; close(shmid); }3. 獲取共享內存狀態
int fstat(int fd, struct stat *buf);該函數不僅可用於獲取共享內存狀態, 而且可以用於獲取文件狀態, 與前面曾經講述過的stat, lstat類型;
//stat結構體 struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for filesystem I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ };
/** 示例: 獲取共享內存的mode和size **/ int main(int argc,char *argv[]) { int shmid = shm_open("/xyz", O_RDWR|O_CREAT, 0666); if (shmid == -1) err_exit("shmget error"); if (ftruncate(shmid, sizeof(Student)) == -1) err_exit("ftruncate error"); struct stat buf; if (fstat(shmid, &buf) == -1) err_exit("lstat error"); // 注意: 獲取權限時, 需要&上0777, 而且要以%o, 八進制方式打印 printf("mode: %o\n", buf.st_mode&0777); printf("size: %ld\n", buf.st_size); close(shmid); }4. 刪除一個共享內存對象
int main(int argc,char *argv[]) { shm_unlink("/xyz"); }5. 共享內存的映射/卸載
#includevoid *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length);
參數:
addr: 要映射的起始地址, 通常指定為NULL, 讓內核自動選擇;
length: 映射到進程地址空間的字節數, 通常是先前已經創建的共享內存的大小;
prot: 映射區保護方式(見下);
flags: 標志(通常指定為MAP_SHARED, 用於進程間通信);
fd: 文件描述符(填為shm_open返回的共享內存ID);
offset: 從文件頭開始的偏移量(一般填為0);
prot
說明
PROT_READ
頁面可讀
PROT_WRITE
頁面可寫
PROC_EXEC
頁面可執行
PROC_NONE
頁面不可訪問
flags
說明
MAP_SHARED
變動是共享的
MAP_PRIVATE
變動是私有的
MAP_FIXED
准確解釋addr參數, 如果不指定該參數, 則會以4K大小的內存進行對齊
MAP_ANONYMOUS
建立匿名映射區, 不涉及文件
mmap返回值:
成功: 返回映射到的內存區的起始地址;
失敗: 返回MAP_FAILED;
注意:mmap失敗返回EACCES錯誤的原因:
EACCES A file descriptor refers to a non-regular file.
Or MAP_PRIVATE was requested, but fd is not open for reading.
Or MAP_SHARED was requested and PROT_WRITE is set, but fd is not open
in read/write (O_RDWR) mode. Or PROT_WRITE is set, but the file is append-only.
/** 示例: 向共享內存寫入數據 **/ int main(int argc,char *argv[]) { int shmid = shm_open("/xyz", O_RDWR, 0); if (shmid == -1) err_exit("shm_open error"); struct stat buf; if (fstat(shmid, &buf) == -1) err_exit("fstat error"); Student *p = (Student *)mmap(NULL, buf.st_size, PROT_WRITE, MAP_SHARED, shmid, 0); if (p == MAP_FAILED) err_exit("mmap error"); strcpy(p->name, "Hadoop"); p->age = 5; munmap(p, buf.st_size); close(shmid); }
/** 從共享內存讀出數據 **/ int main(int argc,char *argv[]) { int shmid = shm_open("/xyz", O_RDONLY, 0); if (shmid == -1) err_exit("shm_open error"); struct stat buf; if (fstat(shmid, &buf) == -1) err_exit("fstat error"); Student *p = (Student *)mmap(NULL, buf.st_size, PROT_READ, MAP_SHARED, shmid, 0); if (p == MAP_FAILED) err_exit("mmap error"); cout << "name: " << p->name << ", age: " << p->age << endl; munmap(p, buf.st_size); close(shmid); }
[注]
-Posix共享內存默認創建在/dev/shm目錄下
-可以使用od命令查看共享內存的內容
od -c /dev/shm/xyz