国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Linux從入門到精通】通信 | 共享內(nèi)存(System V)

這篇具有很好參考價(jià)值的文章主要介紹了【Linux從入門到精通】通信 | 共享內(nèi)存(System V)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存?

? 本篇文章接著上篇文章通信 | 管道通信(匿名管道 & 命名管道)進(jìn)行講解。本篇文章的中點(diǎn)內(nèi)容是共享內(nèi)存。

文章目錄

?一、初識(shí)與創(chuàng)建共享內(nèi)存

1、1 什么是共享內(nèi)存

1、2 共享內(nèi)存函數(shù)

1、2、1 創(chuàng)建共享內(nèi)存 shmget

1、2、2 ftok 生成 key

1、2、3 獲取共享內(nèi)存 shmget

1、3 demo 代碼

二、對(duì)共享內(nèi)存進(jìn)行相關(guān)操作?

2、1 查看/刪除 共享內(nèi)存資源

2、2?共享內(nèi)存掛接和訪問

2、2、1 共享內(nèi)存的掛接 shmat()

2、2、2 共享內(nèi)存的訪問

2、3 刪除共享內(nèi)存 shmctl?

三、完整共享內(nèi)存通信 demo 代碼

3、1 Log.hpp 日志

3、2 comm.hpp

3、3 shmClient.cpp

3、4 shmServer.cpp


???♂??作者:@Ggggggtm????♂?

???專欄:Linux從入門到精通? ??

???標(biāo)題:共享內(nèi)存 ??

????寄語:與其忙著訴苦,不如低頭趕路,奮路前行,終將遇到一番好風(fēng)景???

?一、初識(shí)與創(chuàng)建共享內(nèi)存

1、1 什么是共享內(nèi)存

? 我們?cè)谥皩W(xué)管道通信時(shí),是怎么實(shí)現(xiàn)通信的呢?匿名管道通信的方式是子進(jìn)程繼承父進(jìn)程的內(nèi)核數(shù)據(jù)結(jié)構(gòu),使得父子進(jìn)程能夠看到同一塊空間。命名管道信是讓不同進(jìn)程打開同一份文件。我們發(fā)現(xiàn)通信的前提就是讓不同的進(jìn)程看到同一份“資源”。當(dāng)然,共享內(nèi)存也不例外。

? ?每個(gè)進(jìn)程都有自己獨(dú)立的地址空間,所以它們彼此之間不能直接訪問對(duì)方的內(nèi)存。而共享內(nèi)存則提供了一種特殊的內(nèi)存區(qū)域,允許多個(gè)進(jìn)程可以同時(shí)訪問和操作同一塊內(nèi)存。具體如下圖:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存

? 這里再次解釋一下上圖。我們創(chuàng)建共享內(nèi)存的過程:一個(gè)進(jìn)程在內(nèi)核空間申請(qǐng)一個(gè)共享內(nèi)存對(duì)象,讓后通過頁表建立與物理內(nèi)存的映射。讓后另一個(gè)進(jìn)程通過特殊的方法和算法來找到該共享內(nèi)存并且與其建立映射。下面我們會(huì)對(duì)上述過程進(jìn)行詳細(xì)解釋。?

1、2 共享內(nèi)存函數(shù)

1、2、1 創(chuàng)建共享內(nèi)存 shmget

? shmget函數(shù)用于創(chuàng)建一個(gè)新的共享內(nèi)存段或者獲取現(xiàn)有的共享內(nèi)存段的標(biāo)識(shí)符。

函數(shù)原型為:

int shmget(key_t key, size_t size, int shmflg);

參數(shù)說明:

  • key:用于標(biāo)識(shí)共享內(nèi)存段的鍵值??梢?strong>使用ftok函數(shù)生成。
  • size:指定共享內(nèi)存段的大小,以字節(jié)為單位。
  • shmflg:用于指定共享內(nèi)存段的訪問權(quán)限和標(biāo)志位,可以使用IPC_CREAT、IPC_EXCL等宏進(jìn)行設(shè)置。

返回值:

  • 如果成功,返回共享內(nèi)存段的標(biāo)識(shí)符(即共享內(nèi)存ID)。
  • 如果失敗,返回-1,并設(shè)置errno。

? 更加詳細(xì)的如下圖:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存

? 我們?cè)谶@里具體解釋一下第三個(gè)參數(shù) shmflg。這個(gè)參數(shù)是可以有多個(gè)選擇的。底層是利用了位圖的思想。主要是IPC_CREAT和IPC_EXCL兩個(gè)選項(xiàng)。

? IPC_CREAT和IPC_EXCL是在shmget函數(shù)中使用的標(biāo)志位,用于指定共享內(nèi)存段的訪問權(quán)限和標(biāo)志。它們?cè)趕hmget函數(shù)的第三個(gè)參數(shù)shmflg中使用。

  • IPC_CREAT:該標(biāo)志用于創(chuàng)建一個(gè)新的共享內(nèi)存段。如果指定的key對(duì)應(yīng)的共享內(nèi)存段不存在,則創(chuàng)建一個(gè)新的共享內(nèi)存段。如果共享內(nèi)存段已經(jīng)存在,則返回該共享內(nèi)存段的標(biāo)識(shí)符(即共享內(nèi)存ID)。

  • IPC_EXCL:該標(biāo)志與IPC_CREAT一起使用,在創(chuàng)建共享內(nèi)存段時(shí)起作用。如果指定的key對(duì)應(yīng)的共享內(nèi)存段已經(jīng)存在,則shmget函數(shù)會(huì)失敗,并返回-1,并且置errno為EEXIST(資源已存在)。

? ?我們接下來再看一下 shmget 的具體使用例子。

int shmid = shmget(key, size, IPC_CREAT | permission_flags);

? 上述代碼中,IPC_CREAT標(biāo)志位用于創(chuàng)建共享內(nèi)存段。如果指定的key對(duì)應(yīng)的共享內(nèi)存段已經(jīng)存在,那么shmget函數(shù)會(huì)返回該共享內(nèi)存段的標(biāo)識(shí)符;如果共享內(nèi)存段不存在,則會(huì)創(chuàng)建一個(gè)新的共享內(nèi)存段,并返回新創(chuàng)建的共享內(nèi)存段的標(biāo)識(shí)符。

1、2、2 ftok 生成 key

??在函數(shù)shmget中,key值是用于標(biāo)識(shí)或檢索共享內(nèi)存段的關(guān)鍵值。它在創(chuàng)建或訪問共享內(nèi)存時(shí)起到重要作用。具體來說,key值用于以下兩個(gè)目的:

  • 當(dāng)多個(gè)進(jìn)程需要訪問同一個(gè)共享內(nèi)存段時(shí),它們可以使用相同的key值來標(biāo)識(shí)這個(gè)共享內(nèi)存段。
  • 如果一個(gè)共享內(nèi)存段已經(jīng)存在,并且其他進(jìn)程想要訪問它,那么只需要提供相同的key值即可找到該共享內(nèi)存段。

? 那在使用 shmget 函數(shù)之前,我們應(yīng)該使用 ftok 函數(shù)生成key值,來表示這個(gè)共享內(nèi)存段。由于是標(biāo)示共享空間,所以應(yīng)該確定唯一性。至于key的值是多少并不關(guān)鍵。那我們看一下ftok 函數(shù)的用法。具體如下:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存

參數(shù)說明:

  • pathname:一個(gè)包含一個(gè)現(xiàn)有文件的路徑名,用于生成k值。最好是有權(quán)訪問這個(gè)文件。
  • proj_id:不同的proj_id可以被用作區(qū)分不同類型的通信方式或不同的ipc資源,來生成不同的k值。其實(shí)就是一個(gè)任意整型值。

? 我們?cè)倏匆幌缕渚唧w的例子:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>

int main()
{
    key_t key;
    char *path = "./example.txt";
    int proj_id = 0x66;

    // 使用ftok函數(shù)生成k值
    key = ftok(path, proj_id);

    printf("Generated key: %d\n", key);

    return 0;
}

? 另一個(gè)進(jìn)程也可以用相同的方法生成相同的key值。ftok函數(shù)根據(jù)給定的路徑名和proj_id生成k值。當(dāng)路徑名和proj_id相同時(shí),生成的k值也相同。這是因?yàn)閒tok函數(shù)內(nèi)部使用了哈希運(yùn)算,將路徑名和proj_id轉(zhuǎn)化為一個(gè)唯一的整數(shù)。盡管可能存在哈希沖突(即不同的路徑名和proj_id生成相同的k值),但概率非常低。通常情況下,不同的進(jìn)程可以使用相同的路徑名和proj_id生成相同的k值是非常罕見的。即使出現(xiàn)相同的k值,由于進(jìn)程間通信中還有其他參數(shù)的限制(如消息隊(duì)列標(biāo)識(shí)、共享內(nèi)存標(biāo)識(shí)等),不同進(jìn)程之間的IPC通信仍然可以正常進(jìn)行。??

? 生成 key 值后,我們就可以用key值創(chuàng)建共享內(nèi)存,或者來獲取共享內(nèi)存。下面我們看一下獲取使用key值來獲取共享內(nèi)存的方法。

1、2、3 獲取共享內(nèi)存 shmget

? shmget 函數(shù)還可用來獲取共享內(nèi)存。當(dāng)生成的key值已經(jīng)有對(duì)應(yīng)的共享內(nèi)存時(shí),shmget 函數(shù)就會(huì)返回這段共享內(nèi)存的標(biāo)識(shí)碼。我么不只需要將第三個(gè)參數(shù)修改為0,就是來獲取對(duì)應(yīng)key值的共享內(nèi)存。

1、3 demo 代碼

? 我們接下來寫一段代碼測(cè)試和總結(jié)一下我們上面所學(xué)到的函數(shù)。下面為實(shí)例:

// Log.hpp
#include <iostream>
#include <ctime>

#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;
}


// comm.hpp
#pragma once

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cassert>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Log.hpp"

using namespace std; 

#define PATH_NAME "/home/gtm"
#define PROJ_ID 0x66
#define SHM_SIZE 4096 //共享內(nèi)存的大小,最好是頁(PAGE: 4096)的整數(shù)倍

#define FIFO_NAME "./fifo"


// shmClient.cc
#include"comm.hpp"

int main()
{
    Log("child pid is : ", Debug) << getpid() << endl;
    key_t k = ftok(PATH_NAME, PROJ_ID);
    if (k < 0)
    {
        Log("create key failed", Error) << " client key : " << k << endl;
        exit(1);
    }
    Log("create key done", Debug) << " client key : " << k << endl;

    // 獲取共享內(nèi)存
    int shmid = shmget(k, SHM_SIZE, 0);
    if(shmid < 0)
    {
        Log("create shm failed", Error) << " client key : " << k << endl;
        exit(2);
    }
    Log("create shm success", Error) << " client shmid : " << shmid << endl;


    return 0;
}


// shmServer.cc
#include "comm.hpp"

string TransToHex(key_t k)
{
    char buffer[32];
    snprintf(buffer, sizeof buffer, "0x%x", k);
    return buffer;
}

int main()
{
    // 我們之前為了通信,所做的所有的工作,屬于什么工作呢:讓不同的進(jìn)程看到了同一份資源(內(nèi)存)
    // 1. 創(chuàng)建公共的Key值
    key_t k = ftok(PATH_NAME, PROJ_ID);
    assert(k != -1);

    Log("create key done", Debug) << " server key : " << TransToHex(k) << endl;

    // 2. 創(chuàng)建共享內(nèi)存 -- 建議要?jiǎng)?chuàng)建一個(gè)全新的共享內(nèi)存 -- 通信的發(fā)起者
    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;
    
    return 0;
}

? shmServer.cc文件中的代碼是服務(wù)端代碼。首先調(diào)用ftok函數(shù)生成一個(gè)唯一的鍵值,并將其轉(zhuǎn)換為十六進(jìn)制字符串表示。然后使用shmget函數(shù)創(chuàng)建一個(gè)共享內(nèi)存段,創(chuàng)建時(shí)指定了IPC_CREAT標(biāo)志,用于新建共享內(nèi)存段。如果創(chuàng)建成功,返回一個(gè)共享內(nèi)存標(biāo)識(shí)符shmid。

? shmClient.cc文件中的代碼是客戶端代碼。首先也是調(diào)用ftok函數(shù)生成一個(gè)唯一的鍵值。然后通過shmget函數(shù)打開已存在的共享內(nèi)存段,打開時(shí)不需要指定IPC_CREAT標(biāo)志,而是提供即將打開的共享內(nèi)存段的鍵值和大小。如果打開成功,返回一個(gè)共享內(nèi)存標(biāo)識(shí)符shmid。

? Log.hpp文件定義了一個(gè)宏和一個(gè)Log函數(shù)。宏定義了四個(gè)日志級(jí)別,分別對(duì)應(yīng)Debug、Notice、Warning和Error四個(gè)字符串。Log函數(shù)負(fù)責(zé)輸出日志信息,接受一個(gè)字符串信息和一個(gè)日志級(jí)別參數(shù)。Log函數(shù)將時(shí)間戳、日志級(jí)別和消息內(nèi)容輸出到標(biāo)準(zhǔn)輸出流中。

? 上述代碼就是完成了創(chuàng)建共享內(nèi)存的功能,并且在其中打印了一些日志信息。我們不妨來看一下運(yùn)行結(jié)果。具體如下圖:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存

? 我們發(fā)現(xiàn)對(duì)應(yīng)的shmid是相同的,說明Server和Client確實(shí)獲得了相同的共享內(nèi)存塊。他們所生成的key值相同嗎?其實(shí)是相同的,如下圖:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存

二、對(duì)共享內(nèi)存進(jìn)行相關(guān)操作?

2、1 查看/刪除 共享內(nèi)存資源

? 當(dāng)我們?cè)俅芜\(yùn)行時(shí),就會(huì)發(fā)生錯(cuò)誤。具體如下圖:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存

? 為什么呢?原因是我們剛剛創(chuàng)建的共享內(nèi)存依然存在。當(dāng)進(jìn)程結(jié)束時(shí),共享內(nèi)存并不會(huì)自動(dòng)釋放。為什么呢?我們可以認(rèn)為共享內(nèi)存是屬于操作系統(tǒng)。所以共享內(nèi)存的生命周期隨操作系統(tǒng)!這時(shí)我們可以手動(dòng)關(guān)閉共享內(nèi)存。在關(guān)閉前首先要查看共享內(nèi)存,指令:ipcs -m。具體如下圖:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存

? 當(dāng)然,查到共享內(nèi)存后,可以用指令進(jìn)行刪除。那么問題來了,使用key值刪除呢?還是用shmid 進(jìn)行刪除呢?我們這里需要注意:共享內(nèi)存中的shmid和key值是兩個(gè)不同的概念。

  1. shmid(Shared Memory ID)是共享內(nèi)存的標(biāo)識(shí)符,由操作系統(tǒng)分配,并作為一個(gè)非負(fù)整數(shù)對(duì)共享內(nèi)存進(jìn)行引用。在使用共享內(nèi)存時(shí),我們需要通過shmid來進(jìn)行操作,如創(chuàng)建、附加、訪問和刪除等。shmid可以看作是內(nèi)核用于標(biāo)識(shí)某個(gè)特定共享內(nèi)存段的一個(gè)唯一值

  2. key值是用戶定義的一個(gè)標(biāo)識(shí)符,通常是一個(gè)整數(shù)值。在創(chuàng)建共享內(nèi)存時(shí),我們可以使用ftok函數(shù)生成一個(gè)key值,以便其他進(jìn)程可以通過這個(gè)key值來獲取相同的共享內(nèi)存區(qū)域。key值是用于標(biāo)識(shí)共享內(nèi)存的用戶級(jí)別的標(biāo)識(shí)符,不同于shmid,其值不受內(nèi)核控制。

? 所以刪除共享內(nèi)存,我們使用的shmid。具體指令:ipcrm -m shmid。如下圖:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存

? 當(dāng)我們刪除共享內(nèi)存后,再次進(jìn)行查找發(fā)現(xiàn)就沒有了,且程序能夠正常運(yùn)行。

2、2?共享內(nèi)存掛接和訪問

2、2、1 共享內(nèi)存的掛接 shmat()

??在調(diào)用shmget()函數(shù)時(shí),內(nèi)核會(huì)在內(nèi)部維護(hù)一個(gè)共享內(nèi)存表格,其中包含了共享內(nèi)存的相關(guān)信息,包括共享內(nèi)存的大小、權(quán)限等。當(dāng)調(diào)用成功后,將返回一個(gè)唯一的共享內(nèi)存標(biāo)識(shí)符,該標(biāo)識(shí)符可以用于后續(xù)的共享內(nèi)存操作。

? 那么正常來說,我們?cè)L問共享內(nèi)存是需要通過系統(tǒng)調(diào)用的。但是我們這里可以將內(nèi)核級(jí)別的共享內(nèi)存掛接到進(jìn)程的地址空間。然后用戶就可以直接進(jìn)行訪問。

? 進(jìn)程可以使用系統(tǒng)提供的函數(shù)(如shmat())將自己的地址空間映射到共享內(nèi)存。也可以理解為shmat()函數(shù)將共享內(nèi)存附加到進(jìn)程的虛擬地址空間中,使得進(jìn)程可以訪問該共享內(nèi)存所指向的物理內(nèi)存區(qū)域。具體用法如下:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存

? 參數(shù)說明:

  • shm_id:共享內(nèi)存標(biāo)識(shí)符,通過調(diào)用 shmget 獲取。
  • shm_addr:內(nèi)存段的地址,通常傳入?NULL,表示由系統(tǒng)自動(dòng)選擇一個(gè)適合的地址。
  • shmflg:控制共享內(nèi)存段的附加方式和權(quán)限,可以使用 IPC_CREAT 標(biāo)志創(chuàng)建新的共享內(nèi)存段。通常傳入0。

? 返回值:

  • 如果成功,返回指向共享內(nèi)存段第一個(gè)字節(jié)的指針;
  • 如果失敗,返回?void *?類型的錯(cuò)誤值?-1。

? 其實(shí)我們看完其使用方法后,有沒有發(fā)現(xiàn)與 malloc 很相似。malloc 申請(qǐng)空間成功后,會(huì)返回所申請(qǐng)空間的起始地址。否則就會(huì)返回NULL。shmat 與其確實(shí)有些相似。我們可結(jié)合如下例子一起理解:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>

int main() {
    int shm_id;
    key_t key;
    int *shared_memory;

    // 獲取共享內(nèi)存標(biāo)識(shí)符
    key = ftok("。/file", 0x66);
    shm_id = shmget(key, sizeof(int), IPC_CREAT | 0666);
    
    // 將共享內(nèi)存段附加到進(jìn)程的地址空間中
    shared_memory = shmat(shm_id, NULL, 0);
    
    // 訪問共享內(nèi)存
    printf("共享內(nèi)存中的值為:%d\n", *shared_memory);
    
    // 分離共享內(nèi)存段
    shmdt(shared_memory);
    
    return 0;
}

? 我們也看到了最后是有一個(gè)去關(guān)聯(lián)的 shmdt 函數(shù)。參數(shù)就是我們所獲取的共享內(nèi)存的起始地址,這里就不再過多解釋此函數(shù)。

? 這里有會(huì)有一個(gè)問題:將內(nèi)核級(jí)別的共享內(nèi)存掛接到進(jìn)程地址空間的哪里了呢?我們看如下圖:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存? ?我們之前學(xué)進(jìn)程地址空間時(shí),知道堆和棧的中間有大量的鏤空,而這段位置就是內(nèi)核級(jí)別的共享內(nèi)存所掛接到的位置!

2、2、2 共享內(nèi)存的訪問

? 到這里,我們已經(jīng)學(xué)習(xí)了共享內(nèi)存的大部分內(nèi)容。只差對(duì)共享內(nèi)存的訪問了。當(dāng)我們對(duì)共享內(nèi)存進(jìn)行掛接后,?就可以得到共享內(nèi)存掛接后的起始地址。我們用戶可以對(duì)其進(jìn)行直接訪問(寫入/讀?。?。我們給出如下偽代碼:

    // shmServer.cpp
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    Log("attach shm done", Debug) << " shmid : " << shmid << endl;

    for(;;)
    {
        printf("%s\n", shmaddr);
        if(strcmp(shmaddr, "quit") == 0) break;
        sleep(1);
    }

    // shmClient.cpp  
    // 掛接并獲得共享內(nèi)存起始地址
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    if(shmaddr == nullptr)
    {
        Log("attach shm failed", Error) << " client key : " << k << endl;
        exit(3);
    }

    char a = 'a';
    for(; a <= 'c'; a++)
    {
        // 我們是每一次都向shmaddr[共享內(nèi)存的起始地址]寫入
        snprintf(shmaddr, SHM_SIZE - 1,\
            "hello server, 我是其他進(jìn)程,我的pid: %d, inc: %c\n",\
            getpid(), a);
        sleep(3);
    }

? 對(duì)上述代碼是一個(gè)使用共享內(nèi)存進(jìn)行進(jìn)程間通信的偽代碼。下面對(duì)代碼進(jìn)行詳解:

  1. 首先,在服務(wù)端(shmServer.cpp)中,通過shmat函數(shù)將共享內(nèi)存連接到當(dāng)前進(jìn)程的地址空間。shmat函數(shù)的第一個(gè)參數(shù)是共享內(nèi)存的標(biāo)識(shí)符shmid,第二個(gè)參數(shù)為NULL表示讓系統(tǒng)自動(dòng)選擇合適的地址分配給共享內(nèi)存,第三個(gè)參數(shù)為0表示以默認(rèn)權(quán)限進(jìn)行操作。連接完成后,返回共享內(nèi)存的起始地址,并賦值給shmaddr指針。

  2. 服務(wù)器端的for循環(huán)中,通過printf函數(shù)將shmaddr指向的共享內(nèi)存內(nèi)容輸出到標(biāo)準(zhǔn)輸出(讀取)。然后通過strcmp函數(shù)判斷共享內(nèi)存中的內(nèi)容是否為"quit",如果是,則跳出循環(huán),結(jié)束程序。否則,通過sleep函數(shù)暫停1秒鐘。

  3. 在客戶端(shmClient.cpp)中,同樣通過shmat函數(shù)連接到共享內(nèi)存,并將共享內(nèi)存的起始地址賦給shmaddr指針。若連接失?。╯hmaddr為nullptr),則輸出錯(cuò)誤信息并退出程序。

  4. 客戶端的for循環(huán)中,使用snprintf函數(shù)將格式化的字符串寫入shmaddr指向的共享內(nèi)存中(寫入)。該字符串包含了客戶端進(jìn)程的PID(進(jìn)程標(biāo)識(shí)符)和一個(gè)遞增的字符,以展示多次寫入的內(nèi)容。然后通過sleep函數(shù)暫停3秒鐘。

? 運(yùn)行結(jié)果如下:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存

? 我們通過運(yùn)行結(jié)果發(fā)現(xiàn):在客戶端沒有寫入的情況下,服務(wù)端進(jìn)行讀取時(shí)也會(huì)讀到內(nèi)容。讀到的是空字符串(共享內(nèi)存默認(rèn)會(huì)初始化為0)。我們發(fā)現(xiàn)共享內(nèi)存的讀寫并沒有訪問控制。我們知道命名管道通信是由訪問控制的。但是當(dāng)一個(gè)進(jìn)程寫入時(shí),另一個(gè)就能夠馬上看到寫入的內(nèi)容。所以共享內(nèi)存是所有進(jìn)程間通信(IPC),速度最快的!不需要過多的拷貝?。。ú恍枰獙?shù)據(jù)給操作系統(tǒng))。如果我想一定程度的訪問控制呢?可以在共享內(nèi)存讀寫的過程中加入命名管道來控制。

2、3 刪除共享內(nèi)存 shmctl?

? 上面我們了解了可以使用Linux指令對(duì)共享內(nèi)存進(jìn)行刪除,我們也可以使用系統(tǒng)調(diào)用 shmctl()函數(shù) 對(duì)其進(jìn)行刪除。具體使用如下:

【Linux從入門到精通】通信 | 共享內(nèi)存(System V),Linux從入門到精通,linux,運(yùn)維,通信,共享內(nèi)存?參數(shù)說明:

  • shmid:共享內(nèi)存標(biāo)識(shí)符,通過shmget函數(shù)獲取得到。
  • cmd:表示對(duì)共享內(nèi)存進(jìn)行的操作類型,可以選擇的參數(shù)有:
    • IPC_STAT:獲取共享內(nèi)存的狀態(tài)信息,將共享內(nèi)存的屬性保存在buf所指向的結(jié)構(gòu)體中。
    • IPC_SET:設(shè)置共享內(nèi)存的屬性,使用buf所指向的結(jié)構(gòu)體中的值進(jìn)行設(shè)置。
    • IPC_RMID:刪除共享內(nèi)存。
  • buf:指向一個(gè)struct shmid_ds結(jié)構(gòu)體的指針,用于存儲(chǔ)共享內(nèi)存的屬性信息。通常使用nullptr。

??shmctl函數(shù)可以用于對(duì)共享內(nèi)存段進(jìn)行控制操作。它能夠?qū)崿F(xiàn)共享內(nèi)存的創(chuàng)建、刪除、以及獲取和修改共享內(nèi)存的屬性。但是我們?cè)摵瘮?shù)最常用刪除共享內(nèi)存。使用IPC_RMID操作可以刪除指定的共享內(nèi)存段,并釋放系統(tǒng)資源。這個(gè)操作會(huì)立即刪除共享內(nèi)存段,以及與它關(guān)聯(lián)的任何進(jìn)程中的鍵和id。

三、完整共享內(nèi)存通信 demo 代碼

3、1 Log.hpp 日志

??

#include <iostream>
#include <ctime>

#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;
}

3、2 comm.hpp

#pragma once

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cassert>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Log.hpp"

using namespace std; //不推薦

#define PATH_NAME "/home/whb"
#define PROJ_ID 0x66
#define SHM_SIZE 4096 //共享內(nèi)存的大小,最好是頁(PAGE: 4096)的整數(shù)倍

#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 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";
}

void CloseFifo(int fd)
{
    close(fd);
}

3、3 shmClient.cpp

#include "comm.hpp"

int main()
{
    Log("child pid is : ", Debug) << getpid() << endl;
    key_t k = ftok(PATH_NAME, PROJ_ID);
    if (k < 0)
    {
        Log("create key failed", Error) << " client key : " << k << endl;
        exit(1);
    }
    Log("create key done", Debug) << " client key : " << k << endl;

    // 獲取共享內(nèi)存
    int shmid = shmget(k, SHM_SIZE, 0);
    if(shmid < 0)
    {
        Log("create shm failed", Error) << " client key : " << k << endl;
        exit(2);
    }
    Log("create shm success", Error) << " client key : " << k << endl;

    // sleep(10);

    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    if(shmaddr == nullptr)
    {
        Log("attach shm failed", Error) << " client key : " << k << endl;
        exit(3);
    }
    Log("attach shm success", Error) << " client key : " << k << endl;


    int fd = OpenFIFO(FIFO_NAME, WRITE);

    // client將共享內(nèi)存看做一個(gè)char 類型的buffer
    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);

    // 去關(guān)聯(lián)
    int n = shmdt(shmaddr);
    assert(n != -1);
    Log("detach shm success", Error) << " client key : " << k << endl;


    return 0;
}

3、4 shmServer.cpp

#include "comm.hpp"

Init init; 

string TransToHex(key_t k)
{
    char buffer[32];
    snprintf(buffer, sizeof buffer, "0x%x", k);
    return buffer;
}

int main()
{
    // 1. 創(chuàng)建公共的Key值
    key_t k = ftok(PATH_NAME, PROJ_ID);
    assert(k != -1);

    Log("create key done", Debug) << " server key : " << TransToHex(k) << endl;

    // 2. 創(chuàng)建共享內(nèi)存 -- 建議要?jiǎng)?chuàng)建一個(gè)全新的共享內(nèi)存 -- 通信的發(fā)起者
    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. 將指定的共享內(nèi)存,掛接到自己的地址空間
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    Log("attach shm done", Debug) << " shmid : " << shmid << endl;

    
    int fd = OpenFIFO(FIFO_NAME, READ);
    for(;;)
    {
        Wait(fd);

        // 臨界區(qū)
        printf("%s\n", shmaddr);
        if(strcmp(shmaddr, "quit") == 0) break;
        // sleep(1);
    }
    // 4. 將指定的共享內(nèi)存,從自己的地址空間中去關(guān)聯(lián)
    int n = shmdt(shmaddr);
    assert(n != -1);
    (void)n;
    Log("detach shm done", Debug) << " shmid : " << shmid << endl;


    // 5. 刪除共享內(nèi)存,IPC_RMID即便是有進(jìn)程和當(dāng)下的shm掛接,依舊刪除共享內(nèi)存
    n = shmctl(shmid, IPC_RMID, nullptr);
    assert(n != -1);
    (void)n;
    Log("delete shm done", Debug) << " shmid : " << shmid << endl;

    CloseFifo(fd);
    return 0;
}

? 上述共享內(nèi)存代碼是結(jié)合了命名管道通信進(jìn)行了訪問控制文章來源地址http://www.zghlxwxcb.cn/news/detail-703805.html

到了這里,關(guān)于【Linux從入門到精通】通信 | 共享內(nèi)存(System V)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【探索Linux】—— 強(qiáng)大的命令行工具 P.15(進(jìn)程間通信 —— system V共享內(nèi)存)

    【探索Linux】—— 強(qiáng)大的命令行工具 P.15(進(jìn)程間通信 —— system V共享內(nèi)存)

    在多進(jìn)程編程中,進(jìn)程間通信(IPC)是一項(xiàng)關(guān)鍵技術(shù),它使得不同的進(jìn)程能夠相互交換數(shù)據(jù)和協(xié)調(diào)行為。而在眾多的IPC機(jī)制中,System V共享內(nèi)存是一種高效且強(qiáng)大的通信方式。通過使用共享內(nèi)存,我們可以將數(shù)據(jù)從一個(gè)進(jìn)程快速地傳遞給另一個(gè)進(jìn)程,避免了復(fù)制數(shù)據(jù)的開銷,

    2024年02月05日
    瀏覽(22)
  • 【Linux】system V 共享內(nèi)存

    【Linux】system V 共享內(nèi)存

    system V 是一套標(biāo)準(zhǔn),獨(dú)立于文件系統(tǒng)之外的,專門為了通信設(shè)計(jì)出來的模塊 讓兩個(gè)毫不相關(guān)的進(jìn)程看到同一份資源 第一階段原理 進(jìn)程A和進(jìn)程B都通過自己的頁表映射到物理內(nèi)存中的特定區(qū)域,進(jìn)而找到該進(jìn)程匹配的代碼和數(shù)據(jù) 為了讓進(jìn)程A和進(jìn)程B通信, 前提是兩者要看到同

    2023年04月18日
    瀏覽(27)
  • 【Linux初階】system V - 共享內(nèi)存

    【Linux初階】system V - 共享內(nèi)存

    共享內(nèi)存區(qū)是最快的IPC(進(jìn)程間通信)形式 。一旦這樣的內(nèi)存映射到共享它的進(jìn)程的地址空間,這些進(jìn)程間數(shù)據(jù)傳遞不再涉及到內(nèi)核,換句話說是進(jìn)程不再通過執(zhí)行進(jìn)入內(nèi)核的系統(tǒng)調(diào)用來傳遞彼此的數(shù)據(jù)。 在物理內(nèi)存中 申請(qǐng)一塊空間 ,將創(chuàng)建好的 內(nèi)存映射進(jìn)進(jìn)程的地址空

    2024年02月13日
    瀏覽(23)
  • [Linux打怪升級(jí)之路]-system V共享內(nèi)存

    [Linux打怪升級(jí)之路]-system V共享內(nèi)存

    前言 作者 : 小蝸牛向前沖 名言 : 我可以接受失敗,但我不能接受放棄 ??如果覺的博主的文章還不錯(cuò)的話,還請(qǐng) 點(diǎn)贊,收藏,關(guān)注??支持博主。如果發(fā)現(xiàn)有問題的地方歡迎?大家在評(píng)論區(qū)指正 本期學(xué)習(xí)目標(biāo):認(rèn)識(shí)什么是?system V共享內(nèi)存,認(rèn)識(shí)共享內(nèi)存的接口函數(shù),學(xué)會(huì)

    2024年02月08日
    瀏覽(25)
  • 【Linux】System V 共享內(nèi)存、消息隊(duì)列、信號(hào)量

    【Linux】System V 共享內(nèi)存、消息隊(duì)列、信號(hào)量

    ?? 作者: 阿潤菜菜 ?? 專欄: Linux系統(tǒng)編程 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ù)的不一致性 。 共

    2024年02月04日
    瀏覽(22)
  • 【Linux】進(jìn)程通信 — 共享內(nèi)存

    【Linux】進(jìn)程通信 — 共享內(nèi)存

    上一章我們由進(jìn)程通信,引入并講述了管道,匿名管道和命名管道和匿名管道。本章我們將繼續(xù)講解進(jìn)程通信的另一種方式,通過共享內(nèi)存的方式來進(jìn)行進(jìn)程間的通信。還要學(xué)習(xí)幾個(gè)系統(tǒng)調(diào)用接口,并用代碼實(shí)現(xiàn)兩個(gè)進(jìn)程通過共享內(nèi)存來進(jìn)行通信。目標(biāo)已經(jīng)確定,接下來就要

    2024年02月15日
    瀏覽(21)
  • Linux進(jìn)程通信——共享內(nèi)存

    Linux進(jìn)程通信——共享內(nèi)存

    兩個(gè)進(jìn)程的PCB創(chuàng)建虛擬地址空間然后映射到物理內(nèi)存中,每個(gè)進(jìn)程因?yàn)槭仟?dú)立的,所以在物理內(nèi)存中的地址也不同。 那么共享內(nèi)存是怎么做到的呢? 首先先在物理內(nèi)存中申請(qǐng)一塊內(nèi)存。 然后講這塊內(nèi)存通過頁表映射分別映射到這兩個(gè)進(jìn)程的虛擬地址空間內(nèi),讓這兩個(gè)進(jìn)程都

    2024年02月01日
    瀏覽(24)
  • Linux——進(jìn)程通信之共享內(nèi)存

    Linux——進(jìn)程通信之共享內(nèi)存

    目錄 一.? 回顧上文 二.共享內(nèi)存 1.定義 2.特點(diǎn): 3.實(shí)現(xiàn)步驟: 如下為成功鏈接共享內(nèi)存使用權(quán)的完整步驟: 4.函數(shù)介紹 ????????4.1shmget函數(shù) ????????4.1.2參數(shù)介紹? ? ? ? ? ? ? ? 4.2ftok函數(shù): ????????4.2.1參數(shù)介紹 ????????????????關(guān)于ftok(); shmget();函數(shù)的

    2024年02月12日
    瀏覽(21)
  • system-v IPC共享內(nèi)存通信

    system-v IPC共享內(nèi)存通信

    目錄 system v IPC簡介 共享內(nèi)存需要用到的函數(shù)接口 shmget函數(shù)--獲取對(duì)象ID shmat函數(shù)--獲得映射空間 shmctl函數(shù)--釋放資源 共享內(nèi)存實(shí)現(xiàn)思路 注意 消息隊(duì)列、共享內(nèi)存和信號(hào)量統(tǒng)稱為system v IPC(進(jìn)程間通信機(jī)制),V是羅馬數(shù)字5,是UNIX的ATT分支的其中一個(gè)版本,一般稱它們?yōu)镮PC對(duì)象

    2024年04月26日
    瀏覽(22)
  • (26)Linux 進(jìn)程通信之共享內(nèi)存(共享儲(chǔ)存空間)

    (26)Linux 進(jìn)程通信之共享內(nèi)存(共享儲(chǔ)存空間)

    共享內(nèi)存是System V版本的最后一個(gè)進(jìn)程間通信方式。 共享內(nèi)存,顧名思義就是允許兩個(gè)不相關(guān)的進(jìn)程訪問同一個(gè)邏輯內(nèi)存,共享內(nèi)存是兩個(gè)正在運(yùn)行的進(jìn)程之間共享和傳遞數(shù)據(jù)的一種非常有效的方式。不同進(jìn)程之間共享的內(nèi)存通常為同一段物理內(nèi)存。進(jìn)程可以將同一段物理內(nèi)

    2024年01月16日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包