使用訊號量進行同步的共享記憶體機制

在南京看海發表於2016-07-11

一、簡介

共享記憶體為在多個程式之間共享和傳遞資料提供了一種有效的方式。

但它本身並未提供同步機制。

在實際程式設計中,可以使用

  訊號量,

  傳遞訊息(使用管道或IPC訊息),

  生成訊號,

  條件變數,

等方法來提供讀寫之間的有效的同步機制。

 

本例程式使用訊號量進行同步,

主要是因為它方便,使用廣泛,且獨立於程式。

 

本例程式實現了,

生產者程式:

  每次讀取YUV輸入檔案的一幀,

  然後將其寫到共享記憶體中。

消費者程式:

  每次從共享記憶體中讀到一幀,

  處理後,

  將其寫到輸出檔案。

兩個程式間使用訊號量來保證同步處理每一幀。

 

本例程式很好地示範了共享記憶體和訊號量的機制,

對於實際程式的開發很有意義。

 

二、生產者程式

common.h

用來設定一些測試用的基本引數。

  1. /*
  2.  * \File
  3.  * common.h
  4.  */
  5. #ifndef __COMMON_H__
  6. #define __COMMON_H__
  7.  
  8. #define TEST_FILE "coastguard_cif.yuv"
  9. #define OUTPUT_FILE "coastguard_cif_trsd.yuv"
  10.  
  11. #define FYUV_WIDTH 352
  12. #define FYUV_HEIGHT 288
  13.  
  14. #endif

 

共享記憶體相關的標頭檔案。

shm_com.h

  1. /*
  2.  * \File
  3.  * shm_com.h
  4.  * \Brief
  5.  */
  6. #ifndef __SHM_COM_H__
  7. #define __SHM_COM_H__
  8.  
  9. #define SHM_SEED 1001
  10. #define MAX_SHM_SIZE 2048*2048*
  11.  
  12. typedef struct shared_use_st
  13. {
  14.   int end_flag;              //用來標記程式間的記憶體共享是否結束: 0, 未結束; 1, 結束
  15.   char shm_sp[MAX_SHM_SIZE]; //共享記憶體的空間
  16. }shared_use_st;
  17.  
  18. #endif

 

訊號量的標頭檔案

semaphore.h

  1. /*
  2.  * \File
  3.  * semaphore.h
  4.  * \Brief
  5.  * semaphore operation
  6.  */
  7. #ifndef __SEMAPHORE_H__
  8. #define __SEMAPHORE_H__
  9.  
  10. #define SEM_SEED 1000
  11.  
  12. union semun
  13. {
  14.   int val;
  15.   struct semid_ds *buf;
  16.   unsigned short *array;
  17. };
  18.  
  19. int set_semvalue(int sem_id, int value);
  20. void del_semvalue(int sem_id);
  21. int semaphore_p(int sem_id);
  22. int semaphore_v(int sem_id);
  23.  
  24. #endif

 

幀處理的標頭檔案

frame.h

  1. /*
  2.  * \File
  3.  * frame.h
  4.  * \Brief
  5.  *
  6.  */
  7. #ifndef __FRAME_H__
  8. #define __FRAME_H__
  9.  
  10. #include "shm_com.h"
  11.  
  12. #define PIX_VALUE 1
  13.  
  14. typedef enum 
  15. {
  16.   YUV420,
  17.   YUV422,
  18.   YUV444,
  19.   RGB
  20. }frame_type_e;
  21.  
  22. typedef struct 
  23. {
  24.   int frm_w; // width
  25.   int frm_h; // height
  26.   frame_type_e frm_type; 
  27.   int frm_size;
  28.  
  29.   char *frm_comps;
  30. }frame_t, *frame_p;
  31.  
  32.  
  33. int init_frame(frame_p frame, int width, int height, frame_type_e type);
  34. int free_frame(frame_p frame);
  35.  
  36. int read_frame_from_file(frame_p frame, FILE* fp);
  37. int write_frame_into_file(FILE* fp, frame_p frame);
  38.  
  39. int read_frame_from_shm(frame_p frame, shared_use_st* shm);
  40. int write_frame_into_shm(shared_use_st* shm, frame_p frame);
  41.  
  42. int crop_frame(frame_p frame, int l_offset, int t_offset, int c_w, int c_h);
  43.  
  44. #endif

 

生產者程式的基本流程:

開啟輸入檔案並初始化幀;

建立並初始化共享記憶體和訊號量;

然後每次讀取一幀,

  用訊號量獲取共享記憶體的許可權後,

  將讀取的幀寫入共享記憶體,

  再釋放共享記憶體的許可權。

當處理完所有的幀後,

設定結束標誌,

並釋放相關的資源。

producer.c

  1. /*
  2.  * \File
  3.  * producer.c
  4.  * \Brief
  5.  * Test shared-memory and message-queue
  6.  */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <errno.h>
  11. #include <unistd.h>
  12.  
  13. #include <sys/shm.h>
  14. #include <sys/sem.h>
  15.  
  16. #include "common.h"
  17. #include "semaphore.h"
  18. #include "shm_com.h"
  19. #include "frame.h"
  20.  
  21.  
  22. int main(char argc, char* argv[])
  23. {
  24.   FILE* fp_in = NULL;
  25.   frame_t frame;
  26.   int frame_cnt = 0;
  27.  
  28.   int sem_id; // semaphore id
  29.  
  30.   int shm_id; // shared-memory id
  31.   void* shared_memory = (void*)0;
  32.   shared_use_st* shared_stuff;
  33.  
  34.   /* Open input file */
  35.   if ((fp_in = fopen(TEST_FILE, "rb")) < 0 )
  36.   {
  37.     printf("Open input file failed: %s\n", TEST_FILE);
  38.     exit(EXIT_FAILURE);
  39.   }
  40.  
  41.   /* Init frame */
  42.   init_frame(&frame, FYUV_WIDTH, FYUV_HEIGHT, YUV420);
  43.   printf("FRAME: w = %d, h = %d\n", frame.frm_w, frame.frm_h);
  44.  
  45.   /* Create and init semaphore */
  46.   sem_id = semget((key_t)SEM_SEED, 1, 0666 | IPC_CREAT);
  47.   if (sem_id == -1)
  48.   {
  49.     fprintf(stderr, "semget failed.\n");
  50.     exit(EXIT_FAILURE);
  51.   }
  52.  
  53.   /* Init shared-memory */
  54.   shm_id = shmget((key_t)SHM_SEED, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
  55.   if (shm_id == -1)
  56.   {
  57.     fprintf(stderr, "shmget failed.\n");
  58.     exit(EXIT_FAILURE);
  59.   }
  60.  
  61.   shared_memory = shmat(shm_id, (void*)0, 0);
  62.   if (shared_memory == (void*)-1)
  63.   {
  64.     fprintf(stderr, "shmat failed.\n");
  65.     exit(EXIT_FAILURE);
  66.   }
  67.  
  68.   shared_stuff = (struct shared_use_st*)shared_memory;
  69.   shared_stuff->end_flag = 0;
  70.  
  71.   printf("FRAME_CNT: %d\n", frame_cnt);
  72.   set_semvalue(sem_id, 1);
  73.  
  74.   while (read_frame_from_file(&frame, fp_in) == 0)
  75.   {
  76.     semaphore_p(sem_id);
  77.  
  78.     /* Write it into shared memory */
  79.     write_frame_into_shm(shared_stuff, &frame);
  80.     shared_stuff->end_flag = 0;
  81.  
  82.     semaphore_v(sem_id);
  83.  
  84.     frame_cnt++;
  85.     printf("FRAME_CNT: %d\n", frame_cnt);
  86.   }
  87.   semaphore_p(sem_id);
  88.   shared_stuff->end_flag = 1;
  89.   semaphore_v(sem_id);
  90.  
  91.   /* over */
  92.   printf("\nProducer over!\n");
  93.   fclose(fp_in);
  94.   free_frame(&frame);
  95.   del_semvalue(sem_id);
  96.   if (shmdt(shared_memory) == -1)
  97.   {
  98.     fprintf(stderr, "shmdt failed.\n");
  99.     exit(EXIT_FAILURE);
  100.   }
  101.  
  102.   exit(EXIT_SUCCESS);
  103. }

 

三、消費者程式

消費者程式的基本流程:

開啟輸出檔案並初始化幀;

獲取共享記憶體和訊號量;

每次

  得到共享記憶體的許可權後,

  從共享記憶體中讀取一幀並獲得結束標誌

  進行幀處理,

  釋放共享記憶體的許可權。

直到結束標誌為真。

 

最後釋放相關的資源。

consumer.c

  1. /*
  2.  * \File
  3.  * consumer.c
  4.  * \Brief
  5.  * 
  6.  */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <errno.h>
  11. #include <unistd.h>
  12.  
  13. #include <sys/shm.h>
  14. #include <sys/sem.h>
  15.  
  16. #include "common.h"
  17. #include "semaphore.h"
  18. #include "shm_com.h"
  19. #include "frame.h"
  20.  
  21.  
  22. int main(char argc, char *argv[])
  23. {
  24.   FILE* fp_out = NULL;
  25.   frame_t frame;
  26.   int frame_cnt = 0;
  27.  
  28.   int sem_id; // semaphore id
  29.  
  30.   int shm_id; // shared-memory id
  31.   void* shared_memory = (void*)0;
  32.   shared_use_st* shared_stuff;
  33.   int end_flag = 0;
  34.  
  35.   /* Open output file */
  36.   if ((fp_out = fopen(OUTPUT_FILE, "wb")) < 0 )
  37.   {
  38.     printf("Open output file failed: %s\n", OUTPUT_FILE);
  39.     exit(EXIT_FAILURE);
  40.   }
  41.  
  42.   /* Init frame */
  43.   init_frame(&frame, FYUV_WIDTH, FYUV_HEIGHT, YUV420);    
  44.   printf("FRAME: w = %d, h = %d\n", frame.frm_w, frame.frm_h);
  45.  
  46.   /* Create semaphore */
  47.   sem_id = semget((key_t)SEM_SEED, 1, 0666 | IPC_CREAT);
  48.   if (sem_id == -1)
  49.   {
  50.     fprintf(stderr, "semget failed.\n");
  51.     exit(EXIT_FAILURE);
  52.   }
  53.  
  54.   /* Init shared-memory */
  55.   shm_id = shmget((key_t)SHM_SEED, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
  56.   if (shm_id == -1)
  57.   {
  58.     fprintf(stderr, "shmget failed.\n");
  59.     exit(EXIT_FAILURE);
  60.   }
  61.  
  62.   shared_memory = shmat(shm_id, (void*)0, 0);
  63.   if (shared_memory == (void*)-1)
  64.   {
  65.     fprintf(stderr, "shmat failed.\n");
  66.     exit(EXIT_FAILURE);
  67.   }
  68.   shared_stuff = (struct shared_use_st*)shared_memory;
  69.  
  70.   printf("FRAME_CNT: %d\n", frame_cnt);
  71.   /* 
  72.    * 必須先置0,
  73.    * 否則會因生產者程式的異常退出未釋放訊號量而導致程式出錯 
  74.    */
  75.   set_semvalue(sem_id, 0);
  76.  
  77.   do
  78.   {
  79.     semaphore_p(sem_id); 
  80.  
  81.     /* Read frame from shared-memory */
  82.     read_frame_from_shm(&frame, shared_stuff);    
  83.     end_flag = shared_stuff->end_flag;
  84.  
  85.     crop_frame(&frame, 10, 10, 40, 40);
  86.     write_frame_into_file(fp_out, &frame);
  87.  
  88.     semaphore_v(sem_id);
  89.  
  90.     frame_cnt++;
  91.     printf("FRAME_CNT: %d\n", frame_cnt);
  92.   } while(!end_flag);
  93.  
  94.   /* Over */
  95.   printf("\nConsumer over!\n");
  96.   fclose(fp_out);
  97.   free_frame(&frame);
  98.   if (shmdt(shared_memory) == -1)
  99.   {
  100.     fprintf(stderr, "shmdt failed.\n");
  101.     exit(EXIT_FAILURE);
  102.   }
  103.  
  104.   exit(EXIT_SUCCESS);
  105. }

 

 

四、訊號量函式和幀處理函式

訊號量函式

semaphore.c

  1. /*
  2.  * \File
  3.  * semaphore.c
  4.  * \Breif
  5.  * 
  6.  */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <unistd.h>
  10. #include <sys/sem.h>
  11.  
  12. #include "semaphore.h"
  13.  
  14. /* init semaphore by semctl */
  15. int set_semvalue(int sem_id, int value)
  16. {
  17.   union semun sem_union;
  18.     
  19.   sem_union.val = value;
  20.   if (semctl(sem_id, 0, SETVAL, sem_union) == -1)
  21.     return 1;
  22.  
  23.   return 0;
  24. }
  25.  
  26.  
  27. /* delete semaphore by sectl */
  28. void del_semvalue(int sem_id)
  29. {
  30.   union semun sem_union;
  31.  
  32.   if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
  33.     fprintf(stderr, "Failed to delete semaphore\n");
  34. }
  35.  
  36. /* P(v) */
  37. int semaphore_p(int sem_id)
  38. {
  39.   struct sembuf sem_b;
  40.   sem_b.sem_num = 0;
  41.   sem_b.sem_op = -1; /* P(v) */
  42.   sem_b.sem_flg = SEM_UNDO;
  43.  
  44.   if (semop(sem_id, &sem_b, 1) == -1)
  45.   {
  46.     fprintf(stderr, "semaphore_p failed\n");
  47.     return 1;
  48.   }
  49.  
  50.   return 0;
  51. }
  52.  
  53. /* V(v) */
  54. int semaphore_v(int sem_id)
  55. {
  56.   struct sembuf sem_b;
  57.  
  58.   sem_b.sem_num = 0;
  59.   sem_b.sem_op = 1; // V(v)
  60.   sem_b.sem_flg = SEM_UNDO;
  61.  
  62.   if (semop(sem_id, &sem_b, 1) == -1)
  63.   {
  64.     fprintf(stderr, "semaphore_v failed\n");
  65.     return 1;
  66.   }
  67.  
  68.   return 0;
  69. }

 

幀處理函式

frame.c

  1. /*
  2.  * \File
  3.  * frame.c
  4.  */
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <unistd.h>
  8. #include <memory.h>
  9. #include <assert.h>
  10. #include <errno.h>
  11.  
  12. #include "frame.h"
  13.  
  14. /*
  15.  * \Brief
  16.  * init frame
  17.  */
  18. int init_frame(frame_p frame, int width, int height, frame_type_e type)
  19. {
  20.   assert(frame != NULL);
  21.         
  22.   frame->frm_w = width;
  23.   frame->frm_h = height;
  24.   frame->frm_type = type;
  25.  
  26.   switch(frame->frm_type)
  27.   {
  28.   case YUV420:
  29.     frame->frm_size = (frame->frm_w * frame->frm_h * 3) / 2;
  30.     break;    
  31.   case YUV422:
  32.     frame->frm_size = frame->frm_w * frame->frm_h * 2; 
  33.     break;
  34.   case YUV444:
  35.     frame->frm_size = frame->frm_w * frame->frm_h * 3; 
  36.     break;    
  37.   case RGB:
  38.     frame->frm_size = frame->frm_w * frame->frm_h * 3;
  39.     break;
  40.   default:
  41.     fprintf(stderr, "frame type is invalid.");
  42.     return 1; 
  43.   }
  44.  
  45.   if ((frame->frm_comps = (char*)calloc(frame->frm_size, sizeof(char))) == NULL)
  46.   {
  47.     fprintf(stderr, "calloc failed.");
  48.     return 1;
  49.   } 
  50.  
  51.   return 0;
  52. }
  53.  
  54. /*
  55.  * \Brief
  56.  * init frame
  57.  */
  58. int free_frame(frame_p frame)
  59. {
  60.   assert(frame != NULL);
  61.  
  62.   free(frame->frm_comps);
  63.   return 0;
  64. }
  65.   
  66.  
  67. /*
  68.  * \Brief
  69.  * read a frame from file
  70.  */
  71. int read_frame_from_file(frame_p frame, FILE* fp)
  72. {
  73.   int ret;
  74.   assert(frame != NULL && fp != NULL);
  75.  
  76.   if (ret = (fread(frame->frm_comps, sizeof(char), frame->frm_size, fp)) 
  77.      != frame->frm_size)
  78.   {
  79.     fprintf(stderr, "read_frame_from_file failed. %d\n", ret);
  80.     return 1;
  81.   }
  82.  
  83.   return 0;
  84. } 
  85.  
  86.  
  87. /*
  88.  * \Brief
  89.  * write a frame into file
  90.  */
  91. int write_frame_into_file(FILE* fp, frame_p frame)
  92. {
  93.   assert(frame != NULL && fp != NULL);
  94.  
  95.   if (fwrite(frame->frm_comps, sizeof(char), frame->frm_size, fp)
  96.       != frame->frm_size)
  97.   {
  98.     fprintf(stderr, "write_frame_into_file failed.\n");
  99.     return 1;
  100.   }
  101.  
  102.   return 0;
  103. } 
  104.  
  105. /*
  106.  * \Brief
  107.  * read a frame from shared-memory
  108.  */
  109. int read_frame_from_shm(frame_p frame, shared_use_st* shm)
  110. {
  111.   assert(frame != NULL && shm != NULL);
  112.  
  113.   memcpy((char*)frame->frm_comps, (char*)shm->shm_sp, frame->frm_size);
  114.  
  115.   return 0;
  116. } 
  117.  
  118. /*
  119.  * \Brief
  120.  * write a frame into shared-memory
  121.  */
  122. int write_frame_into_shm(shared_use_st* shm, frame_p frame) 
  123. {
  124.   assert(frame != NULL && shm != NULL);
  125.  
  126.   memcpy((char*)shm->shm_sp, (char*)frame->frm_comps, frame->frm_size);
  127.  
  128.   return 0;
  129. } 
  130.  
  131.  
  132.  
  133. /*
  134.  * \Brief
  135.  * crop frame
  136.  */
  137. int crop_frame(frame_p frame, int l_offset, int t_offset, int c_w, int c_h)
  138. {
  139.   char* crop_loc;
  140.   int index_h;
  141.  
  142.   assert(frame != NULL);
  143.  
  144.   if ((l_offset + c_w) > frame->frm_w)
  145.   {
  146.     printf("Crop width is out of range.\n");
  147.     return 1;
  148.   }
  149.   if ((t_offset + c_h) > frame->frm_h)
  150.   {
  151.     printf("Crop height is out of range.\n");
  152.     return 1;
  153.   }
  154.  
  155.   crop_loc = frame->frm_comps + (t_offset * frame->frm_w) + l_offset; 
  156.   for (index_h = 0; index_h < c_h; index_h++)
  157.   {
  158.     memset(crop_loc, PIX_VALUE, c_w);
  159.  
  160.     crop_loc += frame->frm_w;
  161.   }
  162.  
  163.   return 0;
  164. }

 

 

五、編譯與執行

 

makefile.producer

  1. OBJECTS = producer.o semaphore.o frame.o
  2. CC = gcc
  3. CFLAG = --Wa 
  4.  
  5. producer : $(OBJECTS)
  6.   $(CC) $(CFLAG) -o producer $(OBJECTS)
  7.  
  8. producer.o: common.h semaphore.h frame.h shm_com.h
  9. semaphore.o:semaphore.h
  10. frame.o: frame.h
  11.  
  12. .PHONY:clean
  13. clean:
  14.   rm producer $(OBJECTS)

 

makefile.consumer

  1. OBJECTS = consumer.o semaphore.o frame.o
  2. CC = gcc
  3. CFLAG = --Wa
  4.  
  5. consumer : $(OBJECTS)
  6.   $(CC) $(CFLAG) -o consumer $(OBJECTS)
  7.  
  8. consumer.o: common.h semaphore.h frame.h shm_com.h
  9. semaphore.o:semaphore.h
  10. frame.o:frame.h
  11.  
  12. .PHONY:clean
  13. clean:
  14.   rm consumer $(OBJECTS)

 

編譯與執行:

  1. $make -f makefile.producer
  2. $make -f makefile.consumer
  3. $producer &

 

  1. $consumer

 

轉自 http://blog.chinaunix.net/uid-26000296-id-3442827.html

 

相關文章