進(jìn)程間通信方式目前我們已經(jīng)學(xué)了匿名管道,命名管道。讓兩個(gè)獨(dú)立的進(jìn)程通信,前提是看到同一份資源。匿名管道適用于血緣關(guān)系的進(jìn)程,一個(gè)打開(kāi)寫(xiě)端一個(gè)打開(kāi)讀端實(shí)現(xiàn)的。命名管道適用于完全獨(dú)立的進(jìn)程,打開(kāi)同一份文件實(shí)現(xiàn)的。
接下來(lái)我們看看剩下的實(shí)現(xiàn)進(jìn)程間通信的方式。
1.共享內(nèi)存
1.1共享內(nèi)存的原理
可執(zhí)行程序加載到內(nèi)存,OS會(huì)對(duì)進(jìn)程進(jìn)行管理。進(jìn)程的內(nèi)核結(jié)構(gòu)是獨(dú)立的,加載到物理地址的地址也獨(dú)立,因此進(jìn)程具有獨(dú)立性,互不影響。
那共享內(nèi)存是如何實(shí)現(xiàn)兩個(gè)獨(dú)立的進(jìn)程進(jìn)行通信的呢?
1.申請(qǐng)一塊空間(用戶(hù)給OS發(fā)信號(hào),然后OS去申請(qǐng))
2.將創(chuàng)建好的內(nèi)存經(jīng)過(guò)頁(yè)表映射到進(jìn)程地址空間中的一段區(qū)域(將這塊區(qū)域的起始地址返回給用戶(hù),用戶(hù)通過(guò)訪問(wèn)這里的起始地址方式來(lái)進(jìn)行對(duì)這塊區(qū)域的訪問(wèn))
3.未來(lái)不想通信了
a.取消進(jìn)程和內(nèi)存的映射關(guān)系
b.釋放內(nèi)存
上面就是共享內(nèi)存的原理。
這塊申請(qǐng)的內(nèi)存—>共享內(nèi)存
進(jìn)程和內(nèi)存建立映射關(guān)系過(guò)程—>進(jìn)程和共享內(nèi)存掛接
取消進(jìn)程和內(nèi)存的映射關(guān)系—>去關(guān)聯(lián)
釋放內(nèi)存—>釋放共享內(nèi)存
如何理解上面這個(gè)過(guò)程?
在內(nèi)存上申請(qǐng)空間,然后把地址返回,是不是像C/C++空間的申請(qǐng),如malloc函數(shù),底層都是一樣的先去物理內(nèi)存申請(qǐng)空間,然后經(jīng)過(guò)頁(yè)表映射到進(jìn)程地址空間,最后把這塊空間的起始地址返回用戶(hù)。雖然過(guò)程都是一樣,但是malloc申請(qǐng)的空間沒(méi)有辦法讓另一個(gè)進(jìn)程看見(jiàn),因?yàn)檫@塊空間是在堆上申請(qǐng)的,OS并沒(méi)有專(zhuān)門(mén)為malloc這樣的機(jī)制和其他進(jìn)程建立映射關(guān)系策略。
a.進(jìn)程間通信,是專(zhuān)門(mén)設(shè)計(jì)的,用來(lái)IPC
b.共享內(nèi)存是一種通信方式,所有想通信的進(jìn)程,都可以用
c.OS中一定可能會(huì)同時(shí)存在很多的共享內(nèi)存
1.2共享內(nèi)存的概念
通過(guò)讓不同的進(jìn)程,看到同一個(gè)內(nèi)存的方式:共享內(nèi)存。
1.3接口的認(rèn)識(shí)
1.創(chuàng)建共享內(nèi)存。
size 要申請(qǐng)多大的內(nèi)存空間
shmflg 常用參數(shù)。
看到這里這個(gè)參數(shù)是不是和open接口有點(diǎn)類(lèi)似,大寫(xiě)的宏。
shmflg是一個(gè)標(biāo)志位。
IPC_CREAT:如果想創(chuàng)建的共享內(nèi)存不存在,就創(chuàng)建,如果存在就獲取
IPC_EXCL:無(wú)法單獨(dú)使用。
IPC_CREAT | IPC_EXCL:如果不存在就創(chuàng)建,如果存在就出錯(cuò)返回。用戶(hù)創(chuàng)建共享內(nèi)存如果成功,一定是一個(gè)新的共享內(nèi)存。
創(chuàng)建共享內(nèi)存非常容易,那如果保證進(jìn)程看到的是同一塊共享內(nèi)存呢?
key用來(lái)保證。
key是什么不重要,能進(jìn)行唯一性標(biāo)識(shí)最重要。
將路徑名和項(xiàng)目標(biāo)識(shí)符轉(zhuǎn)換為key。
隨便寫(xiě)個(gè)路徑和項(xiàng)目id。經(jīng)過(guò)算法轉(zhuǎn)換成key。
兩個(gè)進(jìn)程傳一樣參數(shù),能保證是同一個(gè)key,因此可以在系統(tǒng)中找到同一個(gè)內(nèi)存。
返回值。成功是返回共享內(nèi)存的標(biāo)識(shí)符,失敗返回-1。
再來(lái)理解key_t key
OS中一定可能會(huì)同時(shí)存在很多的共享內(nèi)存。
OS也要對(duì)共享內(nèi)存進(jìn)行管理—>先描述,在組織。
申請(qǐng)一塊空間—>共享內(nèi)存=物理內(nèi)存塊+共享內(nèi)存的相關(guān)屬性
key是什么不重要,能進(jìn)行唯一標(biāo)識(shí)最重要。
創(chuàng)建共享內(nèi)存的時(shí)候,key能保證共享內(nèi)存在系統(tǒng)中的唯一性。兩個(gè)進(jìn)程如何看到同一份資源,只要另一個(gè)進(jìn)程也看到同一個(gè)key。
key在那?
key在共享內(nèi)存的相關(guān)屬性這個(gè)結(jié)構(gòu)體里。
創(chuàng)建共享內(nèi)存把key傳到shmget,本質(zhì)上是把key設(shè)置進(jìn)創(chuàng)建好的共享內(nèi)存的某個(gè)屬性里,另一個(gè)進(jìn)程獲取時(shí),查這么多的共享內(nèi)存,不是查共享內(nèi)的物理內(nèi)存塊,而是去遍歷共享內(nèi)存對(duì)于的相關(guān)屬性去查找key。
key傳到shmget,設(shè)置到共享內(nèi)存屬性中,用來(lái)表示共享內(nèi)存在內(nèi)核中的唯一性。
返回值返回的共享內(nèi)存的標(biāo)識(shí)符取名shmid。
shmid vs key的關(guān)系
shmid是為了用戶(hù)去訪問(wèn)共享內(nèi)存的。就像fd vs inode的關(guān)系。
2.共享內(nèi)存和進(jìn)程關(guān)聯(lián)
shmid:和哪一個(gè)共享內(nèi)存關(guān)聯(lián)
shmaddr:把共享內(nèi)存映射到地址空間的那一塊區(qū)域
shmflg:與讀寫(xiě)權(quán)限有關(guān),默認(rèn)設(shè)置為0
成功是返回的是對(duì)應(yīng)進(jìn)程地址空間的起始地址,失敗返回-1。
3.刪除共享內(nèi)存之前要先去關(guān)聯(lián)
將共享內(nèi)存從當(dāng)前調(diào)用這個(gè)函數(shù)的進(jìn)程地址空間進(jìn)行卸裝
shmaddr:進(jìn)程地址空間的首地址
4.刪除共享內(nèi)存
刪除共享內(nèi)存接口是shmctl,本質(zhì)上控制共享內(nèi)存,不過(guò)常用的是刪除選項(xiàng)。
shmid:控制哪一個(gè)共享內(nèi)存
cmd:做什么控制,常用選項(xiàng)IPC_RMID
buf:如果不想獲得共享內(nèi)存的屬性可以設(shè)置nullptr,不然就傳一個(gè)對(duì)象接收共享內(nèi)存部分屬性信息。
1.4實(shí)操
comm.hpp
將寫(xiě)端和讀端用的代碼封裝起來(lái)。
#include<iostream>
#include<cerrno>
#include<cstring>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define PATHNAME "."
#define PROJ_JD "0x11"
#define MAX_SIZE 4096
key_t GetKey()
{
key_t k=ftok(PATHNAME,PROJ_JD);//獲得唯一標(biāo)識(shí)key
if(k < 0)
{
//cin cout cerr->stdin stdout stderr(默認(rèn)打開(kāi)的三個(gè)文件)-(fd)>0,1,2->鍵盤(pán),顯示器,顯示器
cerr<<errno<<":"<<strerror(errno)<<endl;
exit(1);
}
return k;
}
//獲得共享內(nèi)存表示符shmid
int getShmHelper(int key,int shmflg)
{
int shmid=shemget(key,MAX_SIZE,shmflg);
if(shmid < 0)
{
cerr<<errno<<":"<<strerror(errno)<<endl;
exit(1);
}
return shmid;
}
//寫(xiě)端創(chuàng)建共享內(nèi)存
int CreateShm(key_t key)
{
//這里運(yùn)行時(shí)會(huì)報(bào)錯(cuò)。下面再看運(yùn)行結(jié)果有解決方法
return getShmHelper(key,IPC_CREAT|IPC_EXCL);
}
//讀端獲取共享內(nèi)存
int GetShm()
{
return getShmHelper(key,IPC_CREAT);
}
看運(yùn)行結(jié)果寫(xiě)端和讀端的key是一樣的,shmid也是一樣的。
當(dāng)我再次執(zhí)行一樣的操作,發(fā)現(xiàn)不能再創(chuàng)建共享內(nèi)存了。顯示已經(jīng)存在了。可是我已經(jīng)退出進(jìn)程了啊。OS不會(huì)幫我自動(dòng)關(guān)閉嗎。
共享內(nèi)存的生命周期是隨操作系統(tǒng)的,不是隨進(jìn)程的
查看IPC資源
ipcs查看IPC資源
ipcs -m 查看共享內(nèi)存
ipcs -q 查看隊(duì)列
ipcs -s 查信號(hào)量
ipcrm -m shmid 刪除共享內(nèi)存
代碼刪除共享內(nèi)存
//刪除共享內(nèi)存
void DelShm(int shmid)
{
if(shmctl(shmid,IPC_RMID,nullptr) == -1)
{
cerr<<errno<<":"<<strerror(errno)<<endl;
}
}
通信之前需要關(guān)聯(lián)(掛接:將共享內(nèi)存經(jīng)頁(yè)表映射到進(jìn)程地址空間)
void* attachShm(int shimid)
{
void* mem=shmat(shimid,nullptr,0);//linux 64位機(jī)器指針大小位9
if((long long)men == -1L)//1L代表是長(zhǎng)整型
{
cerr<<errno<<":"<<strerror(errno)<<endl;
exit(1);
}
return men;
}
刪除共享內(nèi)存之前需要去關(guān)聯(lián)
void detachShm(const void* adder)
{
if(shmdt(adder) == -1)
{
cerr<<errno<<":"<<strerror(errno)<<endl;
exit(1);
}
}
完整代碼如下
#include<iostream>
#include<cerrno>
#include<cstring>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
using namespace std;
#define PATHNAME "."
#define PROJ_JD 0x11
#define MAX_SIZE 4096
key_t GetKey()
{
key_t k=ftok(PATHNAME,PROJ_JD);
if(k < 0)
{
//cin cout cerr->stdin stdout stderr(默認(rèn)打開(kāi)的三個(gè)文件)-(fd)>0,1,2->鍵盤(pán),顯示器,顯示器
cerr<<errno<<":"<<strerror(errno)<<endl;
exit(1);
}
return k;
}
//獲得共享內(nèi)存表示符shmid
int getShmHelper(int key,int flage)
{
int shmid=shmget(key,MAX_SIZE,flage);
if(shmid < 0)
{
cerr<<errno<<":"<<strerror(errno)<<endl;
exit(1);
}
return shmid;
}
//寫(xiě)端創(chuàng)建共享內(nèi)存
int CreateShm(key_t key)
{
//這里運(yùn)行時(shí)會(huì)報(bào)錯(cuò)。下面再看運(yùn)行結(jié)果有解決方法
return getShmHelper(key,IPC_CREAT|IPC_EXCL);
}
//讀端獲取共享內(nèi)存
int GetShm(key_t key)
{
return getShmHelper(key,IPC_CREAT);
}
//關(guān)聯(lián)
void* attachShm(int shimid)
{
void* mem=shmat(shimid,nullptr,0);//linux 64位機(jī)器指針大小位9
if((long long)mem == -1L)//1L代表是長(zhǎng)整型
{
cerr<<errno<<":"<<strerror(errno)<<endl;
exit(1);
}
return mem;
}
//去關(guān)聯(lián)
void detachShm(const void* adder)
{
if(shmdt(adder) == -1)
{
cerr<<errno<<":"<<strerror(errno)<<endl;
exit(1);
}
}
//刪除共享內(nèi)存
void DelShm(int shmid)
{
if(shmctl(shmid,IPC_RMID,nullptr) == -1)
{
cerr<<errno<<":"<<strerror(errno)<<endl;
}
}
service.cc (寫(xiě))
#include"comm.hpp"
int main()
{
key_t key=GetKey();
printf("key->%d\n",key);
int shmid=CreateShm(key);
printf("shimid->%d\n",shmid);
//關(guān)聯(lián)
char*start=(char*)attachShm(shmid);//我想將這個(gè)地址認(rèn)為是一字符串;
printf("attach success, address start: %p\n", start);
//寫(xiě)
while(true)
{
}
//刪除
DelShm(shmid);
return 0;
}
共享內(nèi)存沒(méi)有read和write這樣的接口。
寫(xiě)—>直接把內(nèi)容寫(xiě)到這塊內(nèi)存
讀—>直接打印這塊內(nèi)存
以往我們可能是這樣的寫(xiě)法,對(duì)于往內(nèi)存中寫(xiě)有點(diǎn)麻煩了。
//寫(xiě)
char buffer[1024];
const char* messge="hello clint,我是另一個(gè)進(jìn)程,我正在和你通信";
int id=getpid();
int cnt=0;
while(true)
{
//以往我們的做法
snprintf(buffer,sizeof buffer,"%s([%d]->[%d])",messge,cnt++,id);
memcpy(start,buffer,strlen(buffer)+1);
}
看這個(gè)函數(shù),我們可以直接把內(nèi)容寫(xiě)到內(nèi)存。
完整代碼如下
#include"comm.hpp"
int main()
{
key_t key=GetKey();
printf("key->%d\n",key);
int shmid=CreateShm(key);
printf("shimid->%d\n",shmid);
//關(guān)聯(lián)
char* start=(char*)attachShm(shmid);//我想將這個(gè)地址認(rèn)為是一字符串;
printf("attach success, address start: %p\n", start);
//寫(xiě)
char buffer[1024];
const char* messge="hello clint,我是另一個(gè)進(jìn)程,我正在和你通信";
int id=getpid();
int cnt=0;
while(true)
{
snprintf(start,MAX_SIZE,"%s([%d]->[%d])",messge,cnt++,id);
//以往我們的做法
// snprintf(buffer,sizeof buffer,"%s([%d]->[%d])",messge,cnt++,id));
// memcpy(start,buffer,strlen(buffer)+1);
}
//去關(guān)聯(lián)
detachShm(start);
//刪除
DelShm(shmid);
return 0;
}
clint.cc (讀)
#include"comm.hpp"
int main()
{
key_t key=GetKey();
printf("key->%d\n",key);
int shmid=GetShm(key);
printf("shimid->%d\n",shmid);
char* start=(char*)attachShm(shmid);
printf("attach success, address start: %p\n", start);
while(true)
{
printf("service say : %s\n", start);
}
return 0;
}
運(yùn)行時(shí)報(bào)了這個(gè)沒(méi)有權(quán)限的錯(cuò)誤。
這是因?yàn)樵賱?chuàng)建共享內(nèi)存的時(shí)候沒(méi)有給權(quán)限。
補(bǔ)上權(quán)限即可
看運(yùn)行結(jié)果,雖然兩個(gè)進(jìn)程進(jìn)行了通信,但是這個(gè)通信有點(diǎn)問(wèn)題。
共享內(nèi)存不像管道那樣,阻塞等待,而是一直在讀。
1.5共享內(nèi)存的總結(jié)
共享內(nèi)存的特點(diǎn):
1.共享內(nèi)存的生命周期是隨OS的,不是隨進(jìn)程的。
2.所有進(jìn)程間通信速度最快的(能大大的減少數(shù)據(jù)的拷貝次數(shù))—>優(yōu)點(diǎn)。
同樣的代碼,如果用管道來(lái)實(shí)現(xiàn),綜合考慮管道和共享內(nèi)存,考慮鍵盤(pán)輸入和顯示器輸出,共享內(nèi)存有幾次數(shù)據(jù)拷貝?管道呢?
3.不給我進(jìn)行同步和互斥的操作,沒(méi)有對(duì)數(shù)據(jù)做任何保護(hù)---->缺點(diǎn)
1.6共享內(nèi)存的內(nèi)核結(jié)構(gòu)
給這個(gè)接口傳一個(gè)struct shmid_de 對(duì)象,就看到共享內(nèi)存一些信息。
#include"comm.hpp"
int main()
{
key_t key=GetKey();
printf("key->%d\n",key);
int shmid=GetShm(key);
printf("shimid->%d\n",shmid);
char* start=(char*)attachShm(shmid);
printf("attach success, address start: %p\n", start);
while(true)
{
printf("service say : %s\n", start);
struct shmid_ds ds;
shmctl(shmid,IPC_STAT,&ds);
printf("獲得屬性: size :%d,link :%d\n",ds.shm_segsz,ds.shm_nattch);
sleep(1);
}
return 0;
}
我們確實(shí)獲得了共享內(nèi)核的一些屬性。
我們的key在struct shmid_ds結(jié)構(gòu)體的第一個(gè)變量里。
#include"comm.hpp"
int main()
{
key_t key=GetKey();
printf("key->%d\n",key);
int shmid=GetShm(key);
printf("shimid->%d\n",shmid);
char* start=(char*)attachShm(shmid);
printf("attach success, address start: %p\n", start);
while(true)
{
printf("service say : %s\n", start);
struct shmid_ds ds;
shmctl(shmid,IPC_STAT,&ds);
printf("獲得屬性: size :%d,link :%d,key :%d\n",\
ds.shm_segsz,ds.shm_nattch,ds.shm_perm.__key);
sleep(1);
}
return 0;
}
共享內(nèi)存大小一般建議4KB(4096)的整數(shù)倍
系統(tǒng)分配共享內(nèi)存是以4KB為單倍的!------內(nèi)存劃分內(nèi)存塊的基本單位page
雖然申請(qǐng)大小是4097,但是內(nèi)核給你的實(shí)際是40962,內(nèi)核給你向上取整。
雖然內(nèi)核給的是40962,但是注意內(nèi)核給你的,和你能用的是兩碼事。
2.消息隊(duì)列
在前面說(shuō)過(guò)System V IPC—>聚焦在本地通信,目前已經(jīng)陳舊了,共享內(nèi)存還是值得我們好好學(xué)習(xí)一番,剩下的我們看一看原理和接口就可以了。
2.1原理
進(jìn)程A,B可以相互為讀寫(xiě)段,一端把數(shù)據(jù)放到隊(duì)列里,一端去拿。
那如何保證兩個(gè)進(jìn)程不會(huì)拿到自己的,而去拿對(duì)方的呢。
這個(gè)隊(duì)列內(nèi)核數(shù)據(jù)結(jié)構(gòu)中有一個(gè)type,用來(lái)標(biāo)識(shí),這個(gè)信息是誰(shuí)發(fā)的,不是自己的不拿。
2.2接口
創(chuàng)建一個(gè)消息隊(duì)列
看到?jīng)]這些參數(shù)和共享內(nèi)存的參數(shù)非常相似。
成功是返回消息隊(duì)列標(biāo)識(shí)符,失敗返回-1。
向消息隊(duì)列中放數(shù)據(jù)
msgp:發(fā)送的數(shù)據(jù)
msgsz:數(shù)據(jù)大小
msgflg:默認(rèn)為0
接收數(shù)據(jù)
msgp:接收數(shù)據(jù)放到這里
msgsz:大小
msgtyp:類(lèi)型
刪除隊(duì)列
這些接口都和共享內(nèi)核相似。
3.信號(hào)量
關(guān)于信號(hào)量這里補(bǔ)充一些概念,也是為了后面的學(xué)習(xí)。
3.1信號(hào)量是什么
本質(zhì)上是一個(gè)計(jì)數(shù)器,通常用來(lái)表示公共資源中,資源數(shù)量的多少問(wèn)題。
信號(hào)量本質(zhì)是一個(gè)計(jì)數(shù)器,那可以直接設(shè)置一個(gè)全局變量用來(lái)充當(dāng)計(jì)數(shù)器嗎?
不可以的,就如在匿名管道,設(shè)置一個(gè)全局變量,因?yàn)閷?xiě)時(shí)拷貝,父子進(jìn)程看到的根本不是同一個(gè)全局變量。
進(jìn)程通信之前要看到同一份資源,然后才能通信。
公共資源:被多個(gè)進(jìn)程同時(shí)可以訪問(wèn)的資源。
以管道的方式通信的雙方,注意到寫(xiě)端沒(méi)寫(xiě),讀端一直在阻塞等待,讀端沒(méi)讀,寫(xiě)端寫(xiě)完也在阻塞等待等等,而共享內(nèi)存的方式呢,不管寫(xiě)端是否寫(xiě)完,讀端一直在讀。假設(shè)寫(xiě)一段數(shù)據(jù),結(jié)果數(shù)據(jù)沒(méi)寫(xiě)完就讀。就造成了問(wèn)題,這些數(shù)據(jù)就在共享內(nèi)存中沒(méi)有被保護(hù)。
訪問(wèn)沒(méi)有被保護(hù)的公共資源導(dǎo)致數(shù)據(jù)不一致的問(wèn)題。
我們來(lái)捋一捋這個(gè)過(guò)程。
為什么要讓不同的進(jìn)程看到同一份資源呢?
因?yàn)槲蚁胪ㄐ?,讓進(jìn)程之間實(shí)現(xiàn)協(xié)同,但是進(jìn)程具有獨(dú)立性,因此需要先讓進(jìn)程看到同一份資源。 提出了方法,然后也引入了新的問(wèn)題------>數(shù)據(jù)不一致問(wèn)題。
我們未來(lái)將被保護(hù)起來(lái)的公共資源:臨界資源
進(jìn)程大部分的資源是獨(dú)立的。
資源(內(nèi)存,文件,網(wǎng)絡(luò))是要被使用的,如何被進(jìn)程使用呢?
一定是該進(jìn)程有對(duì)應(yīng)的代碼來(lái)訪問(wèn)這部分臨界資源,這個(gè)代碼臨界區(qū),其他代碼叫非臨界區(qū)。
那如何保護(hù)公共資源呢?
互斥和同步
互斥:當(dāng)有兩個(gè)進(jìn)程想訪問(wèn)一個(gè)公共資源的時(shí)候,我們只能讓一個(gè)進(jìn)程進(jìn)行完整的訪問(wèn)。
同步,在多線程哪里再說(shuō)。
還有一組概念比較不好理解。
假如我想向緩沖區(qū)寫(xiě)一段數(shù)據(jù),要求我必須要把這段數(shù)據(jù)寫(xiě)完你才能讀,我沒(méi)寫(xiě)完你就讀不了。對(duì)于我來(lái)講,我在寫(xiě)的時(shí)候,我要么不寫(xiě),要寫(xiě)就寫(xiě)完才對(duì)你有意義。
這種要么不做,要做就做完,兩態(tài)的這種情況:原子性。
假設(shè)張三要去銀行給李四轉(zhuǎn)賬200
張三1000 李四1000
1000-200 1000+200
但是網(wǎng)絡(luò)出現(xiàn)問(wèn)題,轉(zhuǎn)賬失敗,那銀行不能就不管了,必須要把這200還給張三保持原樣。
轉(zhuǎn)賬對(duì)于我們來(lái)說(shuō)就只有兩態(tài),要么不轉(zhuǎn),要轉(zhuǎn)就轉(zhuǎn)完,雖然可能有中間過(guò)程,但是最終結(jié)果就是要么不轉(zhuǎn),要轉(zhuǎn)就轉(zhuǎn)完。不會(huì)說(shuō)正在轉(zhuǎn)賬中。
這就是我們所有的原子性。
上面說(shuō)這么多主要是為了說(shuō)明,讓多進(jìn)程和多線程用來(lái)進(jìn)行原子性的互斥和同步,信號(hào)量是其中一種解決方案。
3.2為什么要信號(hào)量
舉個(gè)例子
去電影票看電影,我是不是只要坐到座位上,這個(gè)位置才是屬于我的?
并不是,買(mǎi)票(票號(hào),座位號(hào))之后,這個(gè)位置在那個(gè)時(shí)間段就是屬于我的。
看電影買(mǎi)票的本質(zhì):對(duì)放映廳中座位進(jìn)行進(jìn)行預(yù)定機(jī)制。
當(dāng)我們想要某種資源的時(shí)候,我們可以進(jìn)行預(yù)定。
電影院就相當(dāng)于共享資源。
每個(gè)進(jìn)程想訪問(wèn)某些公共資源時(shí),先申請(qǐng)信號(hào)量,申請(qǐng)成功就相當(dāng)于預(yù)定了共享資源的某一部分資源,才能允許進(jìn)入這個(gè)小資源里進(jìn)行訪問(wèn),申請(qǐng)失敗,就不允許這個(gè)基礎(chǔ)進(jìn)入共享資源,進(jìn)而達(dá)到保護(hù)共享資源以及其他基礎(chǔ)的目的。
所有進(jìn)程在訪問(wèn)公共資源之前,都必須先申請(qǐng)sem信號(hào)量---->必須申請(qǐng)sem信號(hào)量的前提,是所有進(jìn)程必須先看到同一個(gè)信號(hào)量---->信號(hào)量本身就是公共資源---->
信號(hào)量是不是也要保護(hù)自己的安全呢?(++,- - 操作)---->信號(hào)量必須要保護(hù)這種操作的安全性,++,- -操作是原子的!?。?/p>
++,- -就是我們所說(shuō)的PV操作
信號(hào)量本質(zhì)是一把計(jì)數(shù)器,這個(gè)計(jì)數(shù)器可以在多進(jìn)程環(huán)境中,可以被多進(jìn)程先看到,必須給我們匹配上兩種操作,P操作,V操作,讓進(jìn)程對(duì)我們的共享資源進(jìn)行訪問(wèn)。
如果信號(hào)量初始值是1,代表共享資源當(dāng)作整體來(lái)使用,一個(gè)進(jìn)程申請(qǐng)成功了,其他進(jìn)程不能申請(qǐng)------->互斥。
提供互斥功能的信號(hào)量---->二元信號(hào)量。
這里可能有這樣一個(gè)問(wèn)題,進(jìn)程申請(qǐng)成功了信號(hào)量,是有資格去訪問(wèn)共享資源,但具體是去訪問(wèn)那個(gè)資源子部分不知道,可能存在多個(gè)進(jìn)程訪問(wèn)同一個(gè)資源的問(wèn)題,(也就是買(mǎi)票(票號(hào)有了,座位號(hào)還沒(méi)有))該怎么辦?
這部分其實(shí)是由我們?cè)趯?xiě)代碼時(shí)來(lái)確認(rèn)不同進(jìn)程去訪問(wèn)那個(gè)資源的。
3.3接口
獲取信號(hào)量
nsems:想申請(qǐng)幾個(gè)信號(hào)量
申請(qǐng)成功返回一個(gè)信號(hào)量集的標(biāo)識(shí)符。失敗返回-1。
刪除信號(hào)量,或者獲取信號(hào)量屬性
semnum:可能你申請(qǐng)很多信號(hào)量,這個(gè)是信號(hào)量對(duì)應(yīng)的下標(biāo),申請(qǐng)一個(gè)信號(hào)量,下標(biāo)為0,申請(qǐng)10個(gè)信號(hào)量,假設(shè)我想對(duì)第10個(gè)信號(hào)量操作,下標(biāo)就是9。
cmd:刪除,或者獲取信號(hào)量相關(guān)屬性。
對(duì)信號(hào)量做PV操作
對(duì)指定信號(hào)量,做對(duì)應(yīng)的操作
struct sembuf結(jié)構(gòu)有三個(gè)變量
sem_num:對(duì)申請(qǐng)的多個(gè)信號(hào)量,哪一個(gè)進(jìn)行操作。
sem_op:一般只有兩種值,一種是1代表++,V操作。一種是-1代表- -,P操作。
sem_flg:默認(rèn)為0
nspos:有幾個(gè)這樣的結(jié)構(gòu)體。
允許對(duì)多個(gè)信號(hào)量同時(shí)進(jìn)行PV操作。
4.IPC資源的組織方式
我們注意到共享內(nèi)存,消息隊(duì)列,和信號(hào)量的接口都非常相似。都有struct …id_ds這樣的結(jié)構(gòu)體,還有對(duì)應(yīng)描述IPC屬性資源的第一個(gè)字段都是一樣的。
這三種共性可以看出來(lái)一個(gè)細(xì)節(jié),這三種都叫做System V標(biāo)準(zhǔn)的進(jìn)程間通信。
所謂的標(biāo)準(zhǔn)就是,大家用的方式,接口設(shè)計(jì),數(shù)據(jù)結(jié)構(gòu)的設(shè)置都必須遵守某種標(biāo)準(zhǔn)。
那OS如何對(duì)管理這些(比如一會(huì)申請(qǐng)共享內(nèi)存,一會(huì)消息隊(duì)列,信號(hào)量)的呢?
OS并不是對(duì)它們本身進(jìn)行管理,而是先描述在組織,對(duì)匹配的相關(guān)資源的內(nèi)核結(jié)構(gòu)進(jìn)行管理。
因?yàn)楫?dāng)前三種方式的屬性的第一個(gè)字段都是一樣的。
在內(nèi)核中操作系統(tǒng)可以維護(hù)一個(gè)指針數(shù)組。
結(jié)構(gòu)體第一個(gè)成員的地址,在數(shù)字上,和結(jié)構(gòu)體對(duì)象本身的地址數(shù)字是相等的!!
OS有對(duì)應(yīng)的方式知道強(qiáng)轉(zhuǎn)成什么結(jié)構(gòu)體指針。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-752978.html
這就是OS對(duì)IPC資源的組織方式。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-752978.html
到了這里,關(guān)于【linux】進(jìn)行間通信——共享內(nèi)存+消息隊(duì)列+信號(hào)量的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!