??作者:阿潤(rùn)菜菜
??專欄:Linux系統(tǒng)編程
system V共享內(nèi)存介紹
- System V 共享內(nèi)存是一種進(jìn)程間通信的機(jī)制,它允許多個(gè)進(jìn)程共享一塊物理內(nèi)存區(qū)域(稱為“段”)。System V 共享內(nèi)存的優(yōu)點(diǎn)是效率高,因?yàn)檫M(jìn)程之間不需要復(fù)制數(shù)據(jù);缺點(diǎn)是需要進(jìn)程之間進(jìn)行同步,以避免數(shù)據(jù)的不一致性。
- 共享內(nèi)存區(qū)是最快的IPC形式。一旦這樣的內(nèi)存映射到共享它的進(jìn)程的地址空間,這些進(jìn)程間數(shù)據(jù)傳遞不再涉及到
內(nèi)核,換句話說(shuō)是進(jìn)程不再通過(guò)執(zhí)行進(jìn)入內(nèi)核的系統(tǒng)調(diào)用來(lái)傳遞彼此的數(shù)據(jù)
示意圖:
理解共享內(nèi)存IPC
- 進(jìn)程憑什么獨(dú)立?每個(gè)進(jìn)程擁有自己獨(dú)立的進(jìn)程地址空間mm_struct,自己獨(dú)立的映射的物理內(nèi)存空間。進(jìn)程獨(dú)立性的實(shí)現(xiàn)主要依賴于操作系統(tǒng)和硬件的支持。操作系統(tǒng)通過(guò)為每個(gè)進(jìn)程分配獨(dú)立的虛擬地址空間,使得每個(gè)進(jìn)程都有自己的代碼和數(shù)據(jù)空間,這樣不會(huì)被其他進(jìn)程干擾。硬件通過(guò)內(nèi)存管理單元(MMU)來(lái)實(shí)現(xiàn)虛擬地址到物理地址的映射,以及分頁(yè)或分段的方式來(lái)劃分內(nèi)存空間。
- 操作系統(tǒng)還通過(guò)進(jìn)程調(diào)度算法來(lái)控制每個(gè)進(jìn)程的執(zhí)行順序和時(shí)間片,以及通過(guò)進(jìn)程同步和通信機(jī)制來(lái)協(xié)調(diào)多個(gè)進(jìn)程之間的關(guān)系。
-
實(shí)現(xiàn)進(jìn)程間通信的第一個(gè)前提就是如何讓不同的進(jìn)程看到同一份資源,匿名管道我們是通過(guò)子進(jìn)程繼承父進(jìn)程打開的資源,命名管道是通過(guò)兩個(gè)進(jìn)程都打開具有唯一性標(biāo)識(shí)的命名管道文件,而共享內(nèi)存其實(shí)是通過(guò)OS創(chuàng)建一塊shm(共享內(nèi)存塊),然后通過(guò)MMU將shm的地址分別映射到兩個(gè)進(jìn)程的各自地址空間當(dāng)中,那么兩個(gè)進(jìn)程就可以通過(guò)這份虛擬起始地址來(lái)進(jìn)行進(jìn)程間通信。
在應(yīng)用層也就是用戶層,我們只能操作虛擬地址,但內(nèi)核中會(huì)有MMU進(jìn)行虛擬地址的映射,所以進(jìn)程在IPC時(shí),只需要操縱虛擬地址即可,從虛擬地址中讀取或向虛擬地址中進(jìn)行寫入,這樣就完成了共享內(nèi)存式的IPC。 - 所以通過(guò)讓不同的進(jìn)程,看到同一份物理內(nèi)存塊的方式,就叫做共享內(nèi)存!
-
為什么說(shuō)共享內(nèi)存是最快的IPC形式?
共享內(nèi)存是一種進(jìn)程間通信(IPC)的方式,它允許多個(gè)進(jìn)程訪問(wèn)同一塊邏輯內(nèi)存,從而實(shí)現(xiàn)數(shù)據(jù)的快速交換。共享內(nèi)存是最快的IPC形式,因?yàn)樗苊饬藬?shù)據(jù)在進(jìn)程間的復(fù)制,而是直接在內(nèi)存中讀寫。要使用共享內(nèi)存,需要用到一些函數(shù),如shmget, shmat, shmdt, shmctl等。這些函數(shù)可以創(chuàng)建、映射、分離、控制共享內(nèi)存段。共享內(nèi)存的優(yōu)點(diǎn)是高效和靈活,缺點(diǎn)是沒(méi)有提供同步機(jī)制,需要借助其他手段來(lái)實(shí)現(xiàn)進(jìn)程間的同步訪問(wèn),而且共享內(nèi)存沒(méi)有任何保護(hù)機(jī)制 那管道呢?系統(tǒng)接口有封裝。
實(shí)現(xiàn)共享內(nèi)存IPC
認(rèn)識(shí)接口
命令查看 共享內(nèi)存是否已經(jīng)存在
ipcs -m 查看共享內(nèi)存
ipcrm -m 用于刪除共享內(nèi)存 — 注意使用shmid進(jìn)行刪除 類比于文件描述符
System V 共享內(nèi)存的API包括以下幾個(gè)系統(tǒng)調(diào)用:
- shmget(2):創(chuàng)建一個(gè)新的段或獲取一個(gè)已存在的段的標(biāo)識(shí)符(ID)。這個(gè)ID是用來(lái)在其他API中引用段的。
- shmat(2):將一個(gè)已存在的段映射到調(diào)用進(jìn)程的虛擬地址空間中。這樣,進(jìn)程就可以通過(guò)指針來(lái)訪問(wèn)共享內(nèi)存中的數(shù)據(jù)。
- shmdt(2):將一個(gè)段從調(diào)用進(jìn)程的虛擬地址空間中解除映射。這樣,進(jìn)程就不能再訪問(wèn)共享內(nèi)存中的數(shù)據(jù)。
- shmctl(2):對(duì)一個(gè)段進(jìn)行控制操作,例如修改它的權(quán)限、獲取它的狀態(tài)信息、刪除它等。
1.shmget()函數(shù)是用來(lái)創(chuàng)建或打開一塊共享內(nèi)存的,它的原型是:int shmget (key_t key, size_t size, int shmflg);
第一個(gè)參數(shù)key是一個(gè)非零整數(shù),它為共享內(nèi)存段提供一個(gè)外部名,可以用IPC_PRIVATE或ftok()函數(shù)生成。shmget()函數(shù)成功時(shí)返回一個(gè)與key相關(guān)的共享內(nèi)存標(biāo)識(shí)符(非負(fù)整數(shù)),用于后續(xù)的共享內(nèi)存函數(shù)。調(diào)用失敗返回-1。
第二個(gè)參數(shù)size是以字節(jié)為單位指定需要共享的內(nèi)存容量。所有的內(nèi)存分配操作都是以頁(yè)為單位的,所以如果申請(qǐng)的內(nèi)存大小不是頁(yè)的整數(shù)倍,會(huì)被向上取整到最近的頁(yè)大小。
第三個(gè)參數(shù)shmflg是一組標(biāo)志位,它可以指定權(quán)限標(biāo)志、創(chuàng)建標(biāo)志和排他標(biāo)志。權(quán)限標(biāo)志與文件的讀寫權(quán)限一樣,如0644表示允許創(chuàng)建者讀寫,其他用戶只讀。創(chuàng)建標(biāo)志IPC_CREAT表示如果共享內(nèi)存不存在,則創(chuàng)建一個(gè)新的共享內(nèi)存,否則打開已有的共享內(nèi)存。排他標(biāo)志IPC_EXCL表示只有在共享內(nèi)存不存在時(shí),才創(chuàng)建新的共享內(nèi)存,否則返回錯(cuò)誤。
共享內(nèi)存的大小是以4kb為單位的,這是巧合嗎?是這樣的(對(duì)應(yīng)磁盤文件系統(tǒng)):這里第二個(gè)參數(shù)是共享內(nèi)存的大小,一般建議將開辟的共享內(nèi)存大小設(shè)置為4KB的整數(shù)倍,內(nèi)存劃分內(nèi)存塊的基本單位是Page,大小剛好是4KB,所以建議將大小設(shè)置為4KB的整數(shù)倍,如果你設(shè)置成4097什么的,有點(diǎn)浪費(fèi)內(nèi)存,因?yàn)閷?shí)際內(nèi)核會(huì)開辟8KB大小的空間。
2.shmat()函數(shù)用來(lái)將共享內(nèi)存段連接到進(jìn)程的地址空間,它的原型是:void * shmat (int shmid, const void *shmaddr, int shmflg);
第一個(gè)參數(shù)shmid是由shmget()函數(shù)返回的共享內(nèi)存標(biāo)識(shí)符。第二個(gè)參數(shù)shmaddr指定共享內(nèi)存連接到當(dāng)前進(jìn)程中的地址位置,通常為NULL,表示讓系統(tǒng)來(lái)選擇共享內(nèi)存的地址。第三個(gè)參數(shù)shmflg是一組標(biāo)志位,可以指定SHM_RDONLY表示共享內(nèi)存只讀,或者默認(rèn)為0表示可讀可寫。調(diào)用成功時(shí)返回一個(gè)指向共享內(nèi)存第一個(gè)字節(jié)的指針,如果調(diào)用失敗返回-1。
3.shmdt()函數(shù)用來(lái)將共享內(nèi)存從當(dāng)前進(jìn)程中分離,它的原型是:int shmdt (const void *shmaddr);
參數(shù)shmaddr是shmat()函數(shù)返回的地址指針,調(diào)用成功時(shí)返回0,失敗時(shí)返回-1。注意,將共享內(nèi)存分離并不是刪除它,只是使該共享內(nèi)存對(duì)當(dāng)前進(jìn)程不再可用。
4.shmctl()函數(shù)用來(lái)控制共享內(nèi)存的狀態(tài),它的原型是:int shmctl (int shmid, int command, struct shmid_ds *buf);
第一個(gè)參數(shù)shmid是shmget()函數(shù)返回的共享內(nèi)存標(biāo)識(shí)符。第二個(gè)參數(shù)command是要采取的操作,可以取以下三個(gè)值:
IPC_STAT:把shmid_ds結(jié)構(gòu)中的數(shù)據(jù)設(shè)置為共享內(nèi)存的當(dāng)前關(guān)聯(lián)值,即用共享內(nèi)存的當(dāng)前關(guān)聯(lián)值覆蓋shmid_ds的值。
IPC_SET:如果進(jìn)程有足夠的權(quán)限,就把共享內(nèi)存的當(dāng)前關(guān)聯(lián)值設(shè)置為shmid_ds結(jié)構(gòu)中給出的值。
IPC_RMID:刪除共享內(nèi)存段。
第三個(gè)參數(shù)buf是一個(gè)結(jié)構(gòu)指針,它指向共享內(nèi)存模式和訪問(wèn)權(quán)限的結(jié)構(gòu)。調(diào)用成功時(shí)返回0,失敗時(shí)返回-1。
代碼實(shí)現(xiàn)
下面是一個(gè)使用System V 共享內(nèi)存的示例程序,它由兩個(gè)部分組成:writer.c和reader.c。writer.c負(fù)責(zé)創(chuàng)建一個(gè)共享內(nèi)存段,并向其中寫入一些字符串;reader.c負(fù)責(zé)讀取共享內(nèi)存段中的字符串,并打印出來(lái)。
writer.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024 // size of shared memory
int main() {
int shmid; // shared memory ID
key_t key; // key to locate shared memory
char *shm; // pointer to shared memory
// create a key using a file name and a char
if ((key = ftok("writer.c", 'A')) == -1) {
perror("ftok");
exit(1);
}
// create a shared memory segment
if ((shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666)) == -1) {
perror("shmget");
exit(1);
}
// attach the shared memory segment to the process
if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
perror("shmat");
exit(1);
}
// write some strings to the shared memory
strcpy(shm, "Hello, world!");
shm += strlen("Hello, world!");
strcpy(shm, "This is an example of System V shared memory.");
shm += strlen("This is an example of System V shared memory.");
strcpy(shm, "Goodbye!");
// wait until reader finishes reading
while (*shm != '*')
sleep(1);
// detach the shared memory segment from the process
if (shmdt(shm) == -1) {
perror("shmdt");
exit(1);
}
return 0;
}
reader.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024 // size of shared memory
int main() {
int shmid; // shared memory ID
key_t key; // key to locate shared memory
char *shm; // pointer to shared memory
char *s; // pointer to traverse shared memory
// create a key using a file name and a char
if ((key = ftok("writer.c", 'A')) == -1) {
perror("ftok");
exit(1);
}
// get the shared memory segment
if ((shmid = shmget(key, SHM_SIZE, 0)) == -1) {
perror("shmget");
exit(1);
}
// attach the shared memory segment to the process
if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
perror("shmat");
exit(1);
}
// read the strings from the shared memory
s = shm;
while (*s != '\0') {
printf("%s\n", s);
s += strlen(s) + 1;
}
// write a '*' to the shared memory to indicate reading is done
*shm = '*';
// detach the shared memory segment from the process
if (shmdt(shm) == -1) {
perror("shmdt");
exit(1);
}
return 0;
}
為了運(yùn)行這個(gè)示例程序,我們需要先編譯writer.c和reader.c,然后先運(yùn)行writer,再運(yùn)行reader。運(yùn)行結(jié)果如下:
$ gcc writer.c -o writer
$ gcc reader.c -o reader
$ ./writer &
[1] 1234
$ ./reader
Hello, world!
This is an example of System V shared memory.
Goodbye!
[1]+ Done ./writer
$
實(shí)現(xiàn)共享內(nèi)存服務(wù)端和客戶端的通信:查看我的代碼倉(cāng)庫(kù)
補(bǔ)充知識(shí) — 命令操作
ipcs 查看進(jìn)程間通信資源/ipcrm 刪除進(jìn)程間通信資源
-m 針對(duì)共享內(nèi)存的操作
-q 針對(duì)消息隊(duì)列的操作
-s 針對(duì)信號(hào)量的操作
-a 針對(duì)所有資源的操作
深入理解
什么是同步與互斥
- 同步是指協(xié)調(diào)多個(gè)進(jìn)程的執(zhí)行順序,使得某些進(jìn)程在執(zhí)行某些操作之前必須等待其他進(jìn)程完成一些操作。例如,一個(gè)進(jìn)程要讀取一個(gè)文件,必須等待另一個(gè)進(jìn)程寫入該文件。
- 互斥是指保證多個(gè)進(jìn)程對(duì)共享資源的訪問(wèn)不會(huì)發(fā)生沖突,使得某些資源在一個(gè)時(shí)間段內(nèi)只能被一個(gè)進(jìn)程使用。例如,一個(gè)進(jìn)程要打印一份文檔,必須等待打印機(jī)空閑。
OS中常用的同步與互斥機(jī)制有:
- 信號(hào)量與PV操作:信號(hào)量是一個(gè)整數(shù)變量,表示可用資源的數(shù)量。PV操作是兩個(gè)原子操作,用于對(duì)信號(hào)量進(jìn)行加減操作。P操作表示申請(qǐng)一個(gè)資源,如果資源不足則阻塞;V操作表示釋放一個(gè)資源,如果有等待的進(jìn)程則喚醒。
- 管程:管程是一種高級(jí)的同步機(jī)制,它是一種封裝了共享數(shù)據(jù)和對(duì)數(shù)據(jù)操作的過(guò)程的數(shù)據(jù)結(jié)構(gòu)。管程內(nèi)部有一個(gè)互斥鎖和若干條件變量,用于實(shí)現(xiàn)對(duì)共享數(shù)據(jù)的互斥訪問(wèn)和同步控制。
本節(jié)介紹互斥的四個(gè)相關(guān)概念 :
- 什么是臨界資源?
- 臨界資源是指一次僅允許一個(gè)進(jìn)程使用的共享資源,如打印機(jī)、磁帶機(jī)等。臨界資源需要互斥訪問(wèn),即同一時(shí)間只能有一個(gè)進(jìn)程訪問(wèn),否則會(huì)導(dǎo)致數(shù)據(jù)不一致或資源沖突。
- 臨界區(qū)是指每個(gè)進(jìn)程中訪問(wèn)臨界資源的那段代碼,如對(duì)打印機(jī)的操作。臨界區(qū)需要設(shè)置進(jìn)入?yún)^(qū)和退出區(qū),以檢查和控制對(duì)臨界資源的訪問(wèn)。進(jìn)入?yún)^(qū)要判斷是否可以進(jìn)入臨界區(qū),如果可以則設(shè)置標(biāo)志或鎖;退出區(qū)要釋放標(biāo)志或鎖,以便其他進(jìn)程可以進(jìn)入。
2.什么是原子性?
- 原子性是指一個(gè)操作或一組操作要么全部執(zhí)行成功,要么全部不執(zhí)行,不會(huì)被其他進(jìn)程或中斷打斷。原子性是實(shí)現(xiàn)臨界區(qū)互斥的一個(gè)重要條件,因?yàn)槿绻谶M(jìn)入?yún)^(qū)或退出區(qū)被打斷,就會(huì)導(dǎo)致死鎖或饑餓等問(wèn)題。原子性可以通過(guò)硬件指令或軟件方法來(lái)實(shí)現(xiàn)。
共享內(nèi)存的內(nèi)核數(shù)據(jù)結(jié)構(gòu)
下面是OS給用戶暴露的一部分shm的內(nèi)核數(shù)據(jù)結(jié)構(gòu),因?yàn)镺S要進(jìn)行管理,所以實(shí)際在底層中其結(jié)構(gòu)更為復(fù)雜,里面的key被封裝到ipc_perm結(jié)構(gòu)體里面,ipc_perm又被封裝到shmid_ds{}結(jié)構(gòu)體內(nèi)部。
內(nèi)核中的ipc_perm結(jié)構(gòu)體是用來(lái)描述IPC對(duì)象的權(quán)限和所有者的
它的定義如下:
struct ipc_perm {
key_t key; // 調(diào)用shmget()時(shí)給出的關(guān)鍵字
uid_t uid; // 共享內(nèi)存所有者的有效用戶ID
gid_t gid; // 共享內(nèi)存所有者所屬組的有效組ID
uid_t cuid; // 共享內(nèi)存創(chuàng)建者的有效用戶ID
gid_t cgid; // 共享內(nèi)存創(chuàng)建者所屬組的有效組ID
unsigned short mode; // 權(quán)限 + SHM_DEST和SHM_LOCKED標(biāo)志
unsigned short seq; // 序列號(hào)
};
結(jié)構(gòu)中的mode域類似于文件的stat結(jié)構(gòu)的mode域,但是不可以有執(zhí)行權(quán)限。mode值描述如下:
操作者 | 讀 | 寫(更改 更新) |
---|---|---|
用戶 | 0400 | 0200 |
組 | 0040 | 0020 |
其他 | 0004 | 0002 |
IPC對(duì)象包括共享內(nèi)存、消息隊(duì)列和信號(hào)量,它們都是用來(lái)進(jìn)行進(jìn)程通信的一些資源。
IPC對(duì)象的創(chuàng)建、訪問(wèn)和控制都需要使用ipc_perm結(jié)構(gòu)體中信息
IPC資源的組織方式(多態(tài))
對(duì)于System V標(biāo)準(zhǔn)的IPC資源組織方式來(lái)說(shuō),資源的獲取與釋放操作,他們的接口相似度非常高,
所以O(shè)S要對(duì)這些同一標(biāo)準(zhǔn)的各個(gè)通信機(jī)制進(jìn)行管理,他們都有各自的內(nèi)核數(shù)據(jù)結(jié)構(gòu),但都非常的相似,OS系統(tǒng)可以通過(guò)數(shù)組的方式對(duì)這些System V標(biāo)準(zhǔn)的IPC資源進(jìn)行管理。
結(jié)構(gòu)體的第一個(gè)成員地址,在數(shù)字上和結(jié)構(gòu)體對(duì)象本身的地址是相同的。雖然他們類型不同,但是地址的字面值是相同的,所以我們可以只存儲(chǔ)這些內(nèi)核數(shù)據(jù)結(jié)構(gòu)的第一個(gè)字段的地址,用一個(gè)指針數(shù)組來(lái)進(jìn)行存儲(chǔ),因?yàn)殡m然這些IPC資源的內(nèi)核數(shù)據(jù)結(jié)構(gòu)不同,但是他們的第一個(gè)字段的類型都是相同的,都是struct ipc_perm,所以我們可以用指針數(shù)組來(lái)進(jìn)行管理。
當(dāng)要訪問(wèn)具體的某個(gè)IPC資源的內(nèi)核數(shù)據(jù)結(jié)構(gòu)時(shí),我們可以將數(shù)組中的內(nèi)容拿出來(lái),將其強(qiáng)轉(zhuǎn)成對(duì)應(yīng)的IPC資源內(nèi)核數(shù)據(jù)結(jié)構(gòu)的類型,也就是轉(zhuǎn)成結(jié)構(gòu)體類型,那么此時(shí)這個(gè)指針指向的就不再是struct ipc_perm類型的結(jié)構(gòu)體了,而是變?yōu)閟truct shmid_ds或struct semid_ds或struct msqid_ds這幾種IPC內(nèi)核數(shù)據(jù)結(jié)構(gòu)類型的結(jié)構(gòu)體,此時(shí)我們就可以具體的訪問(wèn)某個(gè)IPC資源了。
上面能夠這么做的原因其實(shí)是因?yàn)?,結(jié)構(gòu)體的地址和結(jié)構(gòu)體中第一個(gè)字段的地址 在字面值上是相同的,只是他們類型不同罷了,我們可以通過(guò)類型強(qiáng)轉(zhuǎn)的方式,讓指針指向不同的結(jié)構(gòu)體。
3.
下面組織IPC資源的方式不就是多態(tài)嗎?右邊三個(gè)資源就是派生類,左邊是存儲(chǔ)基類指針的指針數(shù)組,基類指針指向哪個(gè)派生類結(jié)構(gòu)體,就調(diào)用哪個(gè)派生類結(jié)構(gòu)體里的方法成員,只不過(guò)在Linux這里是通過(guò)指針類型強(qiáng)轉(zhuǎn)的方式來(lái)實(shí)現(xiàn)的。
共享內(nèi)存的優(yōu)點(diǎn)和缺點(diǎn)(管道和shm分別數(shù)據(jù)拷貝次數(shù))
共享內(nèi)存的優(yōu)點(diǎn):所有進(jìn)程間通信中速度最快的,只要向shmat返回的虛擬地址寫入數(shù)據(jù),另一個(gè)進(jìn)程直接就可以通過(guò)他自己的shmat返回的虛擬地址讀取到共享內(nèi)存中的數(shù)據(jù),效率非常的高,因?yàn)楣蚕韮?nèi)存能大大減少數(shù)據(jù)的拷貝次數(shù)。
綜合考慮管道和共享內(nèi)存,考慮鍵盤輸入和顯示器輸出,管道和共享內(nèi)存分別有幾次數(shù)據(jù)拷貝呢?如果細(xì)算的話其實(shí)是6次和4次,如果不細(xì)算的話是4次和2次。
有一種說(shuō)法,喜歡把緩沖區(qū)分為內(nèi)核緩沖區(qū)和程序緩沖區(qū),程序緩沖區(qū)指的是語(yǔ)言級(jí)別你所能見到的所有能夠存放數(shù)據(jù)的空間,這些都可以叫做程序緩沖區(qū),是一種籠統(tǒng)的叫法。
管道由于要調(diào)用read和write接口,則必須定義buffer,在讀端和寫端分別都定義出一個(gè)buffer,實(shí)際數(shù)據(jù)會(huì)先從stdin到buffer里面,再?gòu)腷uffer到pipe的內(nèi)核級(jí)緩沖區(qū)中,然后再?gòu)膬?nèi)核級(jí)緩沖區(qū)到讀端的buffer中,最后再?gòu)淖x端的buffer拷貝到stdout的用戶級(jí)緩沖區(qū),這樣算就是4次。
共享內(nèi)存無(wú)須調(diào)用read或write接口,shmat會(huì)直接返回虛擬地址,所以只需將stdin的數(shù)據(jù)拷貝到虛擬地址里面,然后MMU會(huì)將虛擬地址進(jìn)行映射,另一端的進(jìn)程可以直接通過(guò)虛擬地址看到左邊進(jìn)程映射到shm的數(shù)據(jù),所以另一端進(jìn)程也只需要將虛擬地址的數(shù)據(jù)拷貝到stdout的緩沖區(qū)即可,這樣算就是2次。
但我們知道鍵盤輸入的緩沖區(qū)實(shí)際上是先到內(nèi)核標(biāo)準(zhǔn)輸入緩沖區(qū)中的,cin或scanf等標(biāo)準(zhǔn)輸入都是從內(nèi)核標(biāo)準(zhǔn)輸入緩沖區(qū)中拿數(shù)據(jù)的。并且在輸出時(shí),printf或cout等標(biāo)準(zhǔn)輸出其實(shí)是先將數(shù)據(jù)輸出到內(nèi)核標(biāo)準(zhǔn)輸出緩沖區(qū)的,然后才是將數(shù)據(jù)輸出到stdout也就是顯示器文件內(nèi)部的用戶級(jí)緩沖區(qū)。所以如果把這兩步考慮上,那么管道和共享內(nèi)存將各自增加兩次的數(shù)據(jù)拷貝。
認(rèn)識(shí)信號(hào)量
現(xiàn)實(shí)生活中,我們?cè)诳措娪爸?,一定要先買票,看電影,看完走人。
在OS內(nèi)部訪問(wèn)臨界資源:一定要先申請(qǐng)信號(hào)量資源,在使用,然后釋放信號(hào)量資源 。
信號(hào)量的本質(zhì)是一個(gè)計(jì)數(shù)器,通常用來(lái)表示公共資源中,資源數(shù)量多少的問(wèn)題。當(dāng)訪問(wèn)沒(méi)有保護(hù)的公共資源時(shí),會(huì)產(chǎn)生數(shù)據(jù)不一致的問(wèn)題,我們將被保護(hù)起來(lái)的公共資源稱為臨界資源,但大部分資源其實(shí)都是獨(dú)立的。公共資源(內(nèi)存,文件,網(wǎng)絡(luò)等)都是要通過(guò)代碼來(lái)進(jìn)行訪問(wèn)的,這些代碼我們稱為臨界區(qū),其余未訪問(wèn)公共資源的代碼稱為非臨界區(qū)。
當(dāng)信號(hào)量為0時(shí)(count),OS掛起阻塞進(jìn)程。那么是誰(shuí)在申請(qǐng)信號(hào)量? 進(jìn)程在申請(qǐng)!那前提就是 所有進(jìn)程都要看到同一塊信號(hào)量?。?那就得是共享資源,必須保證自己的++ 操作 — 是原子性的!
只要訪問(wèn)公共資源,我們就必須對(duì)公共資源進(jìn)行保護(hù)。所有的進(jìn)程在訪問(wèn)公共資源之前,都必須申請(qǐng)sem信號(hào)量,申請(qǐng)sem信號(hào)量不就需要先看到同一份sem信號(hào)量嗎?那么其實(shí)sem信號(hào)量本身就是公共資源,所以信號(hào)量也必須保證自身操作的安全性,那么信號(hào)量的++或- -等操作也都必須得是原子性的,要么做成功,要么就回到最初狀態(tài)
System V 消息隊(duì)列
1.消息隊(duì)列提供了一個(gè)從一個(gè)進(jìn)程向另外一個(gè)進(jìn)程發(fā)送一塊數(shù)據(jù)的方法
2.每個(gè)數(shù)據(jù)塊都被認(rèn)為是有一個(gè)類型,接收者進(jìn)程接收的數(shù)據(jù)塊可以有不同的類型值
3.IPC資源必須刪除,否則不會(huì)自動(dòng)清除,除非重啟,所以system V IPC資源的生命周期隨內(nèi)核文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-444833.html
內(nèi)核也給我們提供了獲取消息隊(duì)列和控制消息隊(duì)列的系統(tǒng)接口文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-444833.html
到了這里,關(guān)于【Linux】System V 共享內(nèi)存、消息隊(duì)列、信號(hào)量的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!