【Linux學習筆記】進程間通信之共享內存
🔥個人主頁:大白的編程日記
🔥專欄:Linux學習筆記
文章目錄
- 【Linux學習筆記】進程間通信之共享內存
- 前言
- 一. system V共享內存
- 1.1 共享內存數據結構
- 1.2 共享內存函數
- 1.3 共享內存實現通信
- 1.4 借助管道實現訪問控制版的共享內存
- 后言
前言
哈嘍,各位小伙伴大家好!上期我們講了進程間通信之管道 今天我們講的是進程間通信之共享內存。話不多說,我們進入正題!向大廠沖鋒!
一. system V共享內存
共享內存區是最快的IPC形式。?旦這樣的內存映射到共享它的進程的地址空間,這些進程間數據傳遞不再涉及到內核,換句話說是進程不再通過執?進?內核的系統調?來傳遞彼此的數據
共享內存示意圖
1.1 共享內存數據結構
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment
(bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current
attaches */
unsigned short shm_unused; /* compatibility */
void shm_unused2; / ditto - used by
DIPC */
void shm_unused3; / unused */
};
1.2 共享內存函數
shmget函數
功能:?來創建共享內存
原型
int shmget(key_t key, size_t size, int shmflg);
參數
key:這個共享內存段名字
size:共享內存??
shmflg:由九個權限標志構成,它們的?法和創建?件時使?的mode模式標志是?樣的
取值為IPC_CREAT:共享內存不存在,創建并返回;共享內存已存在,獲取并返回。
取值為IPC_CREAT | IPC_EXCL:共享內存不存在,創建并返回;共享內存已存在,出
錯返回。
返回值:成功返回?個?負整數,即該共享內存段的標識碼;失敗返回-1
shmat函數
功能:將共享內存段連接到進程地址空間
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
參數
shmid: 共享內存標識
shmaddr:指定連接的地址
shmflg:它的兩個可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回?個指針,指向共享內存第?個節;失敗返回-1
說明:
shmaddr為NULL,核??動選擇?個地址
shmaddr不為NULL且shmflg?SHM_RND標記,則以shmaddr為連接地址。
shmaddr不為NULL且shmflg設置了SHM_RND標記,則連接的地址會?動向下調整為SHMLBA的整數倍。
公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表?連接操作?來只讀共享內存
shmdt函數
功能:將共享內存段與當前進程脫離
原型
int shmdt(const void *shmaddr);
參數
shmaddr: 由shmat所返回的指針
返回值:成功返回0;失敗返回-1
注意:將共享內存段與當前進程脫離不等于刪除共享內存段
shmctl函數
功能:?于控制共享內存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數
shmid:由shmget返回的共享內存標識碼
cmd:將要采取的動作(有三個可取值)
buf:指向?個保存著共享內存的模式狀態和訪問權限的數據結構
返回值:成功返回0;失敗返回-1
1.3 共享內存實現通信
測試代碼結構
# ls
client.c comm.c comm.h Makefile server.c
# cat Makefile
.PHONY:all
all:server client
client:client.c comm.c
gcc -o $@ $^
server:server.c comm.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f client server
1
comm.h
#ifndef _COMM_H_
#define _COMM_H_
# include <stdio.h>
# include <sys/types.h>
# include <sys/ipc.h>
# include <sys/shm.h>
# define PATHNAME "."
# define PROJ_ID 0x6666
int createShm(int size);
int destroyShm(int shmid);
int getShm(int size);
# endif
comm.c
#include "comm.h"
static int commShm(int size, int flags)
{
key_t key = ftok(PATHNAME, PROJ_ID);
if(key < 0){
perror("ftok");
return -1;
}
int shmid = 0;
if( (shmid = shmget(key, size, flags)) < 0){
perror("shmget");
return -2;
}
return shmid;
}
int destroyShm(int shmid)
{
if(shmctl(shmid, IPC_RMID, NULL) < 0){
perror("shmctl");
return -1;
}
return 0;
}
int createShm(int size)
{
return commShm(size, IPC_CREAT|IPC_EXCL|0666);
}
int getShm(int size)
{
return commShm(size, IPC_CREAT);
}
server.c
#include "comm.h"
int main()
{
int shmid = createShm(4096);
char *addr = shmat(shmid, NULL, 0);
sleep(2);
int i = 0;
while(i++<26){
printf("client# %s\n", addr);
sleep(1);
}
shmdt(addr);
sleep(2);
destroyShm(shmid);
return 0;
}
client.c
#include "comm.h"
int main()
{
int shmid = getShm(4096);
sleep(1);
char *addr = shmat(shmid, NULL, 0);
sleep(2);
int i = 0;
while(i<26){
addr[i] = 'A'+i;
i++;
addr[i] = 0;
sleep(1);
}
shmdt(addr);
sleep(2);
return 0;
}
1.4 借助管道實現訪問控制版的共享內存
- Comm.hpp
以下是提取的代碼:```cpp
#pragma once#include <fcntl.h>#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <ctime>
#include <cstring>
#include <iostream>
#include <string>using namespace std;#define Debug 0
#define Notice 1
#define Warning 2
#define Error 3
const std::string msg[] = {"Debug","Notice","Warning","Error"
};std::ostream &Log(std::string message, int level) {std::cout << " | " << (unsigned)time(nullptr) << " | " << msg[level] << " | " << message;return std::cout;
}#define PATH_NAME "/home/hyb"
#define PROJ_ID 0x66
#define SHM_SIZE 4096 // 共享內存的大小,最好是頁(PAGE: 4096)的整數倍
#define FIFO_NAME "./fifo"class Init {
public:Init() {umask(0);int n = mkfifo(FIFO_NAME, 0666);assert(n == 0);(void)n;Log("create fifo success", Notice) << "\n";}~Init() {unlink(FIFO_NAME);Log("remove fifo success", Notice) << "\n";}
};#define READ O_RDONLY
#define WRITE O_WRONLY
int OpenFIFO(std::string pathname, int flags) {int fd = open(pathname.c_str(), flags);assert(fd >= 0);return fd;
}void CloseFifo(int fd) {close(fd);
}void Wait(int fd) {Log("等待中....", Notice) << "\n";uint32_t temp = 0;ssize_t s = read(fd, &temp, sizeof(uint32_t));assert(s == sizeof(uint32_t));(void)s;
}void Signal(int fd) {uint32_t temp = 1;ssize_t s = write(fd, &temp, sizeof(uint32_t));assert(s == sizeof(uint32_t));(void)s;Log("喚醒中....", Notice) << "\n";
}string TransToHex(key_t k) {char buffer[32];sprintf(buffer, sizeof buffer, "0x%x", k);return buffer;
}
- ShmServer.cc
以下是提取的代碼:```cpp
#include "Comm.hpp"
Init init;int main() {// 1. 創建公共的Key值key_t k = ftok(PATH_NAME, PROJ_ID);assert(k != -1);Log("create key done", Debug) << " server key : " << TransToHex(k) << endl;// 2. 創建共享內存 -- 建議要創建一個全新的共享內存 -- 通信的發起者int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);if (shmid == -1) {perror("shmget");exit(1);}Log("create shm done", Debug) << " shmid : " << shmid << endl;// 3. 將指定的共享內存,掛接到自己的地址空間char *shmaddr = (char*)shmat(shmid, nullptr, 0);Log("attach shm done", Debug) << " << shmid << endl;// 4. 訪問控制int fd = OpenFIFO(FIFO_NAME, O_RDONLY);while (true) {// 阻塞Wait(fd);// 臨界區printf("%s\n", shmaddr);if (strcmp(shmaddr, "quit") == 0)break;}CloseFifo(fd);// 5. 將指定的共享內存,從自己的地址空間中去關聯int n = shmdt(shmaddr);assert(n != -1);(void)n;Log("detach shm done", Debug) << " << shmid << endl;// 6. 刪除共享內存, IPC_RMID即便是有進程和當下的shm掛接,依舊刪除共享內存n = shmctl(shmid, IPC_RMID, nullptr);assert(n != -1);(void)n;Log("delete shm done", Debug) << " << shmid << endl;return 0;
}
- ShmClient.cc
以下是提取的代碼:```cpp
#include "Comm.hpp"int main() {// 1. 創建公共的key值key_t k = ftok(PATH_NAME, PROJ_ID);if (k < 0) {Log("create key failed", Error) << " client key : " << TransToHex(k) << endl;exit(1);}Log("create key done", Debug) << " client key : " << TransToHex(k) << endl;// 2. 獲取共享內存int shmid = shmget(k, SHM_SIZE, 0);if (shmid < 0) {Log("create shm failed", Error) << " client key : " << TransToHex(k) << endl;exit(2);}Log("create shm success", Error) << " client key : " << TransToHex(k) << endl;// 3. 掛接共享內存char* shmaddr = (char*)shmat(shmid, nullptr, 0);if (shmaddr == nullptr) {Log("attach shm failed", Error) << " client key : " << TransToHex(k) << endl;exit(3);}Log("attach shm success", Error) << " client key : " << TransToHex(k) << endl;// 4. 寫int fd = OpenFIFO(FIFO_NAME, O_WRONLY);while (true) {ssize_t s = read(0, shmaddr, SHM_SIZE - 1);if (s > 0) {shmaddr[s - 1] = 0;Signal(fd);if (strcmp(shmaddr, "quit") == 0)break;}}CloseFifo(fd);// 5. 去關聯int n = shmdt(shmaddr);assert(n != -1);Log("detach shm success", Error) << " client key : " << TransToHex(k) << endl;return 0;
}
后言
這就是進程間通信之共享內存。大家自己好好消化!今天就分享到這! 感謝各位的耐心垂閱!咱們下期見!拜拜~