Come condividere i semafori tra processi utilizzando la memoria condivisa

Devo sincronizzare i processi N client con un server. Questi processi sono biforcati da una funzione principale in cui ho dichiarato 3 semafori. Ho deciso di usare i semafori POSIX ma non so come condividerli tra questi processi. Ho pensato che la memoria condivisa dovrebbe funzionare correttamente, ma ho alcune domande:

  • Come posso allocare il giusto spazio di memoria nel mio segmento?
  • Posso usare sizeof(sem_t) nel campo size_t di shmget per allocare esattamente lo spazio che mi serve?
  • Qualcuno ha qualche esempio simile a questa situazione?

È facile condividere POSIX semafori POSIX

  • Scegli un nome per il tuo semaforo

     #define SNAME "/mysem" 
  • Usa sem_open con O_CREAT nel processo che li crea

     sem_t *sem = sem_open(SNAME, O_CREAT, 0644, 3); /* Initial value is 3. */ 
  • Apri semafori negli altri processi

     sem_t *sem = sem_open(SEM_NAME, 0); /* Open a preexisting semaphore. */ 

Se insisti a usare la memoria condivisa, è certamente ansible.

 int fd = shm_open("shmname", O_CREAT, O_RDWR); ftruncate(fd, sizeof(sem_t)); sem_t *sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); sem_init(sem, 1, 1); 

Non ho provato quanto sopra, quindi potrebbe essere completamente fastidioso.

Ho scritto un’applicazione di esempio in cui un genitore (produttore) genera thread N child (consumer) e utilizza un array globale per comunicare i dati tra loro. L’array globale è una memoria circolare ed è protetto quando i dati vengono scritti sull’array.

File di intestazione:

 #define TRUE 1 #define FALSE 0 #define SUCCESS 0 #define FAILURE -1 /* * Function Declaration. */ void Parent( void ); int CalcBitmap(unsigned int Num); void InitSemaphore( void ); void DeInitSemaphore( void ); int ComputeComplexCalc( int op1, int op2); /* * Thread functions. */ void *Child( void *arg ); void *Report( void *arg1 ); /* * Macro Definition. */ #define INPUT_FILE "./input.txt" #define NUM_OF_CHILDREN 10 #define SIZE_CIRCULAR_BUF 256 #define BUF_SIZE 128 //Set the bits corresponding to all threads. #define SET_ALL_BITMAP( a ) a = uiBitmap; //Clears the bit corresponding to a thread after //every read. #define CLEAR_BIT( a, b ) a &= ~( 1 < < b ); /* * Have a dedicated semaphore for each buffer * to synchronize the acess to the shared memory * between the producer (parent) and the consumers. */ sem_t ReadCntrMutex; sem_t semEmptyNode[SIZE_CIRCULAR_BUF]; /* * Global Variables. */ unsigned int uiBitmap = 0; //Counter to track the number of processed buffers. unsigned int Stats; unsigned int uiParentCnt = 0; char inputfile[BUF_SIZE]; int EndOfFile = FALSE; /* * Data Structure Definition. ( Circular Buffer ) */ struct Message { int id; unsigned int BufNo1; unsigned int BufNo2; /* Counter to check if all threads read the buffer. */ unsigned int uiBufReadCntr; }; struct Message ShdBuf[SIZE_CIRCULAR_BUF]; 

Codice sorgente:

  /* # # ipc_communicator is a IPC binary where a parent # create ten child threads, read the input parameters # from a file, sends that parameters to all the children. # Each child computes the operations on those input parameters # and writes them on to their own file. # # Author: Ashok Vairavan Date: 8/8/2014 */ /* * Generic Header Files. */ #include "stdio.h" #include "unistd.h" #include "string.h" #include "pthread.h" #include "errno.h" #include "malloc.h" #include "fcntl.h" #include "getopt.h" #include "stdlib.h" #include "math.h" #include "semaphore.h" /* * Private Header Files. */ #include "ipc*.h" /* * Function to calculate the bitmap based on the * number of threads. This bitmap is used to * track which thread read the buffer and the * pending read threads. */ int CalcBitmap(unsigned int Num) { unsigned int uiIndex; for( uiIndex = 0; uiIndex < Num; uiIndex++ ) { uiBitmap |= 1 << uiIndex; } return uiBitmap; } /* * Function that performs complex computation on * numbers. */ int ComputeComplexCalc( int op1, int op2) { return sqrt(op1) + log(op2); } /* * Function to initialise the semaphores. * semEmptyNode indicates if the buffer is available * for write. semFilledNode indicates that the buffer * is available for read. */ void InitSemaphore( void ) { unsigned int uiIndex=0; CalcBitmap(NUM_OF_CHILDREN); sem_init( &ReadCntrMutex, 0, 1); while( uiIndex uiTotalCnt ) { /* Access the shared memory */ op1 = ShdBuf[uiCnt].BufNo1; op2 = ShdBuf[uiCnt].BufNo2; fprintf(fp, "%d %d = %d\n", op1, op2, ComputeComplexCalc( op1, op2) ); sem_wait( &ReadCntrMutex ); ShdBuf[uiCnt].uiBufReadCntr &= ~( 1 < < ThreadID ); if( ShdBuf[uiCnt].uiBufReadCntr == 0 ) { __sync_add_and_fetch(&Stats, 1); /* Release the semaphore lock if all readers read the data */ sem_post(&semEmptyNode[uiCnt]); } sem_post( &ReadCntrMutex ); uiTotalCnt++; uiCnt = uiTotalCnt % SIZE_CIRCULAR_BUF; /* If the parent reaches the end of file and if the child thread read all the data then break out */ if( isEOF() && ( uiTotalCnt == uiParentCnt ) ) { //printf("Thid %dp %dc %d\n", ThreadID, uiParentCnt, uiCnt ); break; } } else { /* Sleep for ten micro seconds before checking the shared memory */ usleep(10); } } fclose( fp ); return NULL; } void usage( void ) { printf(" Usage:\n"); printf(" -f - the absolute path of the input file where the input parameters are read from.\n\t\t Default input file: ./input.txt"); printf(" -? - Help Menu. \n" ); exit(1); } int main( int argc, char *argv[]) { pthread_attr_t ThrAttr; pthread_t ThrChild[NUM_OF_CHILDREN], ThrReport; unsigned int uiThdIndex = 0; unsigned int *pThreadID; int c; strncpy( inputfile, INPUT_FILE, BUF_SIZE ); while( ( c = getopt( argc, argv, "f:" )) != -1 ) { switch( c ) { case 'f': strncpy( inputfile, optarg, BUF_SIZE ); break; default: usage(); } } if( access( inputfile, F_OK ) == -1 ) { perror( "access" ); return FAILURE; } InitSemaphore(); pthread_attr_init( &ThrAttr ); while( uiThdIndex< NUM_OF_CHILDREN ) { /* Allocate the memory from the heap and pass it as an argument * to each thread to avoid race condition and invalid reference * issues with the local variables. */ pThreadID = (unsigned int *) malloc( sizeof( unsigned int ) ); if( pThreadID == NULL ) { perror( "malloc" ); /* Cancel pthread operation */ return FAILURE; } *pThreadID = uiThdIndex; if( pthread_create( &ThrChild[uiThdIndex], NULL, &Child, pThreadID) != 0 ) { printf( "pthread %d creation failed. Error: %d\n", uiThdIndex, errno); perror( "pthread_create" ); return FAILURE; } uiThdIndex++; } /* Report thread reports the statistics of IPC communication * between parent and childs threads. */ if( pthread_create( &ThrReport, NULL, &Report, NULL) != 0 ) { perror( "pthread_create" ); return FAILURE; } Parent(); uiThdIndex = 0; while( uiThdIndex < NUM_OF_CHILDREN ) { pthread_join( ThrChild[uiThdIndex], NULL ); uiThdIndex++; } /* * Cancel the report thread function when all the * children exits. */ pthread_cancel( ThrReport ); DeInitSemaphore(); return SUCCESS; } 

Dubito davvero che l’approccio alla memoria condivisa possa mai funzionare.

AFAIK il semaforo è in realtà solo un handle, valido per il processo in cui è stato aperto. Le informazioni sul semaforo reale si trovano nella memoria del kernel.

Quindi usare lo stesso valore di handle in altri processi non funzionerà.

Sì, possiamo usare il semaforo nominato tra i due processi. Possiamo aprire un file.

 snprintf(sem_open_file, 128, "/sem_16"); ret_value = sem_open((const char *)sem_open_file, 0, 0, 0); if (ret_value == SEM_FAILED) { /* * No such file or directory */ if (errno == ENOENT) { ret_value = sem_open((const char *)sem_open_file, O_CREAT | O_EXCL, 0777, 1); } } 

E altri processi possono usare questo.