跟消息隊列一樣,共享內存也有自己的數據結構,如下:
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
同樣地,第一個成員是共有的IPC內核數據結構,其余是私有成員。
以下是幾個共享內存函數 :
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:用來創建共享內存
原型 int shmget(key_t key, size_t size, int shmflg);
參數
key:這個共享內存段名字
size:共享內存大小
shmflg:由 九個權限標志構成,它們的用法和創建文件時使用的mode模式標志是一樣的
返回值:成功返回一個非負整數,即該共享 內存段的標識碼;失敗返回-1
功能:將共享內存段連接到進程地址空間
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
參數
shmid: 共享內存標識
shmaddr:指定連接的地址
shmflg:它的兩個可能取值 是SHM_RND和SHM_RDONLY
返回值:成功返回一個指針,指向共享內存第一個字節;失敗返回-1
shmaddr為NULL,核 心自動選擇一個地址
shmaddr不為NULL且shmflg無SHM_RND標記,則以shmaddr為連接地址。
shmaddr不為NULL且shmflg 設置了SHM_RND標記,則連接的地址會自動向下調整為SHMLBA的整數倍。
公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示連接操作用來只讀共享內存
功能:將共享內存段與當前進程脫離
原型 int shmdt (const void *shmaddr);
參數
shmaddr: 由shmat所返回的指針
返回值:成功返回0;失敗返回-1
注意:將共享 內存段與當前進程脫離不等於刪除共享內存段
功能:用於控制共享內存
原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數
shmid:由shmget返回的共享內存標識碼
cmd:將要采取的動作(有三個可取值)
buf:指向一個保存著共享內存的模式狀態和訪問權限的數據結構
返回值:成功返回0;失敗返回-1
cmd 的取值 如下,與消息隊列類似:
IPC_STAT 把shmid_ds結構中的數據設置為共享內存的當前關聯值
IPC_SET 在進程有足 夠權限的前提下,把共享內存的當前關聯值設置為shmid_ds數據結構中給出的值
IPC_RMID 刪除共享內存段
下面 寫兩個函數測試一下:
shm_write.c
#include<string.h> #include<stdio.h> #include<stdlib.h> #include<sys/ipc.h> #include<sys/msg.h> #include<sys/types.h> #include<unistd.h> #include<errno.h> #include<fcntl.h> #include<sys/stat.h> #include<sys/mman.h> #include<sys/ipc.h> #include<sys/shm.h> #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) typedef struct stu { char name[32]; int age; } STU; int main(int argc, char *argv[]) { int shmid; shmid = shmget(1234, sizeof(STU), IPC_CREAT | 0666); if (shmid == -1) ERR_EXIT("shmget"); STU *p; p = shmat(shmid, NULL, 0); if (p == (void *) - 1) ERR_EXIT("shmat"); strcpy(p->name, "lisi"); p->age = 20; shmdt(p); return 0; }
在上面程序中,先創建一塊共享內存,再映射到進程的地址空間。
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./shm_write
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs -m
------ Shared Memory Segments -- ------
key shmid owner perms bytes nattch status
0x000004d2 0 simba 666 36 0
可以看到創建了一塊共享內存,字節數為寫入的STU大小,natth 表示進程連接個數,若在上面程序的 shmdt 之前sleep(n); 此時在另一窗口觀察,可發現連接數為1,進程退出時,連接數再次為0。
shm_read.c
#include<string.h> #include<stdio.h> #include<stdlib.h> #include<sys/ipc.h> #include<sys/msg.h> #include<sys/types.h> #include<unistd.h> #include<errno.h> #include<fcntl.h> #include<sys/stat.h> #include<sys/mman.h> #include<sys/ipc.h> #include<sys/shm.h> #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) typedef struct stu { char name[32]; int age; } STU; int main(int argc, char *argv[]) { int shmid; shmid = shmget(1234, 0, 0); if (shmid == -1) ERR_EXIT("shmget"); STU *p; p = shmat(shmid, NULL, 0); if (p == (void *) - 1) ERR_EXIT("shmat"); printf("name = %s age = %d\n", p->name, p->age); shmdt(p); shmctl(shmid, IPC_RMID, NULL); return 0; }
上面程序中,先打開共享內存,若未知共享內存大小,size 可設為0,然後也映射到自身的進程地址空間,讀取數據, 最後使用shmctl 刪除這段共享內存。
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./shm_read
name = lisi age = 20
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs - m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
共享內存段已經被刪除。