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*

  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.  * \Brief
  14.  * init frame
  15.  */
  16. int init_frame(frame_p frame, int width, int height, frame_type_e type)
  17. {
  18.   assert(frame != NULL);
  19.         
  20.   frame->frm_w = width;
  21.   frame->frm_h = height;
  22.   frame->frm_type = type;

  23.   switch(frame->frm_type)
  24.   {
  25.   case YUV420:
  26.     frame->frm_size = (frame->frm_w * frame->frm_h * 3) / 2;
  27.     break;    
  28.   case YUV422:
  29.     frame->frm_size = frame->frm_w * frame->frm_h * 2; 
  30.     break;
  31.   case YUV444:
  32.     frame->frm_size = frame->frm_w * frame->frm_h * 3; 
  33.     break;    
  34.   case RGB:
  35.     frame->frm_size = frame->frm_w * frame->frm_h * 3;
  36.     break;
  37.   default:
  38.     fprintf(stderr, "frame type is invalid.");
  39.     return 1; 
  40.   }

  41.   if ((frame->frm_comps = (char*)calloc(frame->frm_size, sizeof(char))) == NULL)
  42.   {
  43.     fprintf(stderr, "calloc failed.");
  44.     return 1;
  45.   } 

  46.   return 0;
  47. }

  48. /*
  49.  * \Brief
  50.  * init frame
  51.  */
  52. int free_frame(frame_p frame)
  53. {
  54.   assert(frame != NULL);

  55.   free(frame->frm_comps);
  56.   return 0;
  57. }
  58.   

  59. /*
  60.  * \Brief
  61.  * read a frame from file
  62.  */
  63. int read_frame_from_file(frame_p frame, FILE* fp)
  64. {
  65.   int ret;
  66.   assert(frame != NULL && fp != NULL);

  67.   if (ret = (fread(frame->frm_comps, sizeof(char), frame->frm_size, fp)) 
  68.      != frame->frm_size)
  69.   {
  70.     fprintf(stderr, "read_frame_from_file failed. %d\n", ret);
  71.     return 1;
  72.   }

  73.   return 0;
  74. } 


  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. /*
  91.  * \Brief
  92.  * read a frame from shared-memory
  93.  */
  94. int read_frame_from_shm(frame_p frame, shared_use_st* shm)
  95. {
  96.   assert(frame != NULL && shm != NULL);

  97.   memcpy((char*)frame->frm_comps, (char*)shm->shm_sp, frame->frm_size);

  98.   return 0;
  99. } 

  100. /*
  101.  * \Brief
  102.  * write a frame into shared-memory
  103.  */
  104. int write_frame_into_shm(shared_use_st* shm, frame_p frame) 
  105. {
  106.   assert(frame != NULL && shm != NULL);

  107.   memcpy((char*)shm->shm_sp, (char*)frame->frm_comps, frame->frm_size);

  108.   return 0;
  109. } 



  110. /*
  111.  * \Brief
  112.  * crop frame
  113.  */
  114. int crop_frame(frame_p frame, int l_offset, int t_offset, int c_w, int c_h)
  115. {
  116.   char* crop_loc;
  117.   int index_h;

  118.   assert(frame != NULL);

  119.   if ((l_offset + c_w) > frame->frm_w)
  120.   {
  121.     printf("Crop width is out of range.\n");
  122.     return 1;
  123.   }
  124.   if ((t_offset + c_h) > frame->frm_h)
  125.   {
  126.     printf("Crop height is out of range.\n");
  127.     return 1;
  128.   }

  129.   crop_loc = frame->frm_comps + (t_offset * frame->frm_w) + l_offset; 
  130.   for (index_h = 0; index_h < c_h; index_h++)
  131.   {
  132.     memset(crop_loc, PIX_VALUE, c_w);

  133.     crop_loc += frame->frm_w;
  134.   }

  135.   return 0;
  136. }


五、编译与运行

makefile.producer
  1. OBJECTS = producer.o semaphore.o frame.o
  2. CC = gcc
  3. CFLAG = --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 = --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

你可能感兴趣的:(Linux:,多进程编程)