制片人消费者信号量线程安全

时间:2017-04-05 03:56:38

标签: c linux

我有关于Linux上的C的编程问题。以下代码是一个'快照'一个更大的服务器,我试图编码。

为了解释,我想:

  1. fork()服务器中的多个子进程处理父进程或客户端或
  2. 生成的请求
  3. 每个子进程都有几个生产者(编写者)线程和一个消费者(读者)线程
  4. 指针的环形缓冲区保存写入并由线程读取的信息
  5. 生产者线程从链接列表中检索字符串(node-> string)并且' push' node->字符串到消费者读取的指针的环形缓冲区
  6. 消费者线程' pops'来自环形缓冲区并写入某些流(文件,标准输出等)
  7. 我使用POSIX信号量尝试同步对线程之间的环形缓冲区的访问(这可能是问题)
  8. My Ring Buffer不起作用。我不确定我是否对缓冲区编码不好或者信号量是否按照我的意愿工作。

    我包含了' server.c'和' circbuff.c'还有一个linkedlist.h库,但我确信它有效

    可能存在的问题?

    1. 我全局声明buffer_t buffer(环形缓冲区)。我不喜欢它。

    2. ????

    3. 任何愿意提供帮助的人都会非常感激。

      非常欢迎有关如何让线程与链表或环形缓冲区通信的建议。

      输出始终不同,但它总是向环形缓冲区推送比弹出更多。

      OUTPUT:.. format =' PUSH:子行#thread'

      PUSH: 2264 line 0      thread: 139746191480576
      PUSH: 2264 line 0      thread: 139746183087872
      PUSH: 2264 line 1      thread: 139746183087872
      Buffer underflow
      pop 0  
      Buffer underflow
      pop 0  
      Buffer underflow
      pop 0  
      PUSH: 2275 line 0      thread: 139746191480576
      PUSH: 2275 line 0      thread: 139746191480576
      

      更多输出

      PUSH: 4208 line 0      thread: 140707316717312
      PUSH: 4208 line 1      thread: 140707316717312
      PUSH: 4208 line 2      thread: 140707316717312
      PUSH: 4208 line 3      thread: 140707316717312
      PUSH: 4208 line 4      thread: 140707316717312
      PUSH: 4208 line 5      thread: 140707316717312
      pop 4208 line 0    
      pop 4208 line 0
      

      SERVER.C

          #include <stdio.h>
          #include <string.h>
          #include <fcntl.h>
          #include <sys/shm.h>
          #include <sys/stat.h>
          #include <sys/mman.h>
          #include <unistd.h>
          #include <sys/types.h>
          #include <semaphore.h>
          #include <dirent.h>
          #include <sys/wait.h>
          #include <errno.h>
      
          #include "circbuff.h"
          #include "server.h"
      
      
          #define SNAME "/OS"
      
          #define MAXDIRPATH 1024
          #define MAXKEYWORD 256
          #define MAXLINESIZE 1024
          #define MAXOUTSIZE 2048
      
      
          sem_t * sem_wrt;
          sem_t * sem_empty;
          sem_t * sem_full;
          sem_t * sem_mutex;
      
          buffer_t buffer;             //a circular buffer pointer array that reads info from a linked list
          void * producer(void *arg);  //thread producer
          void * consumer(void *arg);  //thread consumer
      
          int main( int argc, char *argv[]){
      
          if (argc != 3){
              printf("Three arguements required .../client <arbitrary> <**buffer_size**>\n");
              exit(0);
              }
      
          int reqsize= atoi(argv[1]);
          int bbuff= atoi(argv[2]);
          //buffer_t *buf; 
          int qbuffsize= reqsize*(MAXDIRPATH+MAXKEYWORD);
      
          //char tok1[MAXDIRPATH];
          //char tok2[MAXKEYWORD]; 
          int child_num, status; 
      
            /*** Semaphore open */  
          sem_wrt=sem_open("/sem_wrt", O_CREAT, S_IRUSR | S_IWUSR, 1);
              if (sem_wrt == SEM_FAILED) {
              perror("server: sem_open failed for semaphone sem_wrt");
              return EXIT_FAILURE;
          }
          sem_empty=sem_open("/sem_empty", O_CREAT, S_IRUSR | S_IWUSR, bbuff); //semaphore waits for bbuff number of things to stop process
              if (sem_empty == SEM_FAILED) {
              perror("server: sem_open failed for semaphone sem_empty");
              return EXIT_FAILURE;
          }
          sem_full=sem_open("/sem_full", O_CREAT, S_IRUSR | S_IWUSR, 0);
              if (sem_full == SEM_FAILED) {
              perror("server: sem_open failed for semaphone sem_full");
              return EXIT_FAILURE;
          }
          sem_mutex=sem_open("/sem_mutex", O_CREAT, S_IRUSR | S_IWUSR, 1);
              if (sem_mutex == SEM_FAILED) {
              perror("server: sem_poen failed for semaphone sem_mutex");
              return EXIT_FAILURE;
              }
      
      
          /* Create 4 child processes (each will have 11 threads per below, 1 producer, 10 consumers) */
          pid_t pid;
          int ct;
          for(ct=0; ct<4; ct++){
      
              pid=fork();  //make 5 children
              child_num++;
          }
      
      
      
      
          if(pid==0){//IF CHILD PROCESS
      
              struct ThreadList* t_list=NULL;  //delcare a list of threads 
              t_list= create_list();           //create the list of  thread_info_nodes             
      
              init(&buffer, bbuff);   //initial circular buffer (declared GLOBALLY), which to multiple threads write, 1 thread reads
      
              pthread_t ctid;                                 //consumer thread id
              pthread_create(&ctid, NULL, consumer, t_list);  //create one consumer/writer/pop-list thread 
      
      
              /* make n_cnt producer threads (10 threads) that take node->string from a linked list and push to a ring buffer of pointers */
              int n_cnt; 
              for(n_cnt=0; n_cnt<10; n_cnt++){
                  struct thread_info_node *tnode = malloc(sizeof(struct thread_info_node));//make a node on the heap
                  init_tnode(tnode, n_cnt, NULL, " line ");                                //initialize node info ..a string
                  insert_tail( tnode, t_list);                                             //insert node to tail of list
                  pthread_create( &(tnode->tid), NULL, producer, t_list);                  //create a few producer threads that read list
              }
      
      
      
              //When child process is done, close semaphores to release resources used by child
              sem_close(sem_wrt);  
              sem_close(sem_empty);  
              sem_close(sem_full);   
              sem_close(sem_mutex);
          }
      
          else{// if the PARENT
              /*wait for child processes to finish*/
              int cnt;
              for(cnt=0; cnt < child_num ; cnt++){
                  waitpid(-1, &status, 0);}
      
      
              sem_close(sem_wrt);  
              sem_close(sem_empty);  
              sem_close(sem_full);   //printf("close sem_full= %d\n", sem_close(sem_full)   );  
              sem_close(sem_mutex);
      
      
              sem_unlink("/sem_wrt"); 
              sem_unlink("/sem_empty");  
              sem_unlink("/sem_full"); //printf("unlink sem_full= %d\n", sem_unlink("/sem_full")   );  
              sem_unlink("/sem_mutex");
      
              }
      
      
      return 0; 
      }
      
      
      void * producer(void *arg){
      
          struct ThreadList *list = arg;  
          struct thread_info_node *tmp = list->head;
          pthread_t tid= pthread_self(); 
          char thread_id[MAXLINESIZE]; 
      
        /* push all but last string from linked list*/ 
        // I AM PUSHING ONLY THE 'node->string' value, not the whole node.  IS THIS THE WR0NG WAY TO DO IT??? 
          while(tmp->next != NULL){
      
              sem_wait(sem_empty); //decrement empty. if 0 no more empty, stop process.  
              sem_wait(sem_mutex);
      
              /*** CRITICAL SECTION ***/
              snprintf(thread_id, 10,  "%lu", tid);
              //strcat(tmp->filename, thread_id);
              printf("PUSH: %s    thread: %lu\n", tmp->filename, tid);
              push( &buffer, tmp->filename );
              ///--------------------//
      
              sem_post(sem_mutex);
              sem_post(sem_full); 
      
              tmp= tmp->next;
          }
      
        /* push last string left in linked list, and an exit message for the consumer */  
          sem_wait(sem_empty); //decrement empty. if 0 no more slots empty, stop process.  
          sem_wait(sem_mutex);
      
          /*** CRITICAL SECTION ***/
          snprintf(thread_id, 10,  "%lu", tid);
          strcat(tmp->filename, thread_id);
          printf("PUSH: %s\n", tmp->filename);
          push( &buffer, tmp->filename );
      
          printf("SPECIAL_EXIT_CODE\n");
          push( &buffer, "SPECIAL_EXIT_CODE"); 
          ///--------------------//
      
          sem_post(sem_mutex);
          sem_post(sem_full);
      
          return NULL;    
      }
      
      
      void * consumer(void *arg){
      
      //struct ThreadList *list = arg;    
      
      char outline[MAXOUTSIZE]; 
      
          while(1){ 
      
           sem_wait(sem_full);
           sem_wait(sem_mutex); 
                /******* CRITICAL SECTION ******/ 
      
                //printf("==>CONSUMER THREAD\n");
                strcpy(outline, popqueue(&buffer)); 
                printf("pop %s  \n",  outline );
      
                if (strcmp(outline,"SPECIAL_EXIT_CODE")==0) break;
      
      
                /********************************/
            sem_post(sem_mutex);
            sem_post(sem_empty); 
      
      
          }//end WHILE*/
      
          return NULL;
      }
      

      CIRCBUFF.H

      #ifndef __CIRCBUFF_h__
      #define __CIRCBUFF_h__
      
      
      #include <stdio.h>
      #include <stdlib.h>
      
      #define MAXDIRPATH 1024
      #define MAXKEYWORD 256
      #define MAXLINESIZE 1024
      #define MAXOUTSIZE 2048
      
      
      struct buffer {
          int size;
          int start;
          //int end;  // position of last element
          /* Tracking start and end of buffer would waste
           * one position. A full buffer would always have
           * to leave last position empty or otherwise
           * it would look empty. Instead this buffer uses
           * count to track if buffer is empty or full
           */
          int count; // number of elements in buffer
          /* Two ways to make buffer element type opaque
           * First is by using typedef for the element
           * pointer. Second is by using void pointer.
           */
          /* different types of buffer: 
          int *element;   // array of integers
          char *element;  // array of characters 
          void *element;  // array of void type (could cast to int, char, etc)
          char **element; //array of char pointers (array of strings)
          void **element; // array of void pointers
          Choosing array of void pointers since it's the most flexible */
          void **element;
      
      };
      
      typedef struct buffer buffer_t;
      
      void init(buffer_t *buffer, int size) {
          buffer->size = size;
          buffer->start = 0;
          buffer->count = 0;
          //buffer->element = malloc(sizeof(buffer->element)*size);
          /* allocated array of void pointers. Same as below */
          //buffer->element = malloc(sizeof(void *) * size);
      
          /* Allocate array of char poitners*/
          buffer->element = malloc(sizeof(char **) * size);
      
      }
      
      int full(buffer_t *buffer) {
          if (buffer->count == buffer->size) { 
              return 1;
          } else {
              return 0;
          }
      }
      
      int empty(buffer_t *buffer) {
          if (buffer->count == 0) {
              return 1;
          } else {
              return 0;
          }
      }
      
      void push(buffer_t *buffer, void *data) {
          int index;
          if (full(buffer)) {
              printf("Buffer overflow\n");
          } else {
              index = buffer->start + buffer->count; 
              if (index >= buffer->size) {
                  index = 0;
                  //index= index - buffer->size;//6 
              }
              buffer->element[index] = data;
              buffer->count++;
          }
      }
      
      
      void * popqueue(buffer_t *buffer) {
          void * element;
          if (empty(buffer)) {
              printf("Buffer underflow\n");
              return "0";
          } else {
             /* FIFO implementation */
             element = buffer->element[buffer->start];
             buffer->start++;
             buffer->count--;
             if (buffer->start == buffer->size) {
                 buffer->start = 0;
             }
      
             return element;
          }
      }
      
      void * popstack(buffer_t *buffer) {
          int index;
          if (empty(buffer)) {
              printf("Buffer underflow\n");
              return "0";
          } else {
              /* LIFO implementation */
              index = buffer->start + buffer->count - 1;
              if (index >= buffer->size) {
                 index = buffer->count - buffer->size - 1;
                 //index= index-buffer->size
                 //index = buffer->count – buffer->size – 1;
              }      
              buffer->count--;
              return buffer->element[index];
          }
      }
      
      #endif
      

2 个答案:

答案 0 :(得分:0)

问题是fork()不共享全局变量。子进程和父进程有自己的缓冲区副本。您应该为此目的使用共享内存。

答案 1 :(得分:0)

对于那些不幸来到这里寻求答案的人,我设法勾勒出一个解决方案。我在主服务器上修改了两件事

  1. 我摆脱了全球缓冲,......只是冒险的生意。最初的问题是,当调用pthread_create时,只能传递1个arg(参见手册页)。我需要很多信息才能通过,所以我制作了一个超级控制结构&#39;由线程间环缓冲区和线程操作所需的所有信息组成,并通过

  2. 信号量和循环可能很棘手。

  3. 我的基本方法*似乎有效。许多生产者线程进程信息,推送到缓冲区,每个子进程1个写入器线程写入log.txt。在堆上使用环形缓冲区来引用同一堆上的链接列表。

  4. ..现在我解决了内存泄漏问题。