自動寫作文網(wǎng)站建站模板免費下載
共享內(nèi)存
共享內(nèi)存是一種進程間通信(IPC)機制,它允許多個進程訪問同一塊內(nèi)存區(qū)域。這種方法可以提高效率,因為數(shù)據(jù)不需要在進程之間復制,而是可以直接在共享的內(nèi)存空間中讀寫。
?使用共享內(nèi)存的步驟通常包括:
- 創(chuàng)建共享內(nèi)存:一個進程shmget()創(chuàng)建共享內(nèi)存區(qū)域。
- 映射共享內(nèi)存:其他進程將該共享內(nèi)存映射到自己的地址空間中的共享區(qū)中。
- 讀寫數(shù)據(jù):進程可以在共享內(nèi)存中讀寫數(shù)據(jù),進行通信。
- 解除映射和刪除:使用完后,進程解除映射并清理共享內(nèi)存。
shmget()創(chuàng)建共享內(nèi)存 獲取標識符
int shmget(key_t key, size_t size, int shmflg);
#include <sys/shm.h>
參數(shù)
- key: 一個唯一的標識符,用于區(qū)分不同的共享內(nèi)存段??梢允褂?
ftok
?函數(shù)生成。- size: 請求的共享內(nèi)存段的大小(以字節(jié)為單位)。
- shmflg: 位圖 控制標志,用于指定權(quán)限和其他選項。常見的選項包括:
IPC_CREAT
: 如果共享內(nèi)存段不存在則創(chuàng)建它。如果存在就獲取它IPC_EXCL|?
IPC_CREAT
?:一起使用,表示如果共享內(nèi)存段不存在就創(chuàng)建它,反之,存在則返回錯誤。(返回成功一定是創(chuàng)建新的共享內(nèi)存)- 權(quán)限標志,例如?
0666
?表示可讀可寫。返回值
- 成功時,返回共享內(nèi)存標識符(非負整數(shù))。(相當于FILE*)
- 失敗時,返回?
-1
,并設(shè)置?errno
?以指示錯誤類型。
key作為標識符但不能由系統(tǒng)創(chuàng)建分配給進程,而是由用戶自己創(chuàng)建并給進程,但要確保key是唯一的。
我們知道key是不同進程找到同一個內(nèi)存空間的關(guān)鍵,要確保它們的key是一樣的。如果是系統(tǒng)分配fey,A進程先創(chuàng)建了共享內(nèi)存獲得一個keg,B進程如果想訪問A創(chuàng)建的共享內(nèi)存,就要獲取到key,但A B進程相互獨立不可能從A進程中獲取到key。
但如果是用戶給,可以讓用戶先生成唯一的key,再把key作為全局變量寫在AB進程的源代碼中,這樣AB進程就可以通過同一個key訪問到同一個共享內(nèi)存了。
如何生成唯一的key?
ftok()
key_t ftok(const char *pathname, int proj_id); key_t key = ftok("/path/to/file", 'R'); // 'R' 是一個項目標識符
ftok()函數(shù)是一個用于生成唯一鍵值的系統(tǒng)調(diào)用,它接受一個文件路徑和一個項目標識符(也可以是數(shù)字,通常是一個字符),并返回一個唯一的key。這個方法基于文件的 inode 號,因此只要文件存在且未被刪除,返回的key就會是唯一的。
ipcs -m命令?顯示所有的共享內(nèi)存信息
ipcs -m 用于顯示系統(tǒng)中當前存在的共享內(nèi)存段的信息。
ipcs -m
key shmid owner perms bytes nattch status
0x12345678 12345 user 666 1024 2
這條命令會列出所有的共享內(nèi)存段,包括它們的 shmid、鍵值、大小和其他信息。
bytes 1024 操作系統(tǒng)申請空間是按塊為單位(4kb...)來申請空間的,4096*x。假設(shè)該操作系統(tǒng)塊的大小為4kb如果你申請4097字節(jié),還是會申請8kb空間,但只讓你用4097字節(jié)的空間,越界就報錯。
ipcrm -m命令刪除共享內(nèi)存段
ipcrm -m shmid
shmctl() 控制共享內(nèi)存 IPC_RMID
刪除
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數(shù)
shmid
:共享內(nèi)存段的標識符,通常是通過?shmget
?函數(shù)獲取的。cmd
:命令,用于指定要執(zhí)行的操作,可以是以下常用值之一:
IPC_STAT
:將共享內(nèi)存段的信息填充到?buf
?指向的結(jié)構(gòu)中。IPC_RMID
:標記共享內(nèi)存段以便刪除。SHM_LOCK
:鎖定共享內(nèi)存段。SHM_UNLOCK
:解鎖共享內(nèi)存段。SHM_INFO
:獲取共享內(nèi)存的統(tǒng)計信息(POSIX擴展)。buf
:指向?shmid_ds
?結(jié)構(gòu)的指針,用于存儲共享內(nèi)存的元數(shù)據(jù)。- 成功時返回 0。
- 失敗時返回 -1,并設(shè)置?
errno
?以指示錯誤原因。
shmat() 將共享內(nèi)存掛接到自己的地址空間中
void* shmat(int shmid, const void* shmaddr, int shmflg);
參數(shù)說明
- shmid: 共享內(nèi)存段的標識符,通常通過?
shmget()
?獲取。- shmaddr: 指定共享內(nèi)存掛接到虛擬地址空間的起始地址。如果為?
NULL
,系統(tǒng)會選擇一個合適的地址。- shmflg: 標志位: 0默認附加模式,進程可以讀寫共享內(nèi)存。?
SHM_RDONLY
: 只讀模式,進程只能讀取數(shù)據(jù),不能寫入。返回值
- 成功時,返回指向共享內(nèi)存掛接到虛擬地址空間的起始地址。
- 失敗時,返回?
(void*) -1
,并設(shè)置?errno
?指示錯誤類型。
注意共享內(nèi)存也是有權(quán)限的,進程如果沒有對應的權(quán)限是不能完成掛接的。
shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0666); // 創(chuàng)建共享內(nèi)存,權(quán)限為可讀可寫
在shmget()創(chuàng)建共享內(nèi)存時,我們就可以設(shè)置權(quán)限0666:所有用戶都可以讀寫。
如果不設(shè)置權(quán)限,默認權(quán)限是0600:創(chuàng)建者可讀寫 其他人無權(quán)限
shmdt()取消掛載
int shmdt(const void *shmaddr);
const void *shmaddr:掛載到進程的虛擬地址空間的起始地址?
shmdt()于從進程的地址空間分離已經(jīng)附加的共享內(nèi)存段。它不會刪除共享內(nèi)存段本身。
shmctl(IPC_RMID) :用于標記共享內(nèi)存段為刪除,等待所有附加的進程分離后釋放資源。
共享內(nèi)存通信速度最快
1.直接訪問:共享內(nèi)存允許多個進程直接訪問同一塊內(nèi)存區(qū)域,避免了數(shù)據(jù)復制的開銷。這與其他通信方式(如管道、消息隊列等)不同,后者通常需要在進程之間復制數(shù)據(jù)
管道:數(shù)據(jù)從外設(shè)讀取到進程A的內(nèi)核空間,然后通過系統(tǒng)調(diào)用將數(shù)據(jù)寫入管道。B通過系統(tǒng)調(diào)用從管道中讀取數(shù)據(jù),再將其拷貝到自己的內(nèi)存中。
這種方式總共涉及四次拷貝:一次從外設(shè)到進程A的內(nèi)核空間,一次從內(nèi)核空間寫入管道,一次從管道讀取到進程B的內(nèi)核空間,最后一次從內(nèi)核空間到進程B的內(nèi)存。
共享內(nèi)存 :數(shù)據(jù)從外設(shè)讀取到內(nèi)核空間后,進程A和進程B可以直接訪問共享的內(nèi)存區(qū)域,避免了多次拷貝。
因此,總的拷貝次數(shù)減少為兩次:一次從外設(shè)讀取到內(nèi)核空間,另一次是進程B直接從共享內(nèi)存讀取數(shù)據(jù)。
2.低延遲:由于不涉及操作系統(tǒng)內(nèi)核的上下文切換,共享內(nèi)存通信的延遲較低,特別適合需要高頻率、低延遲的數(shù)據(jù)交換的場景。
3.高吞吐量:共享內(nèi)存能夠支持大量數(shù)據(jù)的快速傳輸,適合處理大規(guī)模數(shù)據(jù)或高并發(fā)的情況。
4.減少系統(tǒng)調(diào)用:其他通信機制往往需要進行系統(tǒng)調(diào)用,而共享內(nèi)存可以減少這種需求,從而提升性能。
消息隊列
os提供一個隊列,A B進程都可以看到這個隊列,把結(jié)構(gòu)體struct data作為結(jié)點放入隊列,再讓另一個進程拿該節(jié)點,實現(xiàn)通信。
怎么知道這個節(jié)點是其他進程放的呢?
用戶要自己創(chuàng)建struct data,里面有int type標識符,定義A進程標識符1 B為2,這樣就可以區(qū)分拿與自己標識符不同的節(jié)點。
msgget() msgctl()?
這兩個函數(shù)用法和shmget() shmctl() 差不多
int msgget(key_t key, int msgflg);
參數(shù)說明:
key
: 消息隊列的鍵值,可以通過?ftok()
?函數(shù)生成。msgflg
: 控制消息隊列的行為,通常使用以下標志:
IPC_CREAT
: 如果消息隊列不存在則創(chuàng)建一個。IPC_EXCL
: 如果消息隊列已經(jīng)存在,調(diào)用失敗。- 權(quán)限位(如?
0666
)控制訪問權(quán)限。返回值:
- 成功時返回消息隊列的標識符(非負整數(shù))。
- 失敗時返回 -1。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
參數(shù)說明:
msqid
: 消息隊列的標識符。cmd
: 控制命令,可以是以下之一:
IPC_STAT
: 獲取消息隊列的狀態(tài)信息。IPC_SET
: 設(shè)置消息隊列的屬性。IPC_RMID
: 刪除消息隊列。buf
: 指向?msqid_ds
?結(jié)構(gòu)的指針,用于存放或設(shè)置屬性。返回值:
- 成功時返回 0。
- 失敗時返回 -1。
ipcs -q? ipcrm?-m
ipcs -q 用于顯示當前系統(tǒng)中所有的消息隊列的信息。
------ Message Queues --------
key msqid owner perms used-bytes
0x12345678 12345 user1 666 0
0x87654321 12346 user2 666 0
ipcrm?-m刪除共享內(nèi)存段
ipcrm -m <shmid>
msgsnd() msgrcv() 特有
msgsnd() msgrcv()是消息隊列特有的函數(shù)
msgsnd() 發(fā)送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
參數(shù)說明:
msqid
: 消息隊列的標識符。msgp
: 指向要發(fā)送的消息節(jié)點的指針,消息的結(jié)構(gòu)體struct msgbuf要以標識符long mtype開頭?msgsz
: 消息內(nèi)容的字節(jié)數(shù)(不包括?mtype
)。msgflg
: 自己的標識符,常用值包括?IPC_NOWAIT
(如果隊列已滿則不等待,直接返回錯誤)。0: 默認行為,發(fā)送或接收消息時會阻塞,直到操作成功。返回值:
- 成功時返回 0。
- 失敗時返回 -1。
msgrcv() 接收消息
int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
參數(shù)說明:
msqid
: 消息隊列的標識符。msgp
: 指向接收消息的緩沖區(qū)的指針。指向struct datamsgsz
: 消息內(nèi)容的最大字節(jié)數(shù)(不包括?mtype
)。msgtyp
: 指定接收消息的標識符mtype,可以是特定類型的消息(如?1
),也可以是?0
(接收任何類型的消息)或?-1
(接收隊列中最早的消息)。msgflg
: 控制接收行為的標志,常用值包括?IPC_NOWAIT
(如果隊列為空則不等待,直接返回錯誤)。返回值:
- 成功時返回接收到的消息字節(jié)數(shù)。
- 失敗時返回 -1。
信號量
信號量是一種用于進程間同步和互斥的機制,主要用于控制多個進程對共享資源的訪問。它通過維護一個整型計數(shù)器來實現(xiàn),計數(shù)器的值表示可用資源的數(shù)量或狀態(tài)。
我們之前學過管道,我們把管道的資源看作一個整體,要寫整個資源所有地方都可以寫,要讀都可以讀。
現(xiàn)在我們把整個資源,分成多份,一個進程訪問一份。整個資源,有部分可能在寫入,還有部分可能在讀,不同進程,訪問共享資源,有一定并發(fā)性。
1.但是我們怎么知道整個資源里面還有多少份沒有進程占用?
我們可以用一個計算器count來記錄里面剩余個數(shù),count=16 每進入一個進程count--(這種操作稱作P操作),出去count++(V操作)。
P操作(等待):
- 當線程或進程希望訪問某個共享資源時,它會執(zhí)行P操作。這個操作的作用是檢查信號量的值:
- 如果信號量的值大于0,表示資源可用,線程可以繼續(xù)執(zhí)行,并且信號量的值減1。
- 如果信號量的值為0,表示資源不可用,線程會被阻塞,直到信號量的值變?yōu)檎?/li>
V操作(釋放):
- 當線程或進程完成對共享資源的使用后,會執(zhí)行V操作。這個操作的作用是將信號量的值增加1,表示資源現(xiàn)在可用。如果有其他線程在等待這個資源,執(zhí)行V操作后會喚醒其中一個等待的線程。
2.現(xiàn)在有出現(xiàn)一個問題怎么讓不同進程看到同一個count呢?
可以用共享內(nèi)存 管道 讓進程與進程間建立聯(lián)系就可以3.但這樣又有一個新問題,count++這種操作并不是原子性的,多個線程同時修改同一數(shù)據(jù)引發(fā)數(shù)據(jù)競爭。
使用二進制信號量(或互斥鎖),可以確保在任何時刻只有一個線程或進程能夠訪問臨界區(qū)(共享資源)。當一個線程進入臨界區(qū)時,它會調(diào)用 P 操作(等待),如果信號量值為 0,其他線程會被阻塞,直到該線程調(diào)用 V 操作(釋放),將信號量值加 1。
信號量可以分為兩種類型:
1.二進制信號量。count==1
2.計數(shù)信號量。count>1
1.semget()
可以創(chuàng)建nsems個信號量,一個信號量管理count個資源。
int semget(key_t key, int nsems, int semflg);
key
: 唯一標識符。nsems
: 信號量的數(shù)量。semflg
: 權(quán)限標志(如?IPC_CREAT
、0666
)。
2.semctl()
控制信號量集的操作,如獲取、設(shè)置信號量值或刪除信號量。
int semctl(int semid, int semnum, int cmd, ...);
semid
: 信號量集標識符。semnum
: 信號量在集合中的索引。cmd
: 操作命令(如?SETVAL
、GETVAL
、IPC_RMID
?等)。
semnum:信號量集合允許我們同時管理多個信號量,
semnum
就是用來指定我們想要操作的具體信號量。在一個信號量集合中,每個信號量都有一個唯一的索引,從 0 開始編號。例如,如果一個信號量集合中有三個信號量,索引將是 0、1 和 2。
3.semop()
?執(zhí)行對信號量的操作,如 P(wait)和 V(signal)。
int semop(int semid, struct sembuf *sops, size_t nsops);struct sembuf {unsigned short sem_num; // 信號量在集合中的索引short sem_op; // 要執(zhí)行的操作(正數(shù)表示 V 操作,負數(shù)表示 P 操作)short sem_flg; // 操作標志(如 `IPC_NOWAIT`)
};
semid
: 信號量集標識符。sops
: 指向?sembuf
?結(jié)構(gòu)數(shù)組的指針,定義了要執(zhí)行的操作。nsops
: 數(shù)組中操作的數(shù)量。(對nsops個信號量進行PV操作)
ipcs -s?
顯示當前系統(tǒng)中所有的信號量信息,包括信號量的標識符、擁有者、權(quán)限和其他相關(guān)信息。
------ Semaphore Arrays --------
KEY ID OWNER PERMS NSEMS
0x12345678 12345 user 666 1
System V怎么實現(xiàn)IPC的
1.應用角度,看IPC屬性
struct shmid_ds里面是共享內(nèi)存的屬性,第一個成員變量就是struct ipc_prem結(jié)構(gòu)體。而消息隊列 信號量同樣也是以struct ipc_prem結(jié)構(gòu)體開頭,struct ipc_prem結(jié)構(gòu)體里面就保存著key值。也就是說不同 IPC 機制可以通過相同的方式來管理和控制訪問權(quán)限,不同的IPC機制都有自己的一套key值,所以共享內(nèi)存 消息隊列 信號量的key可能重復,但在一種IPC機制中key是唯一的。
2.從內(nèi)核角度,看IPC結(jié)構(gòu)
有全局的結(jié)構(gòu)體ipc_ids,里面有struct ipc_id_ary* entries指向結(jié)構(gòu)體ipc_id_ary。
結(jié)構(gòu)體ipc_id_ary最后一個成員變量是柔性數(shù)組,也就是說它可以動態(tài)保存kern_ipc_perm指針。kern_ipc_perm結(jié)構(gòu)體里面保存的是IPC不同機制共同的屬性。
為什么說kern_ipc_perm結(jié)構(gòu)體里面保存的是IPC不同機制共同的屬性?
因為在不同PC機制的屬性中第一個成員變量就是kern_ipc_perm結(jié)構(gòu)體,其他成員變量都是根據(jù)不同IPC的實現(xiàn)機制特別增加的。
為什么不同IPC機制的屬性中第一個成員變量一定是kern_ipc_perm結(jié)構(gòu)體?
因為結(jié)構(gòu)體ipc_id_ary中柔性數(shù)組保存的是kern_ipc_perm指針,如果共享內(nèi)存 消息隊列 信號量它們屬性第一個成員變量是kern_ipc_perm,那么就可以指向他們的結(jié)構(gòu)體屬性。
這樣ipc_id_ary數(shù)組就可以找到每個創(chuàng)建的共享內(nèi)存 消息隊列 信號量的屬性,進而進行管理。
其實我們shmget() msgget() semget()獲取的id就是它們在ipc_id_ary數(shù)組的下標。
和key值一樣,id在一種IPC機制中是唯一的,但在不同IPC機制中可能相同。
也就是說一個ipc_id_ary數(shù)組中指向的全是相同IPC機制的屬性。
其實共享內(nèi)存是一種文件
在共享內(nèi)存的屬性中有struct file*的指針,它指向一個文件,文件有inode,知道它的數(shù)據(jù)塊的物理地址。vm_start vm_end是共享內(nèi)存在虛擬地址空間的起始地址 結(jié)束地址,把它與指向文件的內(nèi)存塊的物理地址建立映射關(guān)系,進程可以通過這個映射直接訪問文件內(nèi)容。