Linux下的共享內(nèi)存編程
前面帶小伙伴們學(xué)習(xí)了共享內(nèi)存相關(guān)的內(nèi)容,先簡(jiǎn)單介紹下共享內(nèi)存,然后進(jìn)行結(jié)構(gòu)及函數(shù)的學(xué)習(xí),最后擼代碼使用一下這些函數(shù)使用一下共享內(nèi)存,希望對(duì)大家有所幫助哈!
1 共享內(nèi)存的概念及使用過(guò)程
1)共享內(nèi)存的概念
共享內(nèi)存是IPC機(jī)制中的一種。
共享內(nèi)存:即允許兩個(gè)或多個(gè)進(jìn)程共享一個(gè)給定的存儲(chǔ)區(qū)。
2)共享內(nèi)存的使用過(guò)程
① 進(jìn)程1創(chuàng)建共享內(nèi)存,接著映射共享內(nèi)存。
② 進(jìn)程2獲取共享內(nèi)存,映射共享內(nèi)存。
③ 交互完成,進(jìn)程1分離共享內(nèi)存,進(jìn)程2分離共享內(nèi)存。
④ 進(jìn)程1刪除共享內(nèi)存。
2 共享內(nèi)存相關(guān)的結(jié)構(gòu)及函數(shù)
0)共享內(nèi)存相關(guān)的結(jié)構(gòu)
內(nèi)核為每個(gè)共享存儲(chǔ)段維護(hù)著一個(gè)結(jié)構(gòu),該結(jié)構(gòu)至少要為每個(gè)共享存儲(chǔ)段包含以下成員。
- struct shmid_ds
- {
- struct ipc_perm shm_perm; // 操作權(quán)限
- size_t shm_segsz; // 段的大?。ㄒ宰止?jié)為單位)
- time_t shm_atime; // 上一個(gè)進(jìn)程附加到該段的時(shí)間
- time_t shm_dtime; // 上一個(gè)進(jìn)程分離開該段的時(shí)間
- time_t shm_ctime; // 上一個(gè)進(jìn)程修改該段的時(shí)間
- pid_t shm_cpid; // 創(chuàng)建該段進(jìn)程的PID
- pid_t shm_lpid; // 上個(gè)shmat(2)/shmdt(2)的PID
- shmatt_t shm_nattch; // 當(dāng)前附加到該段的進(jìn)程的個(gè)數(shù)
- ...
- };
系統(tǒng)為每一個(gè)IPC對(duì)象保存一個(gè)ipc_perm結(jié)構(gòu)體,該結(jié)構(gòu)說(shuō)明了IPC對(duì)象的權(quán)限和所有者,每一個(gè)版本的內(nèi)核各有不用的ipc_perm結(jié)構(gòu)成員。
- struct ipc_perm
- {
- key_t __key; // 為 shmget(2) 調(diào)用提供的鍵值
- uid_t uid; // 共享內(nèi)存所有者的有效用戶UID
- gid_t gid; // 共享內(nèi)存所有者所屬組的有效組GID
- uid_t cuid; // 共享內(nèi)存創(chuàng)建者的有效用戶UID
- gid_t cgid; // 共享內(nèi)存創(chuàng)建者所屬組的有效組ID
- unsigned short mode; // 特權(quán) + SHM_DEST 和SHM_LOCKED 標(biāo)志
- unsigned short __seq; // 序列號(hào)
- };
1)shmget函數(shù)
shmget函數(shù)用于創(chuàng)建或者獲取共享內(nèi)存,并返回其描述符id。
① 函數(shù)原型。
- int shmget(key_t key,size_t sizie,int shmflg)
② 頭文件。
- include <sys/ipc.h>
- include <sys/shm.h>
③ 參數(shù)。
key:共享內(nèi)存的鍵值。
size:共享內(nèi)存的大小。
shmflg:打開標(biāo)志,如果使用了IPC_CREAT,則會(huì)新創(chuàng)建一塊共享內(nèi)存。
④ 返回值。
成功:返回創(chuàng)建或者獲取到的共享內(nèi)存的描述符。
失?。?1。
2)shmat函數(shù)
shmat函數(shù)用于映射共享內(nèi)存,即將進(jìn)程連接到它的地址空間。
① 函數(shù)原型。
- void *shmat(int shmid,const void *shmaddr,int shmflg)
② 頭文件。
- include <sys/types.h>
- include <sys/shm.h>
③ 參數(shù)。
shmid:要映射的共享內(nèi)存的描述符。
shmaddr:共享內(nèi)存的地址。
shmflg:打開標(biāo)志,如果使用了IPC_CREAT,則會(huì)新創(chuàng)建一塊共享內(nèi)存。
④ 返回值。
成功:返回創(chuàng)建或者獲取到的共享內(nèi)存的描述符。
失?。?1。
3)shmdt函數(shù)
shmdt函數(shù)用于分離共享內(nèi)存,即操作完存儲(chǔ)段后,用此函數(shù)可以將進(jìn)程與此存儲(chǔ)段脫離開,即斷掉與共享內(nèi)存的聯(lián)系。
① 函數(shù)原型。
- int shmdt(const void *shmaddr)
② 頭文件。
- #include <sys/types.h>
- #include <sys/shm.h>
③ 參數(shù)。
shmaddr:要斷開的共享內(nèi)存的映射地址。
④ 返回值。
成功:0。
失敗:-1。
4)shmctl函數(shù)
shmctl函數(shù)用于控制共享內(nèi)存,通過(guò)參數(shù)可以對(duì)共享內(nèi)存進(jìn)行特定的操作。
① 函數(shù)原型。
- int shmctl(int shmid, int cmd, struct shmid_ds *buf)
② 頭文件。
- #include <sys/ipc.h>
- #include <sys/shm.h>
③ 參數(shù)。
shmid:要控制的共享內(nèi)存的id。
cmd:決定執(zhí)行什么樣的控制操作,如IPC_RMID(表示刪除)。
buf:獲取linux中描述共享內(nèi)存的shmid_ds結(jié)構(gòu)。基本不使用。
cmd可去的參數(shù)如下,需要參照上面的結(jié)構(gòu)shmid_ds和ipc_perm :
IPC_STAT:取此段的shmid_ds結(jié)構(gòu),并將它存儲(chǔ)在由buf指向的結(jié)構(gòu)中。
IPC_SET:按buf指向的結(jié)構(gòu)中的值設(shè)置與此共享存儲(chǔ)段相關(guān)的shmid_ds結(jié)構(gòu)中的下列3個(gè)字段:shmperm.uid、shm perm.gid和shmperm.mode。
此命令只能由下列兩種進(jìn)程執(zhí)行:一種是其有效用戶ID等于shm_perm.cuid或shmperm.uid的進(jìn)程;另一種是具有超級(jí)用戶特權(quán)的進(jìn)程。
IPC_RMID:從系統(tǒng)中刪除該共享存儲(chǔ)段。
除非使用該段的最后一個(gè)進(jìn)程終止或與該段分離,否則不會(huì)實(shí)際上刪除該存儲(chǔ)段。
不管此段是否仍在使用,該段標(biāo)識(shí)符都會(huì)被立即刪除,所以不能再用shmat與該段連接。
此命令只能由下列兩種進(jìn)程執(zhí)行:一種是其有效用戶ID等于shm_perm.cuid或shm_perm.uid的進(jìn)程;另一種是具有超級(jí)用戶特權(quán)的進(jìn)程。
下面兩個(gè)命令只能由超級(jí)用戶執(zhí)行:
SHM_LOCK:在內(nèi)存中對(duì)共享存儲(chǔ)段加鎖。
SHM_UNLOCK:解鎖共享存儲(chǔ)段。
④ 返回值。
成功:根據(jù)不同的操作返回不同的值。
失?。?1。
3 實(shí)例代碼
下面用兩個(gè)進(jìn)程,給大家演示下共享內(nèi)存的使用過(guò)程。
實(shí)例代碼如下,說(shuō)明都在代碼注釋中了。
WriteMemory.c。
- #include <sys/types.h>
- #include <sys/shm.h>
- #include <sys/ipc.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #define SIZE 1024 // 可輸入1K字符串
- struct SharedMemoryST
- {
- int ReadWriteFlag; // 表明是誰(shuí)放進(jìn)去的
- char CharData[SIZE]; // 字符數(shù)組保存用戶輸入數(shù)據(jù)
- };
- int main(int argc,char *argv[])
- {
- int shmid;
- int ReadStatusFlag = 1; // 內(nèi)存中數(shù)據(jù)是否被讀走,1未被讀走
- struct SharedMemoryST *shm; // 共享內(nèi)存結(jié)構(gòu)變量
- char buffer[SIZE];
- key_t key=ftok("/tmp",12); // 創(chuàng)建共享內(nèi)存的鍵值,如果提示創(chuàng)建失敗(一般是沒(méi)有quit引起的),可以修改讀寫進(jìn)程的鍵值,都要改成同一數(shù)字
- //1 創(chuàng)建共享內(nèi)存
- shmid = shmget(key,sizeof(struct SharedMemoryST),IPC_CREAT|IPC_EXCL|0777);
- if(shmid == -1) // 如果創(chuàng)建失敗
- {
- printf("\nCreating share memory fail!\n\n");
- exit(1);
- }
- //2 映射共享內(nèi)存
- shm = shmat(shmid,NULL,0); // 內(nèi)存id,映射的位置,映射的標(biāo)志(此無(wú)特殊要求)
- //3 查詢寫入的
- while(ReadStatusFlag) // 循環(huán)檢查寫入共享內(nèi)存的數(shù)據(jù)是否被讀走,讀走后退出循環(huán)
- {
- while(shm->ReadWriteFlag == 1)
- {
- sleep(1);
- printf("\nWaiting read memory!\n");
- }
- // 獲取用戶輸入
- printf("\nPlease input data or input 'quit' to exit!\n\n");
- fgets(buffer,SIZE,stdin); // 參數(shù):字符串的位置,長(zhǎng)度,獲取的方式位置
- // 將用戶輸入的字符串放入共享內(nèi)存
- strncpy(shm->CharData,buffer,SIZE);// 參數(shù):目的數(shù)據(jù),源數(shù)據(jù),數(shù)據(jù)大小
- shm->ReadWriteFlag = 1;
- if(strncmp(buffer,"quit",4) == 0) // 最后一個(gè)參數(shù)為比較字符的數(shù)量
- {
- ReadStatusFlag = 0; // 寫入共享內(nèi)存的數(shù)據(jù)已經(jīng)被讀走
- }
- }
- //4 脫離共享存
- shmdt((const void *)shm);
- return 0;
- }
ReadMemory.c。
- #include <sys/types.h>
- #include <sys/shm.h>
- #include <sys/ipc.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #define SIZE 1024 // 可輸入1K字符串
- struct SharedMemoryST
- {
- int ReadWriteFlag; // 標(biāo)明是讀進(jìn)程還是寫進(jìn)程放入了數(shù)據(jù)
- char CharData[SIZE]; // 保存用戶輸入數(shù)據(jù)
- };
- int main(int argc,char *argv[])
- {
- int shmid;
- int ReadStatusFlag = 1; // 內(nèi)存中數(shù)據(jù)是否被讀走的標(biāo)志位,1表示未被讀走
- struct SharedMemoryST *shm; // 共享內(nèi)存結(jié)構(gòu)
- key_t key=ftok("/tmp",12); // 創(chuàng)建共享內(nèi)存的鍵值,如果提示創(chuàng)建失敗,修改一下數(shù)字即可,讀寫進(jìn)程都要改成同一數(shù)字
- //1 創(chuàng)建/獲取共享內(nèi)存
- shmid = shmget(key,sizeof(struct SharedMemoryST),IPC_CREAT|0777);//分配大小為結(jié)構(gòu)大小,1234是隨便給的鍵值
- //2 映射共享內(nèi)存
- shm = (struct SharedMemoryST *)shmat(shmid,NULL,0); //內(nèi)存id,映射的位置,映射的標(biāo)志(此無(wú)特殊要求)
- shm->ReadWriteFlag = 0;
- //3 檢查是否收到信息,收到quit退出
- while(ReadStatusFlag)
- {
- //打印共享內(nèi)存
- if(shm->ReadWriteFlag == 1) // 等于說(shuō)明有相應(yīng)的數(shù)據(jù)
- {
- printf("\nThe write context is: %s\n",shm->CharData);
- shm->ReadWriteFlag = 0;
- if(strncmp(shm->CharData,"quit",3) == 0)
- {
- ReadStatusFlag = 0; // 結(jié)束查詢,退出
- }
- }
- }
- //4 脫離共享內(nèi)存
- shmdt((const void *)shm);
- //5 刪除共享內(nèi)存
- shmctl(shmid,IPC_RMID,0);
- }
寫共享內(nèi)存先創(chuàng)建共享內(nèi)存,寫入數(shù)據(jù),讀共享內(nèi)存讀取數(shù)據(jù),通過(guò)標(biāo)志查詢方式,退出輸入quit。
運(yùn)行結(jié)果如下:
本文轉(zhuǎn)載自微信公眾號(hào)「嵌入式雜牌軍」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系嵌入式雜牌軍公眾號(hào)。