💓博主CSDN主頁:杭電碼農-NEO💓
?
?專欄分類:Linux從入門到精通?
?
🚚代碼倉庫:NEO的學習日記🚚
?
🌹關注我🫵帶你學更多操作系統知識
? 🔝🔝
進程間通信
- 1. 前言
- 2. 共享內存的原理
- 3. 實現共享內存的基本步驟
- 4. 共享內存編碼實現
- 5. 進程互斥相關概念
- 6. 總結
1. 前言
在學習Linux中的程序地址空間時,
善于觀察的同學可能會發現在棧區
和堆區中間有一個共享區,這是用來
干啥的?今天就來揭曉一下!
本章重點:
本篇文章著重介紹進程間通信的一種
方式:共享內存
的概念,接口使用以及
它的底層原理,最后會介紹進程間互斥
的一些基本概念
2. 共享內存的原理
共享內存區是最快的IPC形式。一旦這樣的內存映射到共享它的進程的地址空間,這些進程間數據傳遞不再涉及到內核,換句話說是進程不再通過執行進入內核的系統調用來傳遞彼此的數據
說大白話就是: 共享內存的通信方式就是在物理內存中開辟一份空間,然后將這份空間映射到兩個進程的共享區中,這兩個進程可以直接在自己的地址空間中讀取或寫入數據,不會經過內核,效率很快!
3. 實現共享內存的基本步驟
使用shm系統函數來實現這一功能:
第一步: 獲取key值
要通信的雙方怎樣保證自己看見的是和對方一樣共享內存? -> 通過一個key值來保證,只要兩個進程拿到同一個key值,再用這個key值去創建共享內存,那么它們看見的就是同一份共享內存!
使用ftok函數可以形成一個唯一的key值
函數的參數隨意指定,只要保證通信雙方
傳入的參數一樣,就能備注拿到一樣的key
第二步: 創建/獲取共享內存
通信雙方只需要一方來創建共享內存,另外一方可以直接通過key值獲取到共享內存(畢竟物理內存只需要創建一份),不管是創建還是獲取共享內存使用的都是同一個函數!
第三步: 將共享內存映射到地址空間
當雙方進程都拿到共享內存段的標識碼后,此時需要將這份共享空間從物理地址映射到進程自己內部的地址空間中,方便后續使用!
第四步: 使用完后將共享內存與進程去關聯
值得注意的是,共享內存使用完后和指針一樣需要"釋放",否則會導致內存泄漏問題
第五步: 刪除共享內存
將共享內存與進程去關聯后,并不代表它就被刪除了,共享內存的生命周期是隨內核的,而我們的云服務器是永遠不會關閉的,如果不刪除共享內存,將來能使用的空間會越來越少
使用ipcs -m
指令查看共享內存
使用ipcrm -m shmid
指令刪除共享內存
函數刪除共享內存的方法
4. 共享內存編碼實現
由于雙方進程編寫代碼有很多重復的地方
所以使用一個commom.h文件存放公共內容
commom.h文件中:
#pragma once
#include<iostream>
#include<string>
#include<unistd.h>
#include<cstdio>
#include<sys/shm.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<cassert>
#include<cstring>
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;
#define PATHNAME "/home/kwy" //形成key值的字符串
#define PROJ_ID 0x666 //形成key值的整數
#define SHM_SIZE 4096 //共享內存的大小最好是頁(PAGE : 4096)的整數倍
client端代碼:
#include"common.hpp"
int main()
{//獲取keykey_t k = ftok(PATHNAME, PROJ_ID);if (k < 0){perror("ftok");exit(1);}cout<<"creat key success: "<<k<<endl;// 獲取共享內存int shmid = shmget(k, SHM_SIZE, 0);if(shmid < 0){perror("shmget");exit(2);}cout<<"get shard success: "<<shmid<<endl;//掛接共享內存char *shmaddr = (char *)shmat(shmid, nullptr, 0);if(shmaddr == nullptr){perror("shmat");exit(3);}cout<<"attach success!"<<endl;//將共享內存看作數組,寫入數據char ch ='a';for(;ch<='z';ch++){snprintf(shmaddr,SHM_SIZE-1,"hello server,i am other proc,mypid: %d ,inc: %c",getpid(),ch);sleep(2);}// 去關聯int n = shmdt(shmaddr);assert(n != -1);cout<<"detach success!"<<endl;// client 要不要chmctl刪除呢?不需要!!return 0;
}
server端代碼:
#include"common.hpp"
int main()
{//創建公共的key的值key_t key = ftok(PATHNAME,PROJ_ID);cout<<"server key: "<<TransToHex(key)<<endl;assert(key!=-1);//創建共享內存(全新的共享內存)int shmid = shmget(key,SHM_SIZE,IPC_CREAT | IPC_EXCL | 0666);if(shmid==-1){perror("shmid");exit(1);}cout<<"creat shmid: "<<shmid<<endl;//將創建好的共享內容掛接到虛擬地址char* shmaddr = (char*)shmat(shmid,nullptr,0);//shmat的返回值是void*,與malloc相似要強轉cout<<"attach success!"<<endl;//使用共享內存,將共享內存當作一個大字符串//使用完共享內存后,將指定的內存與進程去關聯int n1 = shmdt(shmaddr);if(n1==-1)perror("shmdt");elsecout<<"detach success!"<<endl;//最后用代碼刪除共享內存int n2 = shmctl(shmid,IPC_RMID,nullptr);assert(n2!=-1);cout<<"delete shared memory: "<<shmid<<endl;return 0;
}
5. 進程互斥相關概念
大家在學習共享內存時有沒有發現一個問題:
假如共享區有一個變量a,client進程在將a進行++操作的同時,server進程將a的值從10改為了100,這里就會出現問題,已經被改為100的a值在client進程執行完操作后,會把在CPU中計算出來的11賦值給a,那么進程server就白修改了,這里顯然是有問題的
沒錯,在這個地方,共享內存被稱為共享資源,共享資源如果被同時訪問可能會出現某些預想不到的問題,所以各個進程不能在同一時間訪問共享資源,這種關系叫做進程互斥
對于進程互斥和共享資源以及臨界區的認識遠遠沒有結束,今天我們只是窺見了冰山一角,在后續的學習中我們還會重點講這一個概念!
6. 總結
進程間通信的方式遠不止管道和共享內存這兩種,還有很經典的消息隊列以及信號量等方式這里并沒有過多講述,因為我們只是學習重點,并不是全部都要學會,但是學有余力的同學還是很有必要區了解一下的