2013-08-27 2 views
1

Я пытаюсь поэкспериментировать с IPC, и я придумал базовый пример с участием Mutex. Это уменьшенная версия, чтобы выделить ошибку. Код без мьютекса работает так, как ожидалось, и дочерний процесс может считывать значение из области SHM, измененной родителем. Однако в этой версии, как только родитель освободит блокировку мьютекса, ребенок не сможет ее приобрести и застрял в блоке. Я использую «kbhit.h» для упрощенного интерфейса клавиатуры. Код также прилагается ниже. Если кто-нибудь знает, почему это происходит, пожалуйста, поделитесь знаниями.Ребенок не может получить блокировку Mutex в области общей памяти

Программа Отрывок:

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <ctype.h> 
#include <pthread.h> 
#include <sys/mman.h> 
#include "kbhit.h" 

typedef struct { 
    pthread_cond_t cond; 
    pthread_condattr_t condattr; 
    pthread_mutex_t mutex; 
    pthread_mutexattr_t mutexattr; 
    int status; 
} IPCData; 

int main(int argc, char const *argv[]) 
{ 
    IPCData* sharedMemory; 
    pid_t childPID; //child process PID returned by fork 
    char ch='x'; 

    sharedMemory = (IPCData*)mmap(NULL, sizeof(IPCData), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 

    if ((void*)sharedMemory == -1){ 
     printf("Memory mapping failed\n"); 
     return -1; 
    } 

    sharedMemory->status = 1; //Set shared variable value 

    if (pthread_condattr_init(&(sharedMemory->condattr)) != 0 && pthread_condattr_setpshared(&(sharedMemory->condattr), PTHREAD_PROCESS_SHARED) != 0){ 
     printf("Error initializing and setting condition variable attribute values\n"); 
     return -1; 
    } 

    if (pthread_cond_init(&(sharedMemory->cond), &(sharedMemory->condattr)) !=0){ 
     printf("Error initializing condition variable with attribute\n"); 
     return -1; 
    } 

    if (pthread_mutexattr_init(&(sharedMemory->mutexattr)) != 0 && pthread_mutexattr_setpshared(&(sharedMemory->mutexattr), PTHREAD_PROCESS_SHARED) != 0){ 
     printf("Error initializing mutex attribute and set to process shared\n"); 
     return -1; 
    } 

    if (pthread_mutex_init(&(sharedMemory->mutex), &(sharedMemory->mutexattr)) !=0){ 
     printf("Error initializing mutex with attributes\n"); 
     return -1; 
    } 

    //forking 
    switch (childPID = fork()){ 
     case -1: 
      printf("Failure to fork new child process\n"); 
      return -1; 
     case 0: 
      //child process 
      printf("Child Started\n"); 
      while (__sync_fetch_and_add(&(sharedMemory->status), 0) !=0){ //atomically vertify variable value to continue execution as long as value !=0 
       printf("Child - Looping, Status: %d\n", sharedMemory->status); 
       //child fails to acquire lock after parent release it in SHM region 
       pthread_mutex_lock(&(sharedMemory->mutex)); //acquire lock 
       printf("Child Mutex Acquired\n"); 
       if (__sync_fetch_and_add(&(sharedMemory->status), 0) > 1) {printf("Increment Detected. Status: %d\n", sharedMemory->status);} 
       printf("Child Mutex Released\n"); 
       pthread_mutex_unlock(&(sharedMemory->mutex)); //release lock 
      } 
      printf("Child Exiting\n"); 
      _Exit(3); 
      break; 
     default: 
      //parent process 

      init_keyboard(); 

      while(ch != 'q') { 
       if (kbhit()) { 
        do{ 
         ch=readch(); 
        } while(kbhit()); 
        if (ch == 's'){ 
         pthread_mutex_lock(&(sharedMemory->mutex)); 
         printf("Parent Mutex Acquired\n"); 
         printf("Subbing\n"); 
         printf("Status: %d\n", __sync_fetch_and_sub(&(sharedMemory->status), 1)); 
         printf("Parent Mutex Released\n"); 
         pthread_mutex_unlock(&(sharedMemory->mutex)); 
        } else if (ch == 'a'){ 
         pthread_mutex_lock(&(sharedMemory->mutex)); 
         printf("Parent Mutex Acquired\n"); 
         printf("Adding\n"); 
         printf("Status: %d\n", __sync_fetch_and_add(&(sharedMemory->status), 1)); 
         printf("Parent Mutex Released\n"); 
         pthread_mutex_unlock(&(sharedMemory->mutex)); 
        } else if (ch == 'z'){ 
         printf("Returning value(Adding 0)\n"); 
         printf("Status: %d\n", __sync_fetch_and_add(&(sharedMemory->status), 0)); 
        } 
       } 
      } 
      printf("Parent Exiting\n"); 
      close_keyboard(); 
      break; 
    } 

    pthread_condattr_destroy(&(sharedMemory->condattr)); 
    pthread_cond_destroy(&(sharedMemory->cond)); 
    pthread_mutexattr_destroy(&(sharedMemory->mutexattr)); 
    pthread_mutex_destroy(&(sharedMemory->mutex)); 

    if (munmap(sharedMemory, sizeof(IPCData)) == -1){ 
     printf("Unmap shared memory region failed\n"); 
     return -1; 
    } 

    return 0; 
} 

kbhit.h

#ifndef KBHITh 
#define KBHITh 

#include <termios.h> 
#include <unistd.h> // for read() 

void init_keyboard(void); 
void close_keyboard(void); 
int  kbhit(void); 
int  readch(void); 

static struct termios initial_settings, new_settings; 
static int peek_character = -1; 

void init_keyboard() 
{ 
    tcgetattr(0,&initial_settings); 
    new_settings = initial_settings; 
    new_settings.c_lflag &= ~ICANON; 
    new_settings.c_lflag &= ~ECHO; 
    new_settings.c_lflag &= ~ISIG; 
    new_settings.c_cc[VMIN] = 1; 
    new_settings.c_cc[VTIME] = 0; 
    tcsetattr(0, TCSANOW, &new_settings); 
} 

void close_keyboard() 
{ 
    tcsetattr(0, TCSANOW, &initial_settings); 
} 

int kbhit() 
{ 
unsigned char ch; 
int nread; 

    if (peek_character != -1) return 1; 
    new_settings.c_cc[VMIN]=0; 
    tcsetattr(0, TCSANOW, &new_settings); 
    nread = read(0,&ch,1); 
    new_settings.c_cc[VMIN]=1; 
    tcsetattr(0, TCSANOW, &new_settings); 
    if(nread == 1) 
    { 
     peek_character = ch; 
     return 1; 
    } 
    return 0; 
} 

int readch() 
{ 
char ch; 

    if(peek_character != -1) 
    { 
     ch = peek_character; 
     peek_character = -1; 
     return ch; 
    } 
    read(0,&ch,1); 
    return ch; 
} 


#endif 

После игры с этим кодом, не зная точно, что произошло, она функционировала как и ожидалось. Ниже приведен пример IPC с использованием условных переменных и мьютексов между процессами; в частности, между родителем и ребенком.

Как играть с кодом:

  1. компилировать с -lpthread и включают в себя «kbhit.h» выше в том же каталоге

  2. Нажмите кнопку «V» на клавиатуре, чтобы проверить значения 3 общих переменных, проживающих в SHM

  3. Нажмите «C» на клавиатуре, чтобы изменить значение одного из переменной (приращение)

  4. Нажмите «v» еще раз, чтобы проверить

Для выхода:

1.Press «к», чтобы изменить значение переменной

2.press «с» снова, как ребенок будет заблокирован на конд ожидания перед выходом из цикла

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <ctype.h> 
#include <pthread.h> 
#include <sys/mman.h> 
#include "kbhit.h" 

typedef struct { 
    pthread_cond_t cond; 
    pthread_condattr_t condattr; 
    pthread_mutex_t mutex; 
    pthread_mutexattr_t mutexattr; 
    int status; 
    int jobsignal; 
    int count; 
} IPCData; 

int main(int argc, char const *argv[]) 
{ 
    IPCData* sharedMemory; 
    pid_t childPID; //child process PID returned by fork 
    char ch='x'; 

    sharedMemory = (IPCData*)mmap(NULL, sizeof(IPCData), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 

    if ((void*)sharedMemory == -1){ 
     printf("Memory mapping failed\n"); 
     return -1; 
    } 

    sharedMemory->status = 1; //set shared variable value 1 = execute 0 = terminate 
    sharedMemory->jobsignal = 0; //turn off Signal status 1 = job exist 0 = no job 
    sharedMemory->count = 0; //common count variable 

    if (pthread_condattr_init(&(sharedMemory->condattr)) != 0 || pthread_condattr_setpshared(&(sharedMemory->condattr), PTHREAD_PROCESS_SHARED) != 0){ 
     printf("Error initializing and setting condition variable attribute values\n"); 
     return -1; 
    } 

    if (pthread_cond_init(&(sharedMemory->cond), &(sharedMemory->condattr)) !=0){ 
     printf("Error initializing condition variable with attribute\n"); 
     return -1; 
    } 

    if (pthread_mutexattr_init(&(sharedMemory->mutexattr)) != 0 || pthread_mutexattr_setpshared(&(sharedMemory->mutexattr), PTHREAD_PROCESS_SHARED) != 0){ 
     printf("Error initializing mutex attribute and set to process shared\n"); 
     return -1; 
    } 

    if (pthread_mutex_init(&(sharedMemory->mutex), &(sharedMemory->mutexattr)) !=0){ 
     printf("Error initializing mutex with attributes\n"); 
     return -1; 
    } 

    //forking 
    switch (childPID = fork()){ 
     case -1: 
      printf("Failure to fork new child process\n"); 
      return -1; 
     case 0: 
      //child process 
      printf("Child Process Started\n"); 
      while (__sync_fetch_and_add(&(sharedMemory->status), 0) !=0){ //atomically vertify variable value to continue execution as long as value !=0 
       if (pthread_mutex_lock(&(sharedMemory->mutex)) != 0) { //acquire lock 
        printf("Child Process Mutex Acquisition Failed\n"); 
       } else { 
        printf("Child Process Mutex Acquisition Success\n"); 
        if (pthread_cond_wait(&(sharedMemory->cond), &(sharedMemory->mutex)) != 0){ 
         printf("Child Process Condition Wait Failed\n"); 
        } else { 
         printf("Child Process Condition Wait Success\n"); 
        } 
        __sync_fetch_and_add(&(sharedMemory->count), 1); //add 1 
        if (pthread_mutex_unlock(&(sharedMemory->mutex)) != 0) { //release lock 
         printf("Child Process Mutex Released Failed\n"); 
        } else { 
         printf("Child Process Mutex Release Success\n"); 
        } 
       }   
      } 
      printf("Child Process Exiting\n"); 
      _Exit(3); 
      break; 
     default: 
      //parent process 
      printf("Parent Process Started\n"); 
      init_keyboard(); 

      while(ch != 'q') { 
       if (kbhit()) { 
        do{ 
         ch=readch(); 
        } while(kbhit()); 
        if (ch == 'c'){ 
         if (pthread_mutex_lock(&(sharedMemory->mutex)) != 0) { 
          printf("Parent Process Mutex Acquisition Failed\n"); 
         } else { 
          printf("Parent Process Mutex Acquisition Success\n"); 
          if (pthread_cond_signal(&(sharedMemory->cond)) != 0){ 
           printf("Parent Process Signal Failed\n"); 
          } else { 
           printf("Parent Process Signal Success\n"); 
          } 
          if (pthread_mutex_unlock(&(sharedMemory->mutex)) != 0){ 
           printf("Parent Process Mutex Release Failed\n"); 
          } else { 
           printf("Parent Process Mutex Release Success\n"); 
          } 
         } 
        } else if (ch == 'v'){ 
         printf("Status: %d\n", __sync_fetch_and_add(&(sharedMemory->status), 0)); 
         printf("JobSignal: %d\n", __sync_fetch_and_add(&(sharedMemory->jobsignal), 0)); 
         printf("Count: %d\n", __sync_fetch_and_add(&(sharedMemory->count), 0)); 
        } else if (ch == 'k'){ 
         printf("Terminating Child Process\n"); 
         __sync_fetch_and_sub(&(sharedMemory->status), 1); 
        } 
       } 
      } 
      printf("Parent Process Exiting\n"); 
      close_keyboard(); 
      break; 
    } 

    pthread_condattr_destroy(&(sharedMemory->condattr)); 
    pthread_cond_destroy(&(sharedMemory->cond)); 
    pthread_mutexattr_destroy(&(sharedMemory->mutexattr)); 
    pthread_mutex_destroy(&(sharedMemory->mutex)); 

    if (munmap(sharedMemory, sizeof(IPCData)) == -1){ 
     printf("Unmap shared memory region failed\n"); 
     return -1; 
    } 

    return 0; 
} 
+0

Устранено, пример использования предоставленного в редактировании –

ответ

1

Вы не можете использовать pthread_mutex для синхронизации различных процессов, только нити того же процесса. Если вам это удастся, используйте каналы для ipc, тогда вам не нужно синхронизировать доступ к разным структурам данных.

Edit:

Просто гугл вокруг, кажется, вы иногда можете при установке правильных атрибутов, извините

+0

Реализуя SHM и с помощью атрибута PTHREAD_PROCESS_SHARED, один может позволить объекты синхронизации, к которым должны обращаться любые потоки от любых процессов, имеющих доступ к этой конкретной области памяти, см. [link] (http://linux.die.net/man/3/pthread_mutexattr_init) –

+0

В идеале, канал должен быть зарезервирован исключительно для передача данных. В расширенной версии я реализовал перенаправление каналов вместе с сигнальными и условными переменными. Однако получение функции Mutex является первым фундаментальным шагом. –