linux进程间的通信(C): 使用信号量进行同步的共享内存机制

 
一、简介
共享内存为在多个进程之间共享和传递数据提供了一种有效的方式。
但它本身并未提供同步机制。
在实际编程中,可以使用
  信号量,
  传递消息(使用管道或IPC消息),
  生成信号,
  条件变量,
等方法来提供读写之间的有效的同步机制。
 
本例程序使用信号量进行同步,
主要是因为它方便,使用广泛,且独立于进程。
 
本例程序实现了,
生产者进程:
  每次读取YUV输入文件的一帧,
  然后将其写到共享内存中。
消费者进程:
  每次从共享内存中读到一帧,
  处理后,
  将其写到输出文件。
两个进程间使用信号量来保证同步处理每一帧。
 
本例程序很好地示范了共享内存和信号量的机制,
对于实际程序的开发很有意义。
 
二、生产者进程
common.h
用来设置一些测试用的基本参数。
  1. /*
  2.  * \File
  3.  * common.h
  4.  */
  5. #ifndef __COMMON_H__
  6. #define __COMMON_H__
  7. #define TEST_FILE "coastguard_cif.yuv"
  8. #define OUTPUT_FILE "coastguard_cif_trsd.yuv"
  9. #define FYUV_WIDTH 352
  10. #define FYUV_HEIGHT 288
  11. #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. #define SHM_SEED 1001
  9. #define MAX_SHM_SIZE 2048*2048*3 
  10. typedef struct shared_use_st
  11. {
  12.   int end_flag;              //用来标记进程间的内存共享是否结束: 0, 未结束; 1, 结束
  13.   char shm_sp[MAX_SHM_SIZE]; //共享内存的空间
  14. }shared_use_st;
  15. #endif
 
信号量的头文件
semaphore.h
  1. /*
  2.  * \File
  3.  * semaphore.h
  4.  * \Brief
  5.  * semaphore operation
  6.  */
  7. #ifndef __SEMAPHORE_H__
  8. #define __SEMAPHORE_H__
  9. #define SEM_SEED 1000
  10. union semun
  11. {
  12.   int val;
  13.   struct semid_ds *buf;
  14.   unsigned short *array;
  15. };
  16. int set_semvalue(int sem_id, int value);
  17. void del_semvalue(int sem_id);
  18. int semaphore_p(int sem_id);
  19. int semaphore_v(int sem_id);
  20. #endif
 
帧处理的头文件
frame.h
  1. /*
  2.  * \File
  3.  * frame.h
  4.  * \Brief
  5.  *
  6.  */
  7. #ifndef __FRAME_H__
  8. #define __FRAME_H__
  9. #include "shm_com.h"
  10. #define PIX_VALUE 1
  11. typedef enum 
  12. {
  13.   YUV420,
  14.   YUV422,
  15.   YUV444,
  16.   RGB
  17. }frame_type_e;
  18. typedef struct 
  19. {
  20.   int frm_w; // width
  21.   int frm_h; // height
  22.   frame_type_e frm_type; 
  23.   int frm_size;
  24.   char *frm_comps;
  25. }frame_t, *frame_p;
  26. int init_frame(frame_p frame, int width, int height, frame_type_e type);
  27. int free_frame(frame_p frame);
  28. int read_frame_from_file(frame_p frame, FILE* fp);
  29. int write_frame_into_file(FILE* fp, frame_p frame);
  30. int read_frame_from_shm(frame_p frame, shared_use_st* shm);
  31. int write_frame_into_shm(shared_use_st* shm, frame_p frame);
  32. int crop_frame(frame_p frame, int l_offset, int t_offset, int c_w, int c_h);
  33. #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. #include <sys/shm.h>
  13. #include <sys/sem.h>
  14. #include "common.h"
  15. #include "semaphore.h"
  16. #include "shm_com.h"
  17. #include "frame.h"
  18. int main(char argc, char* argv[])
  19. {
  20.   FILE* fp_in = NULL;
  21.   frame_t frame;
  22.   int frame_cnt = 0;
  23.   int sem_id; // semaphore id
  24.   int shm_id; // shared-memory id
  25.   void* shared_memory = (void*)0;
  26.   shared_use_st* shared_stuff;
  27.   /* Open input file */
  28.   if ((fp_in = fopen(TEST_FILE, "rb")) < 0 )
  29.   {
  30.     printf("Open input file failed: %s\n", TEST_FILE);
  31.     exit(EXIT_FAILURE);
  32.   }
  33.   /* Init frame */
  34.   init_frame(&frame, FYUV_WIDTH, FYUV_HEIGHT, YUV420);
  35.   printf("FRAME: w = %d, h = %d\n", frame.frm_w, frame.frm_h);
  36.   /* Create and init semaphore */
  37.   sem_id = semget((key_t)SEM_SEED, 1, 0666 | IPC_CREAT);
  38.   if (sem_id == -1)
  39.   {
  40.     fprintf(stderr, "semget failed.\n");
  41.     exit(EXIT_FAILURE);
  42.   }
  43.   /* Init shared-memory */
  44.   shm_id = shmget((key_t)SHM_SEED, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
  45.   if (shm_id == -1)
  46.   {
  47.     fprintf(stderr, "shmget failed.\n");
  48.     exit(EXIT_FAILURE);
  49.   }
  50.   shared_memory = shmat(shm_id, (void*)0, 0);
  51.   if (shared_memory == (void*)-1)
  52.   {
  53.     fprintf(stderr, "shmat failed.\n");
  54.     exit(EXIT_FAILURE);
  55.   }
  56.   shared_stuff = (struct shared_use_st*)shared_memory;
  57.   shared_stuff->end_flag = 0;
  58.   printf("FRAME_CNT: %d\n", frame_cnt);
  59.   set_semvalue(sem_id, 1);
  60.   while (read_frame_from_file(&frame, fp_in) == 0)
  61.   {
  62.     semaphore_p(sem_id);
  63.     /* Write it into shared memory */
  64.     write_frame_into_shm(shared_stuff, &frame);
  65.     shared_stuff->end_flag = 0;
  66.  
  67.     semaphore_v(sem_id);
  68.     frame_cnt++;
  69.     printf("FRAME_CNT: %d\n", frame_cnt);
  70.   }
  71.   semaphore_p(sem_id);
  72.   shared_stuff->end_flag = 1;
  73.   semaphore_v(sem_id);
  74.   /* over */
  75.   printf("\nProducer over!\n");
  76.   fclose(fp_in);
  77.   free_frame(&frame);
  78.   del_semvalue(sem_id);
  79.   if (shmdt(shared_memory) == -1)
  80.   {
  81.     fprintf(stderr, "shmdt failed.\n");
  82.     exit(EXIT_FAILURE);
  83.   }
  84.   exit(EXIT_SUCCESS);
  85. }
 
三、消费者进程
消费者进程的基本流程:
打开输出文件并初始化帧;
获取共享内存和信号量;
每次
  得到共享内存的权限后,
  从共享内存中读取一帧并获得结束标志
  进行帧处理,
  释放共享内存的权限。
直到结束标志为真。
 
最后释放相关的资源。
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. #include <sys/shm.h>
  13. #include <sys/sem.h>
  14. #include "common.h"
  15. #include "semaphore.h"
  16. #include "shm_com.h"
  17. #include "frame.h"
  18. int main(char argc, char *argv[])
  19. {
  20.   FILE* fp_out = NULL;
  21.   frame_t frame;
  22.   int frame_cnt = 0;
  23.   int sem_id; // semaphore id
  24.   int shm_id; // shared-memory id
  25.   void* shared_memory = (void*)0;
  26.   shared_use_st* shared_stuff;
  27.   int end_flag = 0;
  28.   /* Open output file */
  29.   if ((fp_out = fopen(OUTPUT_FILE, "wb")) < 0 )
  30.   {
  31.     printf("Open output file failed: %s\n", OUTPUT_FILE);
  32.     exit(EXIT_FAILURE);
  33.   }
  34.   /* Init frame */
  35.   init_frame(&frame, FYUV_WIDTH, FYUV_HEIGHT, YUV420);    
  36.   printf("FRAME: w = %d, h = %d\n", frame.frm_w, frame.frm_h);
  37.   /* Create semaphore */
  38.   sem_id = semget((key_t)SEM_SEED, 1, 0666 | IPC_CREAT);
  39.   if (sem_id == -1)
  40.   {
  41.     fprintf(stderr, "semget failed.\n");
  42.     exit(EXIT_FAILURE);
  43.   }
  44.   /* Init shared-memory */
  45.   shm_id = shmget((key_t)SHM_SEED, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
  46.   if (shm_id == -1)
  47.   {
  48.     fprintf(stderr, "shmget failed.\n");
  49.     exit(EXIT_FAILURE);
  50.   }
  51.   shared_memory = shmat(shm_id, (void*)0, 0);
  52.   if (shared_memory == (void*)-1)
  53.   {
  54.     fprintf(stderr, "shmat failed.\n");
  55.     exit(EXIT_FAILURE);
  56.   }
  57.   shared_stuff = (struct shared_use_st*)shared_memory;
  58.   printf("FRAME_CNT: %d\n", frame_cnt);
  59.   /* 
  60.    * 必须先置0,
  61.    * 否则会因生产者进程的异常退出未释放信号量而导致程序出错 
  62.    */
  63.   set_semvalue(sem_id, 0);
  64.   do
  65.   {
  66.     semaphore_p(sem_id); 
  67.     /* Read frame from shared-memory */
  68.     read_frame_from_shm(&frame, shared_stuff);    
  69.     end_flag = shared_stuff->end_flag;
  70.     crop_frame(&frame, 10, 10, 40, 40);
  71.     write_frame_into_file(fp_out, &frame);
  72.     semaphore_v(sem_id);
  73.     frame_cnt++;
  74.     printf("FRAME_CNT: %d\n", frame_cnt);
  75.   } while(!end_flag);
  76.   /* Over */
  77.   printf("\nConsumer over!\n");
  78.   fclose(fp_out);
  79.   free_frame(&frame);
  80.   if (shmdt(shared_memory) == -1)
  81.   {
  82.     fprintf(stderr, "shmdt failed.\n");
  83.     exit(EXIT_FAILURE);
  84.   }
  85.   exit(EXIT_SUCCESS);
  86. }
 
四、信号量函数和帧处理函数
信号量函数
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. #include "semaphore.h"
  12. /* init semaphore by semctl */
  13. int set_semvalue(int sem_id, int value)
  14. {
  15.   union semun sem_union;
  16.     
  17.   sem_union.val = value;
  18.   if (semctl(sem_id, 0, SETVAL, sem_union) == -1)
  19.     return 1;
  20.   return 0;
  21. }
  22. /* delete semaphore by sectl */
  23. void del_semvalue(int sem_id)
  24. {
  25.   union semun sem_union;
  26.   if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
  27.     fprintf(stderr, "Failed to delete semaphore\n");
  28. }
  29. /* P(v) */
  30. int semaphore_p(int sem_id)
  31. {
  32.   struct sembuf sem_b;
  33.   sem_b.sem_num = 0;
  34.   sem_b.sem_op = -1; /* P(v) */
  35.   sem_b.sem_flg = SEM_UNDO;
  36.   if (semop(sem_id, &sem_b, 1) == -1)
  37.   {
  38.     fprintf(stderr, "semaphore_p failed\n");
  39.     return 1;
  40.   }
  41.   return 0;
  42. }
  43. /* V(v) */
  44. int semaphore_v(int sem_id)
  45. {
  46.   struct sembuf sem_b;
  47.   sem_b.sem_num = 0;
  48.   sem_b.sem_op = 1; // V(v)
  49.   sem_b.sem_flg = SEM_UNDO;
  50.   if (semop(sem_id, &sem_b, 1) == -1)
  51.   {
  52.     fprintf(stderr, "semaphore_v failed\n");
  53.     return 1;
  54.   }
  55.   return 0;
  56. }
帧处理函数
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. #include "frame.h"
  12.  
  13. /*
  14.  * \Brief
  15.  * init frame
  16.  */
  17. int init_frame(frame_p frame, int width, int height, frame_type_e type)
  18. {
  19.   assert(frame != NULL);
  20.         
  21.   frame->frm_w = width;
  22.   frame->frm_h = height;
  23.   frame->frm_type = type;
  24.   switch(frame->frm_type)
  25.   {
  26.   case YUV420:
  27.     frame->frm_size = (frame->frm_w * frame->frm_h * 3) / 2;
  28.     break;    
  29.   case YUV422:
  30.     frame->frm_size = frame->frm_w * frame->frm_h * 2; 
  31.     break;
  32.   case YUV444:
  33.     frame->frm_size = frame->frm_w * frame->frm_h * 3; 
  34.     break;    
  35.   case RGB:
  36.     frame->frm_size = frame->frm_w * frame->frm_h * 3;
  37.     break;
  38.   default:
  39.     fprintf(stderr, "frame type is invalid.");
  40.     return 1; 
  41.   }
  42.   if ((frame->frm_comps = (char*)calloc(frame->frm_size, sizeof(char))) == NULL)
  43.   {
  44.     fprintf(stderr, "calloc failed.");
  45.     return 1;
  46.   } 
  47.   return 0;
  48. }
  49. /*
  50.  * \Brief
  51.  * init frame
  52.  */
  53. int free_frame(frame_p frame)
  54. {
  55.   assert(frame != NULL);
  56.   free(frame->frm_comps);
  57.   return 0;
  58. }
  59.   
  60. /*
  61.  * \Brief
  62.  * read a frame from file
  63.  */
  64. int read_frame_from_file(frame_p frame, FILE* fp)
  65. {
  66.   int ret;
  67.   assert(frame != NULL && fp != NULL);
  68.   if (ret = (fread(frame->frm_comps, sizeof(char), frame->frm_size, fp)) 
  69.      != frame->frm_size)
  70.   {
  71.     fprintf(stderr, "read_frame_from_file failed. %d\n", ret);
  72.     return 1;
  73.   }
  74.   return 0;
  75. /*
  76.  * \Brief
  77.  * write a frame into file
  78.  */
  79. int write_frame_into_file(FILE* fp, frame_p frame)
  80. {
  81.   assert(frame != NULL && fp != NULL);
  82.   if (fwrite(frame->frm_comps, sizeof(char), frame->frm_size, fp)
  83.       != frame->frm_size)
  84.   {
  85.     fprintf(stderr, "write_frame_into_file failed.\n");
  86.     return 1;
  87.   }
  88.   return 0;
  89. /*
  90.  * \Brief
  91.  * read a frame from shared-memory
  92.  */
  93. int read_frame_from_shm(frame_p frame, shared_use_st* shm)
  94. {
  95.   assert(frame != NULL && shm != NULL);
  96.   memcpy((char*)frame->frm_comps, (char*)shm->shm_sp, frame->frm_size);
  97.   return 0;
  98.  
  99. /*
  100.  * \Brief
  101.  * write a frame into shared-memory
  102.  */
  103. int write_frame_into_shm(shared_use_st* shm, frame_p frame) 
  104. {
  105.   assert(frame != NULL && shm != NULL);
  106.   memcpy((char*)shm->shm_sp, (char*)frame->frm_comps, frame->frm_size);
  107.   return 0;
  108. /*
  109.  * \Brief
  110.  * crop frame
  111.  */
  112. int crop_frame(frame_p frame, int l_offset, int t_offset, int c_w, int c_h)
  113. {
  114.   char* crop_loc;
  115.   int index_h;
  116.   assert(frame != NULL);
  117.   if ((l_offset + c_w) > frame->frm_w)
  118.   {
  119.     printf("Crop width is out of range.\n");
  120.     return 1;
  121.   }
  122.   if ((t_offset + c_h) > frame->frm_h)
  123.   {
  124.     printf("Crop height is out of range.\n");
  125.     return 1;
  126.   }
  127.   crop_loc = frame->frm_comps + (t_offset * frame->frm_w) + l_offset; 
  128.   for (index_h = 0; index_h < c_h; index_h++)
  129.   {
  130.     memset(crop_loc, PIX_VALUE, c_w);
  131.     crop_loc += frame->frm_w;
  132.   }
  133.   return 0;
  134. }
 
五、编译与运行
 
makefile.producer
  1. OBJECTS = producer.o semaphore.o frame.o
  2. CC = gcc
  3. CFLAG = -g -Wa 
  4. producer : $(OBJECTS)
  5.   $(CC) $(CFLAG) -o producer $(OBJECTS)
  6. producer.o: common.h semaphore.h frame.h shm_com.h
  7. semaphore.o:semaphore.h
  8. frame.o: frame.h
  9. .PHONY:clean
  10. clean:
  11.   rm producer $(OBJECTS)
 
makefile.consumer
  1. OBJECTS = consumer.o semaphore.o frame.o
  2. CC = gcc
  3. CFLAG = -g -Wa
  4. consumer : $(OBJECTS)
  5.   $(CC) $(CFLAG) -o consumer $(OBJECTS)
  6. consumer.o: common.h semaphore.h frame.h shm_com.h
  7. semaphore.o:semaphore.h
  8. frame.o:frame.h
  9. .PHONY:clean
  10. clean:
  11.   rm consumer $(OBJECTS)
编译与运行:
  1. $make -f makefile.producer
  2. $make -f makefile.consumer
  3. $producer &
  4. $consumer
posted @ 2016-05-26 17:05  孤火  阅读(747)  评论(2编辑  收藏  举报