linux使用共享內存進行進程通信

一、什么是共享內存

  1. 共享內存就是允許兩個不相關的進程訪問同一個邏輯內存。共享內存是在兩個正在運行的進程之間共享和傳遞數據的一種非常有效的方式。不同進程之間共享的內存通常為同一段物理內存。使用共享內存進行通信的進程都需要同一段共享內存連接到它們自己的地址空間中,從而所有進程都可以訪問共享內存中的數據,這段內存就好像由C語言函數malloc()分配的一樣。而如果某個進程向共享內存寫入數據,所做的改動將立即影響到可以訪問同一段共享內存的其他進程
  • 特別提醒:共享內存并未提供同步機制,也就是說,在第一個進程結束對共享內存的寫操作之前,并無機制可以阻止第二個進程對內存數據的讀取。
  • 可以使用信號量來同步對共享內存的訪問

二、共享內存的使用

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

1、shmget()函數

  • 該函數用來創建共享內存,它的原型為:

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()函數 ? ?-- at:attach

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

void *shmat(int shm_id, const void *shm_addr, int shmflg);

  • 第一個參數,shm_id是由shmget()函數返回的共享內存標識。
  • 第二個參數,shm_addr指定共享內存連接到當前進程中的地址位置,通常為空,表示讓系統來選擇共享內存的地址。
  • 第三個參數,shm_flg是一組標志位,通常為0。
  • 調用成功時返回一個指向共享內存第一個字節的指針,如果調用失敗返回-1.

3、shmdt()函數 ? ?-- dt:detach

  • 該函數用于將共享內存從當前進程中分離。注意,將共享內存分離并不是刪除它,只是使該共享內存對當前進程不再可用。它的原型如下:

int?shmdt(const void *shmaddr);

  • 參數shmaddr是shmat()函數返回的地址指針,調用成功時返回0,失敗時返回-1.

4、shmctl()函數 ? ?-- ctl:control

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

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結構 至少包括以下成員:
struct shmid_ds
{uid_t shm_perm.uid;uid_t shm_perm.gid;mode_t shm_perm.mode;
};

三、使用共享內存進行進程間通信

  • 說了這么多,又到了實戰的時候了。下面就以兩個不相關的進程來說明進程間如何通過共享內存來進行通信。其中一個文件shmread.c創建共享內存,并讀取其中的信息,另一個文件shmwrite.c向共享內存中寫入數據。為了方便操作和數據結構的統一,為這兩個文件定義了相同的數據結構,定義在文件shmdata.c中。結構shared_use_st中的written作為一個可讀或可寫的標志,非0:表示可讀,0:表示可寫,text則是內存中的文件。
  • 將shmdata.h和shmread.h作為一個工程
  • 將shmdata.h和shmwrite.h作為一個工程
  • 先執行shmread.h的代碼,再執行shmwrite.h的代碼
  • shmdata.h的源代碼如下:

#ifndef _SHMDATA_H_HEADER
#define _SHMDATA_H_HEADER#define TEXT_SZ 2048struct shared_use_st
{int written; // 作為一個標志,非0:表示可讀,0:表示可寫char text[TEXT_SZ]; // 記錄寫入 和 讀取 的文本
};#endif
  • shmread.h

#include <cstdlib>
#include <cstdio>
#include <sys/shm.h>
#include <unistd.h>
#include <cstring>#include "shmdata.h"
int main() {void *shm = nullptr;struct shared_use_st *shared;int shmid;// 創建共享內存shmid = shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);if (shmid == -1){fprintf(stderr,"shmat failed\n");exit(EXIT_FAILURE);}// 將共享內存連接到當前進程的地址空間shm = shmat(shmid,0,0);if (shm == (void *)-1){fprintf(stderr,"shmat failed\n");exit(EXIT_FAILURE);}printf("Memory attched at %p\n", &shm);shared = (struct shared_use_st*)shm;shared->written = 0;while (1){if (shared->written == 1){printf("You wrote:%s",shared->text);sleep(1);// 讀取完數據,設置written使共享內存段可寫shared->written =0;if (strncmp(shared->text,"end",3) == 0){break;}}else{sleep(1);}}// 把共享內存從當前進程中分離if (shmdt(shm) == -1){fprintf(stderr,"shmat failed\n");exit(EXIT_FAILURE);}// 刪除共享內存if (shmctl(shmid,IPC_RMID,0) ==-1){fprintf(stderr, "shmctl(IPC_RMID) failed\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);
}
  • shmwrite.h

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <sys/shm.h>
#include <unistd.h>
#include "shmdata.h"int main(int argc, char **argv)
{void *shm = NULL;struct shared_use_st *shared = NULL;char buffer[BUFSIZ + 1]; // 用于保存輸入的文本int shmid;// 創建共享內存shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);if (shmid == -1){fprintf(stderr, "shmget failed\n");exit(EXIT_FAILURE);}// 將共享內存連接到當前的進程地址空間shm = shmat(shmid, (void *)0, 0);if (shm == (void *)-1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}printf("Memory attched at %p\n", &shm);// 設置共享內存shared = (struct shared_use_st *)shm;while (1) // 向共享內存中寫數據{// 數據還沒有被讀取,則等待數據被讀取,不能向共享內存中寫入文本while (shared->written == 1){sleep(1);printf("Waiting...\n");}// 向共享內存中寫入數據printf("Enter some text: ");fgets(buffer, BUFSIZ, stdin);strncpy(shared->text, buffer, TEXT_SZ);// 寫完數據,設置written使共享內存段可讀shared->written = 1;// 輸入了end,退出循環(程序)if (strncmp(buffer, "end", 3) == 0){break;}}// 把共享內存從當前進程中分離if (shmdt(shm) == -1){fprintf(stderr, "shmdt failed\n");exit(EXIT_FAILURE);}sleep(2);exit(EXIT_SUCCESS);
}

輸出結果

分析

  • 程序shmread創建共享內存,然后將它連接到自己的地址空間。在共享內存的開始處使用了一個結構struct_use_st。該結構中有個標志written,當共享內存中有其他進程向它寫入數據時,共享內存中的written被設置為0,程序等待。當它不為0時,表示沒有進程對共享內存寫入數據,程序就從共享內存中讀取數據并輸出,然后重置設置共享內存中的written為0,即讓其可被shmwrite進程寫入數據。
  • 程序shmwrite取得共享內存并連接到自己的地址空間中。檢查共享內存中的written,是否為0,若不是,表示共享內存中的數據還沒有被完,則等待其他進程讀取完成,并提示用戶等待。若共享內存的written為0,表示沒有其他進程對共享內存進行讀取,則提示用戶輸入文本,并再次設置共享內存中的written為1,表示寫完成,其他進程可對共享內存進行讀操作。

四、關于前面的例子的安全性討論

  • 這個程序是不安全的,當有多個程序同時向共享內存中讀寫數據時,問題就會出現。可能你會認為,可以改變一下written的使用方式,例如,只有當written為0時進程才可以向共享內存寫入數據,而當一個進程只有在written不為0時才能對其進行讀取,同時把written進行加1操作,讀取完后進行減1操作。這就有點像文件鎖中的讀寫鎖的功能。咋看之下,它似乎能行得通。但是這都不是原子操作,所以這種做法是行不能的。試想當written為0時,如果有兩個進程同時訪問共享內存,它們就會發現written為0,于是兩個進程都對其進行寫操作,顯然不行。當written為1時,有兩個進程同時對共享內存進行讀操作時也是如些,當這兩個進程都讀取完是,written就變成了-1.
  • 要想讓程序安全地執行,就要有一種進程同步的進制,保證在進入臨界區的操作是原子操作。例如,可以使用前面所講的信號量來進行進程的同步。因為信號量的操作都是原子性的。

五、使用共享內存的優缺點

  • 優點:我們可以看到使用共享內存進行進程間的通信真的是非常方便,而且函數的接口也簡單,數據的共享還使進程間的數據不用傳送,而是直接訪問內存,也加快了程序的效率。同時,它也不像匿名管道那樣要求通信的進程有一定的父子關系。
  • 缺點:共享內存沒有提供同步的機制,這使得我們在使用共享內存進行進程間通信時,往往要借助其他的手段來進行進程間的同步工作。

參考鏈接

  • shmget

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/446809.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/446809.shtml
英文地址,請注明出處:http://en.pswp.cn/news/446809.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

安卓TextView文本框與自定義邊框

常用屬性 自定義邊框 基本使用 <?xml version"1.0" encoding"utf-8"?> <shape xmlns:android"http://schemas.android.com/apk/res/android"android:shape"rectangle矩形/ring圓環/oval橢圓/line直線"當為圓環時android:s…

Android RecyclerView實現九宮格效果

RecyclerView更加優化的復用機制和方便實現UI效果&#xff0c;幾乎替代Listview和GridView的使用。但是分割線的實現&#xff0c;需要自己繼承ItemDecoration來繪制。 完整代碼已上傳至Github&#xff1a;RecyclerView實現九宮格效果 效果圖 item的布局文件 <?xml versi…

如何讀取指針指向的地址空間呢?

方法 使用%p 接收指針返回的地址空間 代碼 #include <stdio.h> #include <stdlib.h>int main() {int a 100;int *a_p &a;printf("%p\n",&a);//輸出&#xff1a;002AF744 輸出的是a變量的地址printf("%p\n",a_p);//輸出&#xff1…

科學究研明表,漢字序順并不一定影閱響讀

有個很有意思的現象&#xff1a; 不信你就來試試 中文打亂小工具 github地址&#xff1a;在線打亂文字順序

安卓EditText

常用屬性 android:textAllCaps"false"去除大寫狀態 inputType 常用 textpassword密碼 number數字 phone撥號鍵盤 設置光標位置 editText.setSelection(2);從1開始 editText.setSelection(1,3);從1開始,1–3中間部分,一個范圍

完善博文 共享內存一寫多讀無鎖實現的代碼邏輯部分

使用共享內存(內存映射)實現發布訂閱模式 多進程實現PubSub發布訂閱模式&#xff0c;從而實現進程間的通信。通信方式可以是TCP/UDP&#xff0c;管道Pipe/消息隊列&#xff0c;共享內存shared memory等等。其中TCP/UDP的方式是可以用作局域網以及跨平臺的通信&#xff0c;Pipe…

想對你說的話,就在這里!

甜(Tu)言(Wei)蜜(Qing)語(Hua)最近在github上看到了一個朋友開發的 土味情話在線生成器 &#xff0c;感覺還不錯&#xff0c;在這里推薦一下。 github地址&#xff1a;在線生成土味情話

linux讀寫文件 簡單版

代碼 //write void write_file(const std::string file_name){FILE *fp nullptr;fp fopen(file_name.c_str(),"w");fprintf(fp,"This is testing for mutex\n");fclose(fp); } //read void read_file(const std::string file_name){std::ifstream fp(fi…

具有中國風的傳統顏色(炫酷)

一個小小的中國風的傳統顏色&#xff0c;你覺得應該是什么樣子的呢&#xff1f; 看了下面這個&#xff0c;我一個搞移動開發的都想去搞前端開發了。 廢話不多說了&#xff0c;直接看效果&#xff1a; 訪問地址&#xff1a;中國傳統顏色手冊 github地址&#xff1a;Chinese…

Android Studio安裝問題及填坑

安裝過程 安裝Android Studio 其他問題 1.Android Studio出現Error:Unable to tunnel through proxy. Proxy returns “HTTP/1.1 400 Bad Request” 2.Could not resolve all artifacts for configuration :classpath 3.!No cached version of com.android.tools.build:gr…

Linux strtol將十六進制轉化為十進制

代碼 #include <iostream> #include "crypto_util.h"int get_file(const std::string file_name){size_t get_file_id 0;std::cout << hsm::common::get_md5_digest_hex(file_name) << std::endl;get_file_id strtol(reinterpret_cast<const…

Android WebView使用攻略

目錄前言一、簡介二、作用三、使用介紹1、Webview類常用方法1.1、加載url1.2、WebView的狀態1.3、關于前進 / 后退網頁1.4、清除緩存數據2、常用工具類2.1、WebSettings類2.2、WebViewClient類2.3、WebChromeClient類3、注意事項&#xff1a;如何避免WebView內存泄露&#xff1…

C++If與Switch語句

IF if語句不加括號就只是一個語句 舉例: int a5,b2; if(a)//按邏輯值來理解,0為假,其他為真,這里等價于a!0—>a為真時 ab; else ba; 計算三角形面積代碼 #include<iostream> #include<cmath>//數學公式庫 #include<iomanip> //格式控制 using namesp…

linux fork多進程 demo

注釋 使用系統調用fork()創建三個子進程&#xff1b;各個子進程顯示和輸出一些提示信息和自己的進程標識符&#xff1b;父進程顯示自己的進程ID和一些提示信息&#xff0c;然后調用waitpid()等待多個子進程結束&#xff0c;并在子進程結束后顯示輸出提示信息表示程序結束。 代…

Android WebView 與 JS 交互

目錄二、具體分析2.1 Android通過WebView調用 JS 代碼方式1&#xff1a;通過WebView的loadUrl()方式2&#xff1a;通過WebView的evaluateJavascript()方法對比使用建議2.2、JS通過WebView調用 Android 代碼2.2.1、方法分析方式1&#xff1a;通過 WebView的addJavascriptInterfa…

關于鎖的注意事項

文件鎖 Linux 提供了 fcntl 系統調用&#xff0c;可以鎖定文件但是文件鎖是和進程相關聯的&#xff0c;一個進程中的多個線程/協程對同一個文件進行的鎖操作會互相覆蓋掉&#xff0c;從而無效。fcntl 創建的鎖是建議性鎖&#xff0c;只有寫入的進程和讀取的進程都遵循建議才有效…

安卓實現登錄與注冊界面

使用Intent與Bundle傳遞數據 登錄界面login.xml 1.使用Relativelayout相對布局 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"mat…

Android Button字母自動全部大寫的問題

兩種解決方案&#xff1a; 方法一&#xff1a; 在 xml 布局中設置屬性 android:textAllCaps"false" <Buttonandroid:layout_width"wrap_content"android:layout_height"match_parent"android:text"添加動作組"android:textAllCap…

安卓Activity與intent跳轉

Activity生命周期 Activity啟動模式 Intent跳轉 _________startActivity() 1.Intent intentnew Intent(A.this,B.class); startActivity(intent); 2.startActivity(new Intent(A.this,B.class)); _________startActivityForResult() Intent intentnew Intent(A.this,B.class…

將讀寫鎖放到共享內存中,實現進程之間對數據的讀寫訪問控制

代碼 #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <assert.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <fstream> #include <…