👦個人主頁:Weraphael
?🏻作者簡介:目前正在學習c++和算法
??專欄:Linux
🐋 希望大家多多支持,咱一起進步!😁
如果文章有啥瑕疵,希望大佬指點一二
如果文章對你有幫助的話
歡迎 評論💬 點贊👍🏻 收藏 📂 加關注😍
目錄
- 一、共享內存的工作原理
- 二、系統調用接口
- 2.1 shmget函數 --- 創建共享內存
- 2.2 補充:key值和shmid的區別
- 2.3 shmctl函數 --- 釋放
- 2.4 shmat函數 --- 進程掛接共享內存
- 2.5 shmdt函數 --- 手動解除進程和共享內存掛接
- 三、使用以上接口讓兩個進程通信
- 四、共享內存的特性
- 五、共享內存的屬性
- 六、解決共享內存沒有同步和互斥保護機制問題
- 七、本篇博客源代碼
一、共享內存的工作原理
Linux
操作系統除了要為進程創建結構體對象task_struct
(表示進程的數據結構,包含了進程的所有屬性,如進程標識符PID
);除此之外,操作系統還會為每個進程創建進程地址空間結構體對象mm_struct
(存儲了進程的地址空間信息,包括堆、棧等)。該進程要如何找到自己的進程地址空間呢?因此task_struct
結構體還會有該進程對應的mm_struct
結構體指針字段,可以通過task_struct
對象找到對應的進程地址空間。
另外,操作系統還需為每個進程創建頁表,Linux
操作系統系統會通過分頁機制來管理虛擬地址和物理地址之間的映射關系,用于將虛擬地址映射到物理地址。當程序訪問虛擬地址時,操作系統會根據頁表將虛擬地址轉換為物理地址。
而進程間通信的本質是:讓不同的進程看到同一份資源。因此,共享內存的原理就是:因為進程具有獨立性,無法自己提供資源給對方,因此操作系統會在物理內存中開辟一塊內存(“共享內存”由操作系統提供),再通過頁表映射到兩個不同進程的虛擬地址空間中的共享區,此時兩個獨立的進程看到同一塊空間,那么就可以進行通信了。(整個過程類似于動態庫的加載)
除此之外,因為共享內存是由程序員向操作系統申請的,當然還需要正確地釋放共享內存資源,以避免內存泄漏和資源占用。釋放共享內存的過程包括兩個主要步驟:
- 解除所有與要釋放的共享內存有關系的進程(去關聯)
- 最后釋放共享內存,通常由最后一個使用該共享內存段的進程來執行(釋放共享內存)
因此,System V共享內存通信的整個過程總結如下:
-
創建共享內存段
-
進程掛接共享內存
-
通信
-
解除共享內存掛接
二、系統調用接口
共享內存的創建、進程間建立映射和釋放都是由操作系統完成的。對應的操作系統也要為用戶提供訪問和管理共享內存的接口,允許用戶在程序中使用共享內存來實現進程間通信。
2.1 shmget函數 — 創建共享內存
shmget
函數是一個用于創建共享內存或者訪問已存在共享內存段,通常用于在進程之間共享數據而無需通過文件系統。它的函數原型如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
參數說明:
-
key
:可以唯一標識一個共享內存段(內核層使用)。這個值是多少并不重要,關鍵在于它必須具有唯一性,能夠讓不同的進程看到同一個共享內存(兩個進程傳入相同的key
值即看到同一塊內存)。就這樣說吧,第一個進程通過key
值創建共享內存,后面的進程要用看到同一個共享內存通信,只需拿同一個key
值即可!注意:這個key
值在struct shmid_ds
對象中。那么接下來只有最后一個問題:如何獲取設置key
值?一般使用ftok
函數生成唯一key
值(常用)。(查看函數用法:點擊跳轉) -
size
:指定要創建的共享內存段的大小,以字節為單位。如果創建新的共享內存段,建議分配4096
的整數倍(如果你創建的大小是4097
,實際上操作系統分配的大小是4096 * 2
)。如果只是獲取一個已存在的共享內存段的標識符,這個參數可以設置為0
來表示忽略。 -
shmflg
:這是一個標志參數,用于指定操作模式和權限。可以用操作符'|'
進行組合使用。它可以是以下幾個標志的組合:IPC_CREAT
:這個選項單獨使用的話,如果申請的共享內存不存在,則創建一個新的共享內存;如果存在,獲取已存在的共享內存。IPC_EXCL
: 一般配合IPC_CREAT
一起使用(不單獨使用)。他主要是檢測共享內存是否存在,如果存在,則出錯返回;如果不存在就創建。確保申請的共享內存一定是新的。- 權限標志:以與文件權限類似的方式指定共享內存段的訪問權限(例如
0666
表示所有用戶可讀寫)。 - 但在獲取已存在的共享內存時,可以設置為
0
-
返回值:
-
成功時返回共享內存段的標識符
shmid
。(操作系統內部分配的,提供給用戶層使用,類似于文件描述符fd
) -
失敗時返回
-1
,并設置errno
以指示錯誤原因。
-
-
返回值常見錯誤
errno
如下:-
EACCES
:對于給定的鍵值沒有足夠的權限。 -
EEXIST
: 設置了IPC_CREAT | IPC_EXCL
,但具有給定鍵值的共享內存段已經存在。 -
EINVAL
: 請求的大小無效,或者給定的鍵值無效。 -
ENOENT
: 沒有設置IPC_CREAT
,而且具有給定鍵值的共享內存段不存在。 -
ENOMEM
: 沒有足夠的內存來滿足請求。
-
ftok
函數是專門用于生成唯一鍵值,通常用于進程間通信的共享內存、信號量和消息隊列。它通過將文件路徑(路徑本身就具有唯一性) 和 項目標識符ID
結合起來生成一個唯一的鍵值。其函數原型如下:
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
-
pathname
: 指向一個現有文件的路徑。這個文件可以是任何文件,但在實際應用中,通常選擇一個固定存在且不會被刪除的文件。 -
proj_id
: 項目標識符,一個用戶指定的整數值。該值一般用于擴展唯一性,如果沖突該次參數就行。 -
返回值:
-
成功時返回生成的鍵值 (
key_t
類型)。可以用于后續的函數調用(如shmget()
等)。 -
失敗時返回
-1
,并設置errno
以指示錯誤原因。
-
常見的 errno
值包括:
EACCES
: 文件不可訪問。ENOENT
: 文件不存在。ENAMETOOLONG
: 路徑名過長。
代碼樣例:
在resource.hpp
中封裝創建共享內存段的接口,來供其他進程使用。
#pragma once#include <iostream>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <string>
#include <cstring>
#include "log.hpp"using namespace std;const string pathname = "/home/wj"; // ftok函數的第一個參數
int proj_id = 'A'; // ftok函數的第二個參數
log l; // 日志對象key_t Getkey()
{key_t key = ftok(pathname.c_str(), proj_id);if (key == -1){l.logmessage(Fatal, "ftok error: %s", strerror(errno));exit(1);}l.logmessage(Info, "ftok success, key is: 0x%x", key);return key;
}int GetShareMem()
{key_t key = Getkey();int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL);if (shmid == -1){l.logmessage(Fatal, "create share memory error: %s", strerror(errno));}l.logmessage(Info, "create share memory success: %d", shmid);return shmid;
}
我們先使用一個進程processA.cc
來測試一下
#include "resource.hpp"int main()
{int shmid = GetShareMem();l.logmessage(Debug, "process quit...");return 0;
}
結果如下:
2.2 補充:key值和shmid的區別
不同的進程在選擇共享內存通信時,是通過key
值來保證共享內存的唯一性,那這里就存在一個問題:shmget
函數也會返回一個值,我們把這個值稱為共享內存段標識符shmid
,而往后要對共享內存進行操作(如附加、分離、刪除等)的時候,通常使用的是shmid
來進行操作。為什么要創建兩種共享內存的標識呢?直接使用一個來進行標識不就夠了嘛?
那么這里我就可以舉一個生活中的例子來進行解釋:我們中國有14+億的人口,每個人出生之后就會擁有一個獨一無二(唯一)的身份證號,通過這個身份證號我們就可以標識一個人,那為什么還要給每個人取一個名字呢?直接使用身份證號來代替名字不就可以了嗎?之所以這么做是因為使用名字可以更加方便的社交,在短范圍內能夠更快的確定一個人,如果使用身份證號的話就會導致標識和管理的時候比較臃腫。
那么這里也是同樣的道理,key
就相當于身份證號,shmid
相當于姓名。站在用戶角度,使用key
值來標定共享內存太難了太復雜了,所以我們使用shmid
來標識共享內存這樣可以更加的方便和容易;而操作系統它不嫌麻煩他為了更加嚴謹的標識共享內存就必須得使用更加復雜的key
值來進行標定,就好比政府機構在處理具體某個人的事情時,都會拿身份證號來標定某個人,因為身份證號更加的復雜、更加的準確、權威性更高。因此key
是給內核層使用的。
那key
值在哪里存儲的呢?我們說操作系統中存在多個共享內存所以要進行先描述在組織,那么描述共享內存的shm
結構體中就存在一個字段專門用來記錄共享內存的key
值,所以key
值會通過系統調用shmget
設置進共享內存的屬性中的用來表示共享內存在內核中的唯一性。
總之,shmid
相當于文件系統的文件描述符fd
,而key
就相當于文件系統的inode
編號,inode
編號是用來給操作系統看的,fd
是用來給我們操作者使用的,操作系統之所以這么做是為了方便標識符的解藕,用戶層和操作系統層使用不同的東西來標識內存,這樣用戶層和操作系統層就不會發生相互的干擾,也就是一個層面出現了問題不會影響另外的一個層面,那么這就是key
(內核層使用)和shmid
(用戶層使用)的區別。
2.3 shmctl函數 — 釋放
如上,當我再次啟動進程processA
的時候,我們發現使用shmget
函數創建共享內存失敗了,原因是文件存在(共享內存段存在)。通過分析我們知道:我使用IPC_CREAT
和IPC_EXCL
選項保證進程每次申請的共享內存一定是新的,而在第一次運行完進程processA
,我們的代碼內并沒有手動釋放共享內存段,因此導致報錯!
在Linux
中,如果想查看操作系統管理的共享內存段,我們可以使用以下命令:
ipcs -m
共享內存的生命周期是隨內核的(操作系統重啟,共享內存通常才會被釋放)!因為共享內存是由用戶向操作系統申請的,如果不主動關閉,共享內存會一直存在。另外,如果程序頻繁地分配共享內存而不釋放,系統的可用內存資源會逐漸減少,可能導致系統性能下降或者其它進程受到影響(內存泄漏)。
有兩種方法可以釋放共享內存段:
- 使用以下命令來指定釋放共享內存段
ipcrm -m <shmid>
- 使用系統調用接口
shmctl
函數
shmctl
函數用于對共享內存進行控制操作,例如獲取共享內存信息、設置共享內存權限、銷毀共享內存等。它的原型如下:
#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數說明:
-
shmid
:共享內存標識符shmid
-
cmd
:控制命令,可以是下列之一:IPC_STAT
:獲取共享內存的狀態信息,并將其存儲在buf
結構體中。IPC_SET
:設置共享內存的狀態信息,使用buf
結構體中提供的新值。IPC_RMID
:刪除共享內存段。
-
buf
:指向描述共享內存的結構體shmid_ds
的指針,用于存儲或傳遞共享內存的狀態信息。如果是刪除共享內存段,此參數可以設置為nullptr
-
返回值:成功返回
0
,失敗返回-1
【代碼樣例】
#include "resource.hpp"int main()
{// 創建共享內存段int shmid = GetShareMem();l.logmessage(Debug, "create shm done");// 銷毀共享內存段int res = shmctl(shmid, IPC_RMID, nullptr);if (res == -1){l.logmessage(Error, "share Memory destroy failed, shmid is %d", shmid);exit(Error);}l.logmessage(Debug, "share Memory destroy success, shmid is %d", shmid);l.logmessage(Debug, "process quit...");return 0;
}
【程序結果】
2.4 shmat函數 — 進程掛接共享內存
shmat
函數的基本作用是將一個共享內存段映射到調用進程的地址空間的共享區,從而使得進程可以直接訪問共享內存中的數據。
函數基本原型如下:
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
-
shmid
:共享內存的標識符shmid
。 -
shmaddr
:指定共享內存段映射到進程地址空間的起始地址,通常設為nullptr
,讓系統自動選擇合適的地址。 -
shmflg
:附加標志,通常設為0
。 -
返回值:成功返回指向共享內存的起始地址的指針;失敗返回
nullptr
。 -
代碼寫法有點類似于
malloc
函數 -
注意:進程退出后,會自動解除掛接。
為了更好觀察進程掛接共享內存的個數,在銷毀共享內存段之前休眠10
秒
#include "resource.hpp"int main()
{// 1. 創建共享內存段int shmid = GetShareMem();l.logmessage(Debug, "create shm done");// 2. 掛接共享內存段char *shmaddress = (char *)shmat(shmid, nullptr, 0);if (shmaddress == nullptr){l.logmessage(Error, "processA attach failed, shmid is %d", shmid);}l.logmessage(Debug, "processA attach success, shmid is %d", shmid);// 休眠10ssleep(10);// 3. 銷毀共享內存段int res = shmctl(shmid, IPC_RMID, nullptr);if (res == -1){l.logmessage(Error, "share Memory destroy failed, shmid is %d", shmid);exit(Error);}l.logmessage(Debug, "share Memory destroy success, shmid is %d", shmid);l.logmessage(Debug, "process quit...");return 0;
}
【程序結果】
2.5 shmdt函數 — 手動解除進程和共享內存掛接
shmdt
函數用于將共享內存從當前進程的地址空間中分離,即取消共享內存的映射。這個函數的原型如下:
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
shmaddr
參數是一個指向共享內存段起始地址的指針,通常是shmat
函數的返回值。- 返回值:成功返回
0
,失敗返回-1
代碼演示略 ~
三、使用以上接口讓兩個進程通信
一般而言,只要有一方進程創建了共享內存段,另一方進程直接獲取其共享內存段標識符shmid
后,即可進行通信。
resource.hpp
主要是封裝獲取共享內存段接口,確保兩個進程使用的是同一塊共享內存。
#pragma once#include <iostream>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <string>
#include <cstdlib>
#include <cstring>
#include "log.hpp"using namespace std;const string pathname = "/home/wj"; // ftok函數的第一個參數
int proj_id = 'A'; // ftok函數的第二個參數
log l; // 日志對象key_t Getkey()
{key_t key = ftok(pathname.c_str(), proj_id);if (key == -1){l.logmessage(Fatal, "ftok error: %s", strerror(errno));exit(1);}l.logmessage(Info, "ftok success, key is: 0x%x", key);return key;
}int GetShareMem()
{key_t key = Getkey();int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0666);if (shmid == -1){l.logmessage(Fatal, "create share memory error: %s", strerror(errno));}l.logmessage(Info, "create share memory success, shmid is %d", shmid);return shmid;
}int GetShm()
{// 大小:獲取一個已存在的共享內存段的標識符,這個參數可以設置為0來表示忽略// 第三個參數:獲取已存在的共享內存時,可以設置為 0return shmget(Getkey(), 0, 0);
}
processA.cc
主要是負責讀取共享內存段的數據。注意:此進程要先運行起來。
#include "resource.hpp"int main()
{// 1. 創建共享內存段int shmid = GetShareMem();// 2. 掛接共享內存段char *shmaddress = (char *)shmat(shmid, nullptr, 0);// 3. 通信while (true){// 假設processA進程作為客戶端,負責讀取cout << "client say@ " << shmaddress << endl;if (strcmp(shmaddress, "quit\n") == 0){break;}sleep(1);}// 4. 取消掛接shmdt(shmaddress);// 5. 銷毀共享內存段int res = shmctl(shmid, IPC_RMID, nullptr);return 0;
}
processB
主要是向共享內存段寫入數據。當寫入quit
時整個通信過程結束。
#include "resource.hpp"int main()
{// 1. 獲取shmidint shmid = GetShm();// 2. 掛接共享內存char *shmaddress = (char *)shmat(shmid, nullptr, 0);// 3. 通信while (true){// processB進程作為服務端,負責寫cout << "請輸入:";char *context = fgets(shmaddress, 4096, stdin);if (context != nullptr && strcmp(shmaddress, "quit\n") == 0){break;}}// 4. 取消掛接shmdt(shmaddress);return 0;
}
- 程序結果
四、共享內存的特性
-
共享內存沒有同步和互斥之類的保護機制。即讀寫雙方可以同時訪問共享內存,這會導致數據不一致問題,這個問題的解決方案將在下方會介紹到。
-
共享內存是所有的進程間通信中,速度最快的!原因在于它減少數據拷貝。在使用共享內存時,多個進程可以直接訪問同一塊物理內存,而不需要將數據從一個進程的地址空間復制到另一個進程的地址空間。這避免了數據在內存之間的復制,從而減少了通信的開銷和延遲。
-
共享內存內部的數據由用戶自己維護(讀完要自己清空)。
-
共享內存的生命周期是隨內核的,用戶不主動刪除,共享內存會一直存在(除非內核重啟或用戶釋放)
-
共享內存的大小一般建議是
4096
的整數倍,內存管理的一頁大小為4096
字節(4KB
)。若申請4097
,則系統會分配4096 * 2
,但用戶還是只能使用4097
的空間,會存在4095
字節空間的浪費。
五、共享內存的屬性
而我們知道,因為系統中不止一對進程在進行通信,可能會存在多個,那么操作系統就要在物理內存上開辟多個共享內存,那么操作系統就必須對這些區域進行管理,這又得搬出管理的六字真言:先描述,再組織。在Unix/Linux
中,描述共享內存段的信息通常通過 struct shmid_ds
結構體來表示:
struct shmid_ds
{/*這是一個 struct ipc_perm 結構體,用于描述共享內存的操作權限struct ipc_perm 包含了共享內存段的擁有者、組和訪問權限等信息。*/struct ipc_perm shm_perm; // shm_segsz表示共享內存段的大小,單位是字節int shm_segsz; // shm_atime表示最后一次附加該共享內存段的時間。__kernel_time_t shm_atime; // shm_dtime表示最后一次分離該共享內存段的時間。__kernel_time_t shm_dtime; // 表示最后一次更改該共享內存段的時間。__kernel_time_t shm_ctime;// shm_cpid表示關聯共享內存的進程標識符PID__kernel_ipc_pid_t shm_cpid;// shm_lpid表示最后一個操作該共享內存段的進程的 PID__kernel_ipc_pid_t shm_lpid; // shm_nattch表示當前使用到該共享內存段的進程數unsigned short shm_nattch; // ...
};
// struct ipc_perm 結構體
struct ipc_perm
{// key 用于標識共享內存段。不同的進程可以通過這個key來訪問同一個共享內存段。key_t __key; // uid 擁有者(owner)的有效用戶ID(UID),即對共享內存段有讀寫權限的用戶的UID。uid_t uid; // gid 這是擁有者的有效組ID(GID),即對共享內存段有讀寫權限的用戶所在的組的GID。gid_t gid; // cuid 這是創建者(creator)的有效用戶ID(UID),即創建共享內存段的進程的UID。 uid_t cuid; // cgid 這是創建者的有效組ID(GID),即創建共享內存段的進程所在的組的GID。 gid_t cgid; // mode 這個字段包含了權限和一些特定標志,如使用IPC_CREAT來創建IPC對象。unsigned short mode; // __seq 這個字段是序列號,用于維護IPC對象的序列。 unsigned short __seq;
};
最后再通過諸如鏈表、順序表等數據結構將這些結構體對象管理起來。因此,往后我們對共享內存的管理,只需轉化為對某種數據結構的增刪查改。
我們可以使用shmctl
函數來獲取屬性信息(具體用法查看往上翻)
int main()
{// 創建共享內存段int shmid = GetShareMem();// 掛接共享內存段char *shmaddress = (char *)shmat(shmid, nullptr, 0);// 獲取共享內存的屬性struct shmid_ds shmds;shmctl(shmid, IPC_STAT, &shmds);cout << "共享內存的大小:" << shmds.shm_segsz << endl;cout << "共享內存的連接數:" << shmds.shm_nattch << endl;printf("共享內存的key值:0x%x\n", shmds.shm_perm.__key);// 取消掛接shmdt(shmaddress);return 0;
}
【程序結果】
六、解決共享內存沒有同步和互斥保護機制問題
共享內存的特點是 無讀寫規則限制,進程即可讀也可寫,容易造成沖突,因此我們可以對其加以限制,所使用的工具正是命名管道。
邏輯思路是這樣的:當共享內存的寫方向共享內存段寫完數據后,使用命名管道向讀方發送一條通知,說明可以向共享內存讀取數據了。
resource.hpp
增加了創建命名管道的類
#pragma once#include <iostream>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <string>
#include <sys/stat.h>
#include <cstdlib>
#include <cstring>
#include "log.hpp"using namespace std;const string pathname = "/home/wj"; // ftok函數的第一個參數
int proj_id = 'A'; // ftok函數的第二個參數
log l; // 日志對象key_t Getkey()
{key_t key = ftok(pathname.c_str(), proj_id);if (key == -1){l.logmessage(Fatal, "ftok error: %s", strerror(errno));exit(1);}l.logmessage(Info, "ftok success, key is: 0x%x", key);return key;
}int GetShareMem()
{key_t key = Getkey();int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0666);if (shmid == -1){l.logmessage(Fatal, "create share memory error: %s", strerror(errno));}l.logmessage(Info, "create share memory success, shmid is %d", shmid);return shmid;
}int GetShm()
{// 大小:獲取一個已存在的共享內存段的標識符,這個參數可以設置為0來表示忽略// 第三個參數:獲取已存在的共享內存時,可以設置為 0return shmget(Getkey(), 0, 0);
}// ================== 命名管道 ==============================
enum
{// 規定錯誤碼從1開始遞增MKFIFO_FAIL = 1, // 創建匿名管道失敗UNLINK_FAIL, // 刪除匿名管道失敗OPEN_FAIL // 打開文件失敗
};class Init
{
public:Init(){// 創建管道int n = mkfifo("./myfifo", 0664);if (n == -1){perror("mkfifo");exit(MKFIFO_FAIL);}}~Init(){int m = unlink("./myfifo");if (m == -1){perror("unlink");exit(UNLINK_FAIL);}}
};
processB.cc
是寫方,當寫完后就向管道寫入一個字符作為通知。
int main()
{// 1. 獲取shmidint shmid = GetShm();// 2. 掛接共享內存char *shmaddress = (char *)shmat(shmid, nullptr, 0);// 打開命名管道int fd = open("./myfifo", O_WRONLY);if (fd == -1){exit(OPEN_FAIL);}// 3. 通信while (true){// processB進程作為服務端,負責寫cout << "請輸入:";char *context = fgets(shmaddress, 4096, stdin);// 寫完之后,通知對方來讀取。write(fd, "c", 1);if (context != nullptr && strcmp(shmaddress, "quit\n") == 0){break;}}// 4. 取消掛接shmdt(shmaddress);close(fd);return 0;
}
processA.cc
是讀方,在向共享內存段讀取之前,先判斷管道是否有“通知”,有則可以讀取。
int main()
{// 創建管道Init init;// 1. 創建共享內存段int shmid = GetShareMem();// 2. 掛接共享內存段char *shmaddress = (char *)shmat(shmid, nullptr, 0);// 打開命名管道int fd = open("./myfifo", O_RDONLY);if (fd == -1){exit(OPEN_FAIL);}// 3. 通信while (true){// 假設processA進程作為客戶端,負責讀取// 在讀取之前先去管道看看是否有通知char c;ssize_t s = read(fd, &c, 1);// s == 0 說明沒讀到,那就繼續讀取if (s == 0){continue;}// s == -1說明讀取發生錯誤,那就退出if (s == -1){break;}cout << "client say@ " << shmaddress << endl;if (strcmp(shmaddress, "quit\n") == 0){break;}sleep(1);}// 4. 取消掛接shmdt(shmaddress);// 5. 銷毀共享內存段int res = shmctl(shmid, IPC_RMID, nullptr);close(fd);return 0;
}
- 程序結果
七、本篇博客源代碼
Gitee
鏈接:點擊跳轉