Linux處理序間通訊(IPC)編程實踐(八)共用記憶體的使用-POSIX 共用記憶體(API)
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);
1. 建立/擷取一個共用記憶體
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. 共用記憶體的映射/卸載
#include void *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