處理序間通訊---共用記憶體

來源:互聯網
上載者:User

處理序間通訊---共用記憶體

進程間的通訊方式我們都熟悉,管道(具名管道)、訊號(signal)、共用記憶體、訊息佇列、通訊端(socket),關於訊號量個人認為應該歸為進程間的同步機制裡。
下面我們就說說共用記憶體的通訊方式。 它是IPC中最快的。一旦記憶體區映射到共用它的進程的地址空間,這些進程間資料的傳遞就不在涉及核心。但是存取資料的時候需要保持同步. 關於共用記憶體權威的參考資料為《unix網路編程卷2》. 在前面我們講解了mmap的核心機制,在posix v的共用記憶體的方式就是這個原理。system v也類似,只不過對介面進行了封裝.
我們先從posix v的方式開始說起,它常用的函數介面:
open/shm_open + mmap 、munmap、msync、shm_unlink等.
既然是open那麼就會對應檔案,當然並不是所有的檔案都可以映射.這裡支援的檔案類型:
1 .普通的磁碟檔案
2. 裝置檔案(除去一些特殊的檔案)
3.記憶體檔案 (例如tmpfs)
4. 匿名映射 (主要用在具有親緣關係的進程間)
先說一下常用的匿名映射,直接open /dev/zero保證映射地區都初始化為0 的匿名映射。 還有可以直接用mmap來映射,而不用開啟任何檔案:

  1. mmap( NULL,size_of_map,PROT_READ|PROT_WRITE,MAP_SHARED | MAP_ANON, -1,0 );
這裡提示一下mmap的函數原型:
  1. void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
匿名映射主要區別是flags 裡多了一個 MAP_ANON標誌,和fd傳遞的為-1. 關於更多mmap的資料請自行查詢.
下面說一下一般mmap方式的共用記憶體需要注意的:
映射的記憶體地區大小和檔案大小,記憶體映射的大小都是頁面的整數倍(PAGESIZE 4096預設) 比如檔案大小為5000,映射了5000 ,那麼可以訪問2*PAGESIZE的記憶體地區,但是大於5000的記憶體寫不會同步到檔案裡。這個記憶體可以訪問的大小跟檔案大小也是有關係的。映射的時候會自動檢查底層的支援即檔案的大小. 但是我們知道mmap的方式的優點就是可以動態改變檔案的大小,函數介面為ftruncate和fstat. 具體用法參考unix網路編程卷2.

下面就簡單總結下它的 特點:
1. 可以隨時改變其大小
操作函數介面:ftruncate 和fstat
2. 必須先建立開啟一個檔案為基礎 ,這個檔案可以是磁碟檔案 或者記憶體檔案比如tmpfs下的檔案;如果共用記憶體的配置需要寫入到磁碟 ,可以選擇開啟磁碟檔案。如果在嵌入式裡用一般會把flash一個檔案作為映射 ,既然需要頻繁的操作檔案 和磁碟讀寫 所以建議還是用shm。 畢竟flash頻繁讀寫影響壽命和容易出現壞塊。

而system v的方式不需要明確的open檔案,但是函數操作介面比較多.
函數介面: shmget、shmat、shmdt、shmctl等。
shm方式有些限制:
shmax 一個共用記憶體區的最大位元組數 (具體系統不太一樣)
shmmnb 一個共用記憶體區的最小位元組數 1
shmmni 系統範圍最大共用記憶體區標識數 128
shmseg 每個進程附接的最大共用記憶體區數 32
這些資訊可以通過proc檔案系統來查看。cat /proc/sys/kernel/shmmax 等. 具體查看映射的記憶體區 可以通過ipcs命令來查看和操作.

我們也總結下它的特點:
1.不需要建立和開啟檔案
2. 如果是多處理序間通訊,需要統一 一個key標示 。動態產生的不一定一致。
3.讀寫的速度高於mmap的方式
4. 共用記憶體空間不能太大,畢竟直接佔用記憶體空間.
不論哪種方式,都會涉及多進程間的通訊、資料互動什麼的,那麼就必須保持互斥和同步,這裡最常用的方式是採用訊號量的方式.
訊號量的介面:sem_init、sem_wait、sem_post. 由於用了訊號量編譯的時候會需要連結線程庫 -lpthread.
下面就具體程式碼範例看看它們的具體用法:
posix v方式:
1. 初始化,之後進入迴圈不停的寫,周期為1s。
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include


  6. #define CLUSTER_SHARED_FILE "/tmp/cluster_share"

  7. typedef struct{
  8. char name[4];
  9. int age;
  10. }people;

  11. struct stu {

  12. sem_t mutex;
  13. people s[10];

  14. };

  15. struct stu t;

  16. main(int argc, char** argv) // map a normal file as shared mem:
  17. {
  18. int fd,i;
  19. struct stu *p_map;
  20. char temp;

  21. fd=open(CLUSTER_SHARED_FILE,O_CREAT|O_RDWR|O_TRUNC,00777);
  22. lseek(fd,sizeof(t)-1,SEEK_SET);
  23. write(fd,"",1);

  24. p_map = (struct stu *) mmap( NULL,sizeof(t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
  25. close(fd);
  26. temp = 'a';
  27. sem_init(&p_map->mutex,1,1);
  28. int j=0;
  29. while(1)
  30. {
  31. j++;
  32. printf("j 1\n");
  33. sem_wait(&p_map->mutex);
  34. printf("j 2\n");
  35. for(i=1; i<10; i++)
  36. {
  37. // temp += 1;

  38. memcpy(p_map->s[i].name, &temp,2 );
  39. p_map->s[i].age = p_map->s[i].age +20+i+j;
  40. }
  41. printf("j 3\n");
  42. sem_post(&p_map->mutex);
  43. printf("j 4\n");
  44. sleep(1);
  45. }
  46. printf(" initialize over \n ");
  47. sleep(50);
  48. munmap( p_map, sizeof(t) );
  49. printf( "umap ok \n" );
  50. }
2. 讀取操作:
  1. #include
  2. #include
  3. #include
  4. #include

  5. #include

  6. #include

  7. #define CLUSTER_SHARED_FILE "/tmp/cluster_share"

  8. typedef struct{
  9. char name[4];
  10. int age;
  11. }people;

  12. struct stu {

  13. sem_t mutex;
  14. people s[10];
  15. };

  16. struct stu t;

  17. main(int argc, char** argv) // map a normal file as shared mem:
  18. {
  19. int fd,i;
  20. struct stu *p_map;
  21. char temp;

  22. fd=open(CLUSTER_SHARED_FILE,O_CREAT|O_RDWR,00777);
  23. // lseek(fd,sizeof(people)*5-1,SEEK_SET);
  24. // write(fd,"",1);

  25. p_map = (struct stu *) mmap( NULL,sizeof(t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
  26. close( fd );

  27. while(1)
  28. {
  29. sleep(1);
  30. printf("2.....s\n");
  31. sem_wait(&p_map->mutex);
  32. printf("2.....\n");
  33. for(i=0; i<10; i++)
  34. {
  35. printf("name:%s,age:%d\n",p_map->s[i].name,p_map->s[i].age);

  36. }
  37. printf("2.....0\n");
  38. sem_post(&p_map->mutex);
  39. printf("2.......1");
  40. }
  41. munmap( p_map, sizeof(t));
  42. printf( "umap ok \n" );
  43. }
至於並非的寫操作,程式稍微修改一下就可以測試. 多並發沒有問題.
system V的方式,雖然介面不太一樣,但是也很類似。
1. 初始化
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include

  7. //#include "shmdata.h"




  8. #define TEXT_SZ 2048

  9. struct shared_use_st
  10. {
  11. sem_t mutex;
  12. int written;//作為一個標誌,非0:表示可讀,0表示可寫
  13. char text[TEXT_SZ];//記錄寫入和讀取的文本
  14. int cnt;
  15. };


  16. int main()
  17. {
  18. int running = 1;
  19. void *shm = NULL;
  20. struct shared_use_st *shared = NULL;
  21. char buffer[4096]="hello world";
  22. int shmid;
  23. int i=0;
  24. //建立共用記憶體
  25. #if 0
  26. //刪除共用記憶體
  27. if(shmctl(shmid, IPC_RMID, 0) == -1)
  28. {
  29. fprintf(stderr, "shmctl(IPC_RMID) failed\n");
  30. exit(EXIT_FAILURE);
  31. }
  32. #endif
  33. shmid = shmget((key_t)12345, sizeof(struct shared_use_st), 0666|IPC_CREAT);
  34. if(shmid == -1)
  35. {
  36. fprintf(stderr, "shmget failed\n");
  37. exit(EXIT_FAILURE);
  38. }
  39. //將共用記憶體串連到當前進程的地址空間
  40. shm = shmat(shmid, (void*)0, 0);
  41. if(shm == (void*)-1)
  42. {
  43. fprintf(stderr, "shmat failed\n");
  44. exit(EXIT_FAILURE);
  45. }
  46. printf("Memory attached at %X\n", (int)shm);
  47. //設定共用記憶體
  48. shared = (struct shared_use_st*)shm;
  49. sem_init(&shared->mutex,1,1);

  50. while(running)//向共用記憶體中寫資料
  51. {
  52. strncpy(shared->text, buffer, TEXT_SZ);

  53. sem_wait(&shared->mutex);
  54. //shared->cnt = 0;
  55. shared->cnt = shared->cnt +i;
  56. sem_post(&shared->mutex);
  57. i++;
  58. shared->written = 1;

  59. sleep(1);
  60. }
  61. //把共用記憶體從當前進程中分離
  62. if(shmdt(shm) == -1)
  63. {
  64. fprintf(stderr, "shmdt failed\n");
  65. exit(EXIT_FAILURE);
  66. }
  67. sleep(2);
  68. exit(EXIT_SUCCESS);
  69. }
2. 讀取操作
  1. #include
  2. #include
  3. #include
  4. #include
  5. //#include "shmdata.h"
  6. #include




  7. #define TEXT_SZ 2048

  8. struct shared_use_st
  9. {
  10. sem_t mutex;
  11. int written;//作為一個標誌,非0:表示可讀,0表示可寫
  12. char text[TEXT_SZ];//記錄寫入和讀取的文本
  13. int cnt;
  14. };

  15. int main()
  16. {
  17. int running = 1;//程式是否繼續啟動並執行標誌
  18. void *shm = NULL;//分配的共用記憶體的原始首地址
  19. struct shared_use_st *shared;//指向shm
  20. int shmid;//共用記憶體標識符
  21. //建立共用記憶體
  22. shmid = shmget((key_t)12345, sizeof(struct shared_use_st), 0666);
  23. if(shmid == -1)
  24. {
  25. fprintf(stderr, "shmget failed\n");
  26. exit(EXIT_FAILURE);
  27. }
  28. //將共用記憶體串連到當前進程的地址空間
  29. shm = shmat(shmid, 0, 0);
  30. if(shm == (void*)-1)
  31. {
  32. fprintf(stderr, "shmat failed\n");
  33. exit(EXIT_FAILURE);
  34. }
  35. printf("\nMemory attached at %X\n", (int)shm);
  36. //設定共用記憶體
  37. shared = (struct shared_use_st*)shm;
  38. // shared->written = 0;
  39. while(running)//讀取共用記憶體中的資料
  40. {

  41. sem_wait(&shared->mutex);
  42. printf("text:%s,cnt:%d\n",shared->text,shared->cnt);
  43. sem_post(&shared->mutex);
  44. sleep(1);


  45. }
  46. //把共用記憶體從當前進程中分離
  47. if(shmdt(shm) == -1)
  48. {
  49. fprintf(stderr, "shmdt failed\n");
  50. exit(EXIT_FAILURE);
  51. }
  52. #if 0
  53. //刪除共用記憶體
  54. if(shmctl(shmid, IPC_RMID, 0) == -1)
  55. {
  56. fprintf(stderr, "shmctl(IPC_RMID) failed\n");
  57. exit(EXIT_FAILURE);
  58. }
  59. #endif
  60. exit(EXIT_SUCCESS);
  61. }

整體的用法還算比較簡單. 也很高效方便.








聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.