目錄
一、什麼是共享記憶體" >一、什麼是共享記憶體
二、共享記憶體的使得" >二、共享記憶體的使得
三、使用共享内存进行进程间通信" >三、使用共享内存进行进程间通信
首頁 系統教程 Linux Linux 進程間通訊的一種高效方法:使用共享內存

Linux 進程間通訊的一種高效方法:使用共享內存

Feb 11, 2024 pm 07:00 PM
linux linux教程 linux系統 linux指令 shell腳本 同步機制 嵌入式linux linux入門 linux學習

以下將講解進程間通訊的另一種方式,使用共享記憶體。

一、什麼是共享記憶體

顧名思義,共享記憶體就是允許兩個不相關的程序存取同一個邏輯記憶體。共享記憶體是在兩個正在運行的進程之間共享和傳遞資料的一種非常有效的方式。不同進程之間共享的記憶體通常安排為同一段實體記憶體。進程可以將同一段共享記憶體連接到它們自己的位址空間中,所有進程都可以存取共享記憶體中的位址,就好像它們是由用C語言函數malloc分配的記憶體一樣。而如果某個進程向共享記憶體寫入數據,所做的改動將立即影響到可以存取同一段共享記憶體的任何其他進程。

Linux 进程间通信的一种高效方法:使用共享内存

#特別提醒:共享記憶體並未提供同步機制,也就是說,在第一個進程結束對共享記憶體的寫入操作之前,並無自動機制可以阻止第二個進程開始對它進行讀取。所以我們通常需要用其他的機制來同步對共享記憶體的訪問,例如前面說到的信號量。有關信號量的更多內容,可以查閱我的另一篇文章:Linux進程間通訊-使用信號量

二、共享記憶體的使得

與信號量一樣,在Linux中也提供了一組函數介面用於使用共享內存,而且使用共享共存的介面還與信號量的非常相似,而且比使用信號量的接口來得簡單。它們聲明在頭檔 sys/shm.h中。

1、shmget函數

此函數用來建立共享內存,它的原型是:

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

#第一個參數,與信號量的semget函數一樣,程式需要提供一個參數key(非0整數),它有效地為共享記憶體段命名,shmget函數成功時傳回一個與key相關的共享記憶體標識符(非負整數),用於後續的共享記憶體函數。呼叫失敗返回-1.

不相關的進程可以透過該函數的返回值存取同一共享內存,它代表程式可能要使用的某個資源,程式對所有共享內存的存取都是間接的,程式先透過調用shmget函數並提供一個鍵,再由系統產生一個對應的共享記憶體標識符(shmget函數的返回值),只有shmget函數才直接使用信號量鍵,所有其他的信號量函數使用由semget函數傳回的信號量識別碼。

第二個參數,size以位元組為單位指定需要共享的記憶體容量

第三個參數,shmflg是權限標誌,它的作用與open函數的mode參數一樣,如果要想在key標識的共享記憶體不存在時,創建它的話,可以與IPC_CREAT做或操作。共享記憶體的權限標誌與檔案的讀寫權限一樣,舉例來說,0644,它表示允許一個進程創建的共享內存被內存創建者所擁有的進程向共享內存讀取和寫入數據,同時其他用戶創建的進程只能讀取共享記憶體。

2、shmat函數

第一次創建完共享記憶體時,它還不能被任何進程訪問,shmat函數的作用就是用來啟動對該共享記憶體的訪問,並把共享記憶體連接到當前進程的地址空間。它的原型如下:

  1. **void** *shmat(**int** shm_id, **const** **void** *shm_addr, **int** shmflg); 
    
    登入後複製

第一個參數,shm_id是由shmget函數傳回的共享記憶體標識。

第二個參數,shm_addr指定共享記憶體連接到目前進程中的位址位置,通常為空,表示讓系統來選擇共享記憶體的位址。

第三個參數,shm_flg是一組標誌位,通常為0。

呼叫成功時傳回一個指向共享記憶體第一個位元組的指針,如果呼叫失敗返回-1.

3、shmdt函數

此函數用於將共享記憶體從目前進程中分離。請注意,將共享記憶體分離並不是刪除它,只是使該共享記憶體對當前進程不再可用。它的原型如下:

 1. **int** shmdt(**const** **void** *shmaddr); 
登入後複製

參數shmaddr是shmat函數傳回的位址指針,呼叫成功時回傳0,失敗時回傳-1.

4、shmctl函數

與信號量的semctl函數一樣,用來控制共享內存,它的原型如下:

  1. **int** shmctl(**int** shm_id, **int** command, **struct** shmid_ds *buf); 
    
    登入後複製

第一个参数,shm_id是shmget函数返回的共享内存标识符。

第二个参数,command是要采取的操作,它可以取下面的三个值 :

IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。

IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值

IPC_RMID:删除共享内存段

第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。

shmid_ds结构至少包括以下成员:

1. struct** shmid_ds 
2. { 
3.   uid_t shm_perm.uid; 
4.   uid_t shm_perm.gid; 
5.   mode_t shm_perm.mode; 
6. }; 
登入後複製

三、使用共享内存进行进程间通信

说了这么多,又到了实战的时候了。下面就以两个不相关的进程来说明进程间如何通过共享内存来进行通信。其中一个文件shmread.c创建共享内存,并读取其中的信息,另一个文件shmwrite.c向共享内存中写入数据。为了方便操作和数据结构的统一,为这两个文件定义了相同的数据结构,定义在文件shmdata.c中。结构shared_use_st中的written作为一个可读或可写的标志,非0:表示可读,0表示可写,text则是内存中的文件。

shmdata.h的源代码如下:

1. \#ifndef _SHMDATA_H_HEADER 
2. \#define _SHMDATA_H_HEADER 
3.  
4. \#define TEXT_SZ 2048 
5.  
6. **struct** shared_use_st 
7. { 
8.   **int** written;//作为一个标志,非0:表示可读,0表示可写 
9.   **char** text[TEXT_SZ];//记录写入和读取的文本 
10. }; 
11.  
12. \#endif 
登入後複製

源文件shmread.c的源代码如下:

  1. 1. \#include  
    2. \#include  
    3. \#include  
    4. \#include  
    5. \#include "shmdata.h" 
    6.  
    7. **int** main() 
    8. { 
    9.   **int** running = 1;//程序是否继续运行的标志 
    10.   **void** *shm = NULL;//分配的共享内存的原始首地址 
    11.   **struct** shared_use_st *shared;//指向shm 
    12.   **int** shmid;//共享内存标识符 
    13.   //创建共享内存 
    14.   shmid = shmget((key_t)1234, **sizeof**(**struct** shared_use_st), 0666|IPC_CREAT); 
    15.   **if**(shmid == -1) 
    16.   { 
    17. ​    fprintf(stderr, "shmget failed\n"); 
    18. ​    exit(EXIT_FAILURE); 
    19.   } 
    20.   //将共享内存连接到当前进程的地址空间 
    21.   shm = shmat(shmid, 0, 0); 
    22.   **if**(shm == (**void***)-1) 
    23.   { 
    24. ​    fprintf(stderr, "shmat failed\n"); 
    25. ​    exit(EXIT_FAILURE); 
    26.   } 
    27.   printf("\nMemory attached at %X\n", (**int**)shm); 
    28.   //设置共享内存 
    29.   shared = (**struct** shared_use_st*)shm; 
    30.   shared->written = 0; 
    31.   **while**(running)//读取共享内存中的数据 
    32.   { 
    33. ​    //没有进程向共享内存定数据有数据可读取 
    34. ​    **if**(shared->written != 0) 
    35. ​    { 
    36. ​      printf("You wrote: %s", shared->text); 
    37. ​      sleep(rand() % 3); 
    38. ​      //读取完数据,设置written使共享内存段可写 
    39. ​      shared->written = 0; 
    40. ​      //输入了end,退出循环(程序) 
    41. ​      **if**(strncmp(shared->text, "end", 3) == 0) 
    42. ​        running = 0; 
    43. ​    } 
    44. ​    **else**//有其他进程在写数据,不能读取数据 
    45. ​      sleep(1); 
    46.   } 
    47.   //把共享内存从当前进程中分离 
    48.   **if**(shmdt(shm) == -1) 
    49.   { 
    50. ​    fprintf(stderr, "shmdt failed\n"); 
    51. ​    exit(EXIT_FAILURE); 
    52.   } 
    53.   //删除共享内存 
    54.   **if**(shmctl(shmid, IPC_RMID, 0) == -1) 
    55.   { 
    56. ​    fprintf(stderr, "shmctl(IPC_RMID) failed\n"); 
    57. ​    exit(EXIT_FAILURE); 
    58.   } 
    59.   exit(EXIT_SUCCESS); 
    60. } 
    
    登入後複製

源文件shmwrite.c的源代码如下:

1. \#include  
2. \#include  
3. \#include  
4. \#include  
5. \#include  
6. \#include "shmdata.h" 
7.  
8. **int** main() 
9. { 
10.   **int** running = 1; 
11.   **void** *shm = NULL; 
12.   **struct** shared_use_st *shared = NULL; 
13.   **char** buffer[BUFSIZ + 1];//用于保存输入的文本 
14.   **int** shmid; 
15.   //创建共享内存 
16.   shmid = shmget((key_t)1234, **sizeof**(**struct** shared_use_st), 0666|IPC_CREAT); 
17.   **if**(shmid == -1) 
18.   { 
19. ​    fprintf(stderr, "shmget failed\n"); 
20. ​    exit(EXIT_FAILURE); 
21.   } 
22.   //将共享内存连接到当前进程的地址空间 
23.   shm = shmat(shmid, (**void***)0, 0); 
24.   **if**(shm == (**void***)-1) 
25.   { 
26. ​    fprintf(stderr, "shmat failed\n"); 
27. ​    exit(EXIT_FAILURE); 
28.   } 
29.   printf("Memory attached at %X\n", (**int**)shm); 
30.   //设置共享内存 
31.   shared = (**struct** shared_use_st*)shm; 
32.   **while**(running)//向共享内存中写数据 
33.   { 
34. ​    //数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本 
35. ​    **while**(shared->written == 1) 
36. ​    { 
37. ​      sleep(1); 
38. ​      printf("Waiting...\n"); 
39. ​    } 
40. ​    //向共享内存中写入数据 
41. ​    printf("Enter some text: "); 
42. ​    fgets(buffer, BUFSIZ, stdin); 
43. ​    strncpy(shared->text, buffer, TEXT_SZ); 
44. ​    //写完数据,设置written使共享内存段可读 
45. ​    shared->written = 1; 
46. ​    //输入了end,退出循环(程序) 
47. ​    **if**(strncmp(buffer, "end", 3) == 0) 
48. ​      running = 0; 
49.   } 
50.   //把共享内存从当前进程中分离 
51.   **if**(shmdt(shm) == -1) 
52.   { 
53. ​    fprintf(stderr, "shmdt failed\n"); 
54. ​    exit(EXIT_FAILURE); 
55.   } 
56.   sleep(2); 
57.   exit(EXIT_SUCCESS); 
58. } 
登入後複製

再来看看运行的结果:

分析:

1、程序shmread创建共享内存,然后将它连接到自己的地址空间。在共享内存的开始处使用了一个结构struct_use_st。该结构中有个标志written,当共享内存中有其他进程向它写入数据时,共享内存中的written被设置为0,程序等待。当它不为0时,表示没有进程对共享内存写入数据,程序就从共享内存中读取数据并输出,然后重置设置共享内存中的written为0,即让其可被shmwrite进程写入数据。

2、程序shmwrite取得共享内存并连接到自己的地址空间中。检查共享内存中的written,是否为0,若不是,表示共享内存中的数据还没有被完,则等待其他进程读取完成,并提示用户等待。若共享内存的written为0,表示没有其他进程对共享内存进行读取,则提示用户输入文本,并再次设置共享内存中的written为1,表示写完成,其他进程可对共享内存进行读操作。

四、关于前面的例子的安全性讨论

这个程序是不安全的,当有多个程序同时向共享内存中读写数据时,问题就会出现。可能你会认为,可以改变一下written的使用方式,例如,只有当written为0时进程才可以向共享内存写入数据,而当一个进程只有在written不为0时才能对其进行读取,同时把written进行加1操作,读取完后进行减1操作。这就有点像文件锁中的读写锁的功能。咋看之下,它似乎能行得通。但是这都不是原子操作,所以这种做法是行不能的。试想当written为0时,如果有两个进程同时访问共享内存,它们就会发现written为0,于是两个进程都对其进行写操作,显然不行。当written为1时,有两个进程同时对共享内存进行读操作时也是如些,当这两个进程都读取完是,written就变成了-1.

要想让程序安全地执行,就要有一种进程同步的进制,保证在进入临界区的操作是原子操作。例如,可以使用前面所讲的信号量来进行进程的同步。因为信号量的操作都是原子性的。

五、使用共享内存的优缺点

1、优点:我们可以看到使用共享内存进行进程间的通信真的是非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。

2、缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。

以上是Linux 進程間通訊的一種高效方法:使用共享內存的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

vscode需要什麼電腦配置 vscode需要什麼電腦配置 Apr 15, 2025 pm 09:48 PM

VS Code 系統要求:操作系統:Windows 10 及以上、macOS 10.12 及以上、Linux 發行版處理器:最低 1.6 GHz,推薦 2.0 GHz 及以上內存:最低 512 MB,推薦 4 GB 及以上存儲空間:最低 250 MB,推薦 1 GB 及以上其他要求:穩定網絡連接,Xorg/Wayland(Linux)

Linux體系結構:揭示5個基本組件 Linux體系結構:揭示5個基本組件 Apr 20, 2025 am 12:04 AM

Linux系統的五個基本組件是:1.內核,2.系統庫,3.系統實用程序,4.圖形用戶界面,5.應用程序。內核管理硬件資源,系統庫提供預編譯函數,系統實用程序用於系統管理,GUI提供可視化交互,應用程序利用這些組件實現功能。

notepad怎麼運行java代碼 notepad怎麼運行java代碼 Apr 16, 2025 pm 07:39 PM

雖然 Notepad 無法直接運行 Java 代碼,但可以通過借助其他工具實現:使用命令行編譯器 (javac) 編譯代碼,生成字節碼文件 (filename.class)。使用 Java 解釋器 (java) 解釋字節碼,執行代碼並輸出結果。

vscode終端使用教程 vscode終端使用教程 Apr 15, 2025 pm 10:09 PM

vscode 內置終端是一個開發工具,允許在編輯器內運行命令和腳本,以簡化開發流程。如何使用 vscode 終端:通過快捷鍵 (Ctrl/Cmd ) 打開終端。輸入命令或運行腳本。使用熱鍵 (如 Ctrl L 清除終端)。更改工作目錄 (如 cd 命令)。高級功能包括調試模式、代碼片段自動補全和交互式命令歷史。

git怎麼查看倉庫地址 git怎麼查看倉庫地址 Apr 17, 2025 pm 01:54 PM

要查看 Git 倉庫地址,請執行以下步驟:1. 打開命令行並導航到倉庫目錄;2. 運行 "git remote -v" 命令;3. 查看輸出中的倉庫名稱及其相應的地址。

vscode 無法安裝擴展 vscode 無法安裝擴展 Apr 15, 2025 pm 07:18 PM

VS Code擴展安裝失敗的原因可能包括:網絡不穩定、權限不足、系統兼容性問題、VS Code版本過舊、殺毒軟件或防火牆干擾。通過檢查網絡連接、權限、日誌文件、更新VS Code、禁用安全軟件以及重啟VS Code或計算機,可以逐步排查和解決問題。

vscode在哪寫代碼 vscode在哪寫代碼 Apr 15, 2025 pm 09:54 PM

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

vscode 可以用於 mac 嗎 vscode 可以用於 mac 嗎 Apr 15, 2025 pm 07:36 PM

VS Code 可以在 Mac 上使用。它具有強大的擴展功能、Git 集成、終端和調試器,同時還提供了豐富的設置選項。但是,對於特別大型項目或專業性較強的開發,VS Code 可能會有性能或功能限制。

See all articles