Linux進程間通信——使用共享內存

//本文轉載http://blog.csdn.net/ljianhui/article/details/10253345
下面將講解進程間通信的另一種方式,使用共享內存。

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

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

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

1、shmget函數
該函數用來創建共享內存,它的原型為:
[cpp] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  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函數的作用就是用來啟動對該共享內存的訪問,并把共享內存連接到當前進程的地址空間。它的原型如下:
[cpp] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. void?*shmat(int?shm_id,?const?void?*shm_addr,?int?shmflg);??
第一個參數,shm_id是由shmget函數返回的共享內存標識。
第二個參數,shm_addr指定共享內存連接到當前進程中的地址位置,通常為空,表示讓系統來選擇共享內存的地址。
第三個參數,shm_flg是一組標志位,通常為0。

調用成功時返回一個指向共享內存第一個字節的指針,如果調用失敗返回-1.

3、shmdt函數
該函數用于將共享內存從當前進程中分離。注意,將共享內存分離并不是刪除它,只是使該共享內存對當前進程不再可用。它的原型如下:
[cpp] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. int?shmdt(const?void?*shmaddr);??
參數shmaddr是shmat函數返回的地址指針,調用成功時返回0,失敗時返回-1.

4、shmctl函數
與信號量的semctl函數一樣,用來控制共享內存,它的原型如下:
[cpp] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  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結構至少包括以下成員:
[cpp] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  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的源代碼如下:
[cpp] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  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的源代碼如下:
[cpp] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. #include?<unistd.h>??
  2. #include?<stdlib.h>??
  3. #include?<stdio.h>??
  4. #include?<sys/shm.h>??
  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的源代碼如下:
[cpp] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. #include?<unistd.h>??
  2. #include?<stdlib.h>??
  3. #include?<stdio.h>??
  4. #include?<string.h>??
  5. #include?<sys/shm.h>??
  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、 缺點:共享內存沒有提供同步的機制,這使得我們在使用共享內存進行進程間通信時,往往要借助其他的手段來進行進程間的同步工作

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

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

相關文章

laravel擴展包開發步驟總結

1. 創建包1php artisan workbench vendor/package --resources注: vendor:開發商名 package:包名2.修改下包里composer.json中的authors123456"authors": [{"name": "cicl","email": "test126.com"}]3. 為創建的包注冊Se…

洛谷 P1340 獸徑管理

題目描述 約翰農場的牛群希望能夠在 N 個(1<N<200) 草地之間任意移動。草地的編號由 1到 N。草地之間有樹林隔開。牛群希望能夠選擇草地間的路徑&#xff0c;使牛群能夠從任一 片草地移動到任一片其它草地。 牛群可在路徑上雙向通行。 牛群并不能創造路徑&#xff0c;但是…

功放關鍵規格參數檢查

編號規格備注1功放類型(開環/閉環)影響電性能指標2工作電壓(V)影響IC的穩定性3最大耐壓(V)影響IC的穩定性4最小負載(ohm)穩定性&#xff0c;過流&#xff0c;輸出功率&#xff0c;散熱5輸出功率過流&#xff0c;輸出功率&#xff0c;散熱6輸出方式(SE/BTL/PBTL)輸出功率&#x…

不踩雷不將就 京東智能產品30天無憂退

剁手節已經來臨&#xff0c;鋪天蓋地的促銷信息讓人應接不暇&#xff0c;恰好又是換季&#xff0c;確實需要買買買一波了。各種滿減活動讓人眼花繚亂&#xff0c;這波堪稱全年最大力度的促銷活動&#xff0c;令人是又喜又怕。倘若之前踩過雷的朋友&#xff0c;必然現在會謹慎許…

Linux進程間通信——使用信號量

//轉自http://blog.csdn.net/ljianhui/article/details/10243617 這篇文章將講述別一種進程間通信的機制——信號量。注意請不要把它與之前所說的信號混淆起來&#xff0c;信號與信號量是不同的兩種事物。有關信號的更多內容&#xff0c;可以閱讀我的另一篇文章&#xff1a;L…

麥克風陣列音頻檢查方法和標準

為確保產品能夠符合算法要求&#xff0c;務必提前做好相關設計&#xff0c;盡量確保各項指標滿足如下標準。 音頻評測工作&#xff0c;主要集中在研發設計階段&#xff1b;針對產品形態的不同&#xff0c;測試可分為裸板測試和整機測 試&#xff0c;下表為不同階段需要測試的…

Linux下git的使用——將已有項目放到github上

本地已經有一個項目了&#xff0c;需要將該項目放到github上&#xff0c;怎么操作&#xff1f; 步驟&#xff1a; 本地安裝git&#xff0c;有github賬號是前提。 &#xff08;1&#xff09;先在github創建一個空的倉庫&#xff0c;并復制鏈接地址。使用https&#xff0c;以.git…

SVG格式圖片轉成HTML中SVG的Path路徑

AI圖標制作完成之后&#xff0c;保存的svg文件包含許多AI的信息&#xff0c;如果要在HTML中使用&#xff0c;我們需要在svg文件中提取/修改信息&#xff0c;重新保存。 1、在AI中已經完成圖標&#xff0c;要保存SVG文件&#xff0c;點擊“文件(File)”-“另存為(Save As)”&…

11-5 筆記

函數&#xff1a; 函數在調用的時候&#xff0c;會形成一個私有作用域&#xff0c;內部的變量不會被外面訪問&#xff0c;這種保護機制叫閉包。這就意味著函數調用完畢&#xff0c;這個函數形成的棧內存會被銷毀。 函數歸屬誰跟它在哪調用沒有關系&#xff0c;跟在哪定義有關。…

linux下socket連接下的心跳機制

1&#xff0c;在長連接下&#xff0c;有可能很長一段時間都沒有數據往來。 理論上說&#xff0c;這個連接是一直保持連接的&#xff0c;但是實際情況中&#xff0c;如果中間節點出現什么故障是難以知道的。 有的節點&#xff08;防火墻&#xff09;會自動把一定時間之內沒有數…

大力智能臺燈與飛利浦臺燈 智能調光功能體驗

目前市面上絕大部分智能臺燈幾乎都宣稱有自動調光功能&#xff0c;即臺燈隨環境光變化自動調節LED光的亮度&#xff0c;或者臺燈在固定環境光下&#xff0c;一旦開啟了自動調光模式LED燈將自動調光至一個最適合讀寫作業的亮度&#xff1b; 下面對比體驗了大力臺燈T6 和 飛利浦…

php-驗證碼

<html><body> <h2>用戶注冊&#xff1a;</h2> <br> <form action"a.php" method"post"> 賬 號&#xff1a;<input type"text" name"zh" id""> <br> 密 碼&#xff1a;&l…

Linux內核Socket參數調優

可調優的內核變量存在兩種主要接口&#xff1a;sysctl命令和/proc文件系統&#xff0c;proc中與進程無關的所有信息都被移植到sysfs中。IPV4協議棧的sysctl參數主要是sysctl.net.core、sysctl.net.ipv4&#xff0c;對應的/proc文件系統是/proc/sys/net/ipv4和/proc/sys/net/cor…

Mango 的組織重構

為了提高敏捷性&#xff0c;企業應將自己劃分為一些負責業務戰略計劃價值中心&#xff0c;承擔端到端的責任&#xff0c;并完全獲取有關客戶需求的信息。企業需要為員工營造可交叉協作的空間&#xff0c;可以學習和使用自組織的改進圈、實踐社群&#xff08;CoP&#xff0c;Com…

vue.js單頁面應用實例

一&#xff1a;npm的安裝由于新版的node.js已經集成了npm的環境&#xff0c;所以只需去官網下載node.js并安裝&#xff0c;安裝完成后使用cmd檢測是否成功。測試node的版本號&#xff1a;node -v測試npm的版本號&#xff1a;npm -v以上提示代表安裝成功二&#xff1a;vue.js環境…

大屏拼接控制器的發展歷程與現狀分析

【中國數字視聽網訊】在大屏幕拼接系統中&#xff0c;我們很容易將焦點聚集在拼接單元上&#xff0c;殊不知幕后還有一個英雄在默默的支持&#xff0c;這個英雄就是是拼接控制器。拼接控制器的優劣直接決定著整個大屏幕顯示系統效果的好與壞&#xff0c;也決定了整套顯示系統的…

AA級與AAA級臺燈 重要指標對比

讀寫作業臺燈&#xff0c;按照國家標準&#xff08;GB/T 9473-2017 讀寫作業臺燈性能要求 &#xff09;臺燈只有兩個等級 即為A級和AA級&#xff1a; 但是大家在各個購物網站挑選臺燈尤其是挑選孩子學習用的讀寫臺燈時&#xff0c;會發現很多廠家宣稱臺 燈為AAA級&#xff0c…

windbg調試相關命令

windbg查找函數:x exe!main*條件斷點打印字符&#xff1a;bp 7199a2b0 ".printf \"message:%ma\", poi(esp8);.echo;g"//搜索內存:s -a 0000000000780000 L8000000 "This is a test2"!address,可以查看進程的堆布局&#xff0c;堆屬性。!addre…

零基礎學python,看完這篇文章,你的python基礎就差不多了!干貨【1】

2019獨角獸企業重金招聘Python工程師標準>>> Python基礎語法和面向對象&#xff08;下一篇分享面向對象&#xff09; Python基礎語法 1. 認識Python 1.1 Python 簡介 Python 的創始人為吉多范羅蘇姆&#xff08;Guido van Rossum&#xff09;。 Python 的設計目標&a…

消費類電子認證測試資料清單

消費類電子上市前必須取得相關認證&#xff0c;其中最常見的有3C、SRRC和CTA等強制性認證&#xff0c;還有類似TUV和Rohs等自愿性認證&#xff0c;現將常見認證測試資料清單小結如下&#xff1a; CCC測試認證&#xff1a; 測試項&#xff1a;EMC、安規和隨機等。 SRRC核準&am…