1. 程式人生 > >Linux 下 IPC 之 System V 共享記憶體的使用和原理初探

Linux 下 IPC 之 System V 共享記憶體的使用和原理初探

前言

總結一下最近對於共享記憶體的學習, 可能比較淺顯或者有疏漏, 歡迎指正!

原理初探

我們知道, 程序空間相互隔離, 互相對立, 但是共享記憶體允許多個程序可以訪問同一塊記憶體來達到程序間通訊的目的.

共享記憶體是最高效的 IPC 機制, 它不涉及任何程序間的資料傳輸, 而且他和程序同處於使用者空間, 不像訊息佇列, 訊號量是核心空間的系統物件, 不需要花費額外的資料拷貝, 但是同時他並沒有預防競態條件, 也就是說在多程序利用共享記憶體進行通訊的情況下, 我們需要自己去利用鎖等操作來進行同步

共享記憶體的建立過程

當我們建立了一塊共享記憶體, 其實是在 tmpfs 中建立了一個檔案 (這個檔案是儲存於記憶體的), 也就意味著在 tmpfs 中建立了一個 iNode

節點

然後我們需要將這個建立好的檔案對映到程序中 (如下圖, 此圖來自網路, 應該是哪個部落格或者知乎吧…已經記不清了)

在這裡插入圖片描述

我們建立的這塊共享記憶體不會隨著程序的結束而被釋放, 他會在關機前一直存在於記憶體中, 除非被程序明確的刪除

也可以使用 `ipcrm + 共享記憶體 id ` 命令刪除

在這裡插入圖片描述

比如這裡我在剛開始練習的時候沒有刪除建立的共享記憶體

在這裡插入圖片描述

系列 API 的使用

ftok

#include <sys/shm.h>
#include <sys/types.h>
key_t ftok ( const char* fname, int fd);

成功返回一個key_t值, 失敗返回-1

fname 引數是一個必須存在且能訪問到的目錄
id 子序號, 自己約定, 只有8個位元位被指用

注意, 當檔案路徑和子序號都相同時返回的並不一定永遠返回一樣的key值, 如果該路徑指向的檔案或者目錄被刪除而又重新建立, 就算名字還是一樣, 但是檔案系統會賦予它不同的 iNode 資訊, 所以返回的 key 值就不同了

shmget

#include <sys/shm.h>
int shmget (key_t key, size_t size, int shmflag);

用來建立一塊共享記憶體並返回其 id 
或者獲得一塊已經被建立的共享記憶體的 id

成功返回一個正整數值, 這個整數是共享記憶體的識別符號, 失敗時返回 -1

, 錯誤儲存於 errno

key_t key 引數用來唯一標識一段全域性共享記憶體, 通常通過 ftok 函式獲得,
size_t size 引數是建立的共享記憶體的大小, 單位為位元組, 如果是要建立一塊共享記憶體, 此引數必須被指定, 如果是要獲取一塊建立好的共享記憶體的 id, 可以將其設定為 0
int shmglag 引數為 0 為獲取共享記憶體 id, 為 IPC_CREAT 時是建立一個新的共享記憶體, 通常要同時指定許可權 (和許可權進行 | 運算)
同時還能取IPC_EXCL , 只有在共享記憶體不存在的時候,新的共享記憶體才建立,否則就產生錯誤。

使用 shmget 建立的共享記憶體段會全部被初始化為 0, 同時和他關聯的核心資料結構 shmid_ds 將被建立和初始化

struct shmid_ds { 
    struct ipc_perm    shm_perm;      /* 操作許可權 */ 
    size_t             shm_segsz;     /* 大小,單位是位元組 */ 
    __kernel_time_t    shm_atime;     /* 對這段記憶體最後一次呼叫shmat的時間 */ 
    __kernel_time_t    shm_dtime;     /* 最後一次呼叫shmdt的時間*/ 
    __kernel_time_t    shm_ctime;     /* 最後一次呼叫shmctl的時間*/ 
    __kernel_ipc_pid_t shm_cpid;      /* 建立者的pid*/ 
    __kernel_ipc_pid_t shm_lpid;      /* 最後一次執行shmat或shmdt的程序的pid*/ 
    unsigned short     shm_nattch;    /*目前關聯到次共享記憶體的程序的數量*/ 
    unsigned short     shm_unused;    /* 以下為填充 */ 
    void               *shm_unused2; /* ditto - used by DIPC */ 
    void               *shm_unused3; /* unused */ 
};

shmat

前面我們也說道, 我們建立共享記憶體其實是建立了一個檔案, 然後要把它對映到當前程序, 即, 我們在使用 shmget 建立了一塊共享記憶體之後, 要使用 shmat 將他關聯到當前程序, 同樣的使用完不再需要之後, 也需要使用 shmdt 將其分離, 讓我們先看 shmat

#include <sys/shm.h>
void* shmat ( int shm_id, const void* shm_addr, int shmflag );

成功返回對映到程序的地址空間 失敗返回 (void *)-1並將錯誤儲存於 errno

shm_id 引數是 shmget 返回的共享記憶體 id,
shm_addr 引數是指定共享記憶體在程序記憶體地址的對映位置, 推薦使用 NULL, 由核心自己決定
shmflag一般為0

shmat 呼叫成功後, 會修改 shmid_ds 的部分欄位:
將 shm_nattach 加一
將 shm_lpid 設定為呼叫程序 pid
將 shm_atime 設定為當前時間

shmdt

在共享記憶體使用完之後使用此函式將其從程序地址空間分離

#include <sys/shm.h>
int shmdt ( const void* shm_addr );

成功返回 0, 失敗返回 -1

shm_addr 引數是共享記憶體在程序的對映地址, 即 shmat 返回的值

呼叫 shmdt 並不會刪除共享記憶體
呼叫成功時修改核心資料結構 shmid_ds 部分欄位:
將 shm_nattach 減一
將 shm_lpid 設定為呼叫程序的pid
將 shm_dtime 設定為當前時間

shmctl

管理共享記憶體

#include 
int shmctl ( int shm_id, int command, struct shmid_ds* buf );

失敗返回-1, 並存儲錯誤於 errno, 成功時的返回值取決於 command

shm_id 是共享記憶體識別符號
command 指定要執行的命令

常用命令為 IPC_RMID, 即, 刪除共享記憶體

如果共享記憶體已經與所有訪問它的程序斷開了連線,則呼叫IPC_RMID子命令後,系統將立即刪除共享記憶體的識別符號,並刪除該共享記憶體區,以及所有相關的資料結構;

如果仍有別的程序與該共享記憶體保持連線,則呼叫IPC_RMID子命令後,該共享記憶體並不會被立即從系統中刪除,而是被設定為IPC_PRIVATE狀態,並被標記為"已被刪除";直到已有連線全部斷開,該共享記憶體才會最終從系統中消失。

共享記憶體例項

通過共享記憶體進行同機間的程序間通訊

建立一個長度為10的 Stu 陣列, 向其中寫入資料, 另一個程序讀取
程式碼很簡單, 就不做註釋了
/*
write.cpp
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

typedef struct Stu 
{
    int age;
    char name[10];
} Stu;

int main() 
{
 
    int id = shmget((key_t)1234, sizeof(Stu) * 10, IPC_CREAT | 0644);
    if (id == -1) 
    {
        perror("shmget "), exit(1);
    }
    
    Stu* t = (Stu *)shmat(id, NULL, 0);
    for (int i = 0; i < 5; i++) 		//初始化前五個
    {
        (t + i)->age = i;
        strcpy((t + i)->name, "lvbai");
    }

    shmdt(t);
 
    return 0;
}

/*
read.cpp
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

typedef struct Stu 
{
    int age;
    char name[10];
} Stu;

int main() 
{
    int id = shmget((key_t)1234, 0, IPC_CREAT);
    if (id == -1) 
    {
        perror("shmget "), exit(1);
    }

    void* p = NULL;
    p = shmat(id, NULL, 0);
    if (p == (void *)-1) 
    {
        perror("****** : ");
    }


    Stu* ptr = (Stu *)p;

	printf("here is message : \n");

    for (int i = 0; i < 10; i++) 
    {
        printf("age = %d, name = %s\n", (ptr + i)->age, (ptr + i)->name);
    }

    shmdt(p);

    shmctl(id, IPC_RMID, 0);
   
   
    
    return 0;
}

在這裡插入圖片描述

相關推薦

Linux IPC System V 共享記憶體的使用原理初探

前言 總結一下最近對於共享記憶體的學習, 可能比較淺顯或者有疏漏, 歡迎指正! 原理初探 我們知道, 程序空間相互隔離, 互相對立, 但是共享記憶體允許多個程序可以訪問同一塊記憶體來達到程序間通訊的目的. 共享記憶體是最高效的 IPC 機制, 它不涉及任何程

57-System V 共享記憶體-shmctl

在很久以前,我們學過一個函式 fcntl,當時學習它時挺費勁,因為一個函式,竟然有如此多的功能。今天要學習 shmctl 函式,也是一樣。這種以 cntl/ctl 為字尾的函式,往往都具備這樣的特性:它們都有一個命令控制引數,你傳遞不同的命令,這個函式有具備著不

arm linux編譯庫System.Net.Primitives.dllSystem.Xml.XmlSerializer.dll

cad serial linu 5.4 mcs download 切換 mon dll 1.環境: /home/jello # uname -aLinux 3.10.0 #2 SMP Mon Mar 6 17:52:09 CST 2017 armv7l GNU/Linux

LinuxLinux程序通訊與System V IPC機制

Linux程序通訊基本概念 從原理上來看,程序通訊的關鍵技術就是在程序間建立某種共享區,利用程序都可以訪問共享區的特點來建立一些通訊通道。如下圖所示: 其實,以前設計程式時使用的全域性變數,就是一種可以在各個函式之間進行通訊的手段,它所佔用的記憶體空間就是程式中各個函

linux網路程式設計POSIX 共享記憶體 系列函式

在前面介紹了system v 共享記憶體的相關知識,現在來稍微看看posix 共享記憶體 和系列函式。 共享記憶體簡單來說就是一塊真正的實體記憶體區域,可以使用一些函式將這塊區域對映到程序的地址空間進行讀寫,而posix 共享記憶體與system v 共享記憶體不同的是它是

LinuxIPC機制Socket通訊總結

Linux下IPC機制有很多種,Socket算得上比較廣泛的一種,在不使用像D-Bus之類的重量級訊息匯流排之前採用socket作為兩個程序之間的通話算得上比較不錯的選擇,因此它的用途比較廣泛.這裡稍微做下總結吧.1:常規用法//初始化MyLink程序 int initMyl

system v 共享內存區

include system #include<sys/shm.h> int shmget(key_t key,size_t size,int oflag); 返回:成功則為共享內存區對象,出錯為-1 key 的值可以是ftok的返回值,也可以是IPC_PRIVA

system v 共享內存

print usr ftok 新的 byte ipc 共享 err turn #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h

Linuxtouch、mv、cp、rm

linux touch mv cp rm touch 新建文件 touch 文件名 例如: touch file1 在當前目錄新建名為file1的文件 註意: 1)同一目錄無法創建同名的文件 2)Linux的文件名是區分大小寫的 mv 修改文件名(或者目錄的名

Linux搭建SMB文件共享服務,Linux/Windows互聯互通

出現 lan 分別是 接下來 linux下 server 需要 init.d main 一丶簡介:SMB協議是建立在NetBIOS協議之上的應用協議,是基於TCP138、139兩個端口的服務,NetBIOS出現之後,Microsoft就使用NetBIOS實現了一個網絡文件/

linuxtomcattoo many open files

設置 inux roc spa ava linux 執行 java 使用命令 一、問題表象:   程序日誌報錯:java.io.IOException: Too many open files at 二、解決方案:   1、查看系統允許打開的最大文件數:     ca

c語言實現linux高危函式system (簡易V1.0版本)

system這個函式真的是要慎用,一不小心就會留下漏洞。 下面是用c語言簡易的實現了一下system函式 #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<err

如何理解Linux,使用top命令看到記憶體佔用情況

linux 下使用top命令之後看到記憶體佔用情況如下: Mem: 32849260k total, 32630656k used, 218604k free, 445512k buffers Swap: 0k total, 0k used,

Linux-程序通訊-訊息佇列/訊號燈/共享記憶體

訊息佇列     訊息佇列提供了程序間傳送資料塊的方法,每個資料塊都可以被認為是有一個型別,接受者接受的資料塊可以有不同的型別;我們可以通過傳送訊息來避免命名管道的同步和阻塞問題;訊息佇列與命名管道一樣,每個資料塊都有一個最大長度的限制;我們可以將每個資料塊當作是一

Linux使用java獲取cpu、記憶體使用率

原文地址:http://www.voidcn.com/article/p-yehrvmep-uo.html 思路如下:Linux系統中可以用top命令檢視程序使用CPU和記憶體情況,通過Runtime類的exec()方法執行命令"top”,獲取"top"的輸出,從而得到CPU和記憶體的使用情況。

Linux的多程序間共享資源的互斥訪問

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Linuxiscsi提供的磁碟共享服務(企業級)

ISCSI iSCSI( Internet Small Computer System Interface 網際網路小型計算機系統介面)是由IBM 下屬的兩大研發機構一一加利福尼亞AImaden和以色列Haifa研究中心共同開發的,是一個供硬體裝置使用的、可在IP協議上層執行的SCSI指

Linux學習筆記smb檔案共享

SMB檔案共享服務:SMB協議是建立在NetBIOS協議之上的應用協議,是基於TCP138、139兩個埠的服務,NetBIOS出現之後,Microsoft就使用NetBIOS實現了一個網路檔案/列印服務系統。這個系統基於NetBIOS設定了一套檔案共享協議,Microsoft稱之為SMB(Server Mes

c語言實現linux高危函式system (簡易V1.0版本)

system這個函式真的是要慎用,一不小心就會留下漏洞。 下面是用c語言簡易的實現了一下system函式 #include<stdio.h> #include<stdlib.h> #include<unistd.h> #inc

linux掛載windows上的共享目錄,並設定所有者為非root使用者

參考了很多文章,這裡總結下我得出來的最優答案(針對我的需求而言)吧,但是還是存在bug,稍後指出! 以下是我的bash指令碼,防止多次重複掛載,相信看了就能明白: [[email protected] share]$ cat m.sh #!/bin/bash