使用信号量

时间:2018-04-07 06:30:17

标签: c ipc semaphore shared-memory

我有两个代码:生产者(PR)消费者(CO)。存在一块存储器(Mat)(准确的3D矩阵),需要在两个程序之间共享。我目前正在使用基于共享内存的IPC函数来共享两个代码之间的内存空间。

约束:

  1. PR Mat的所有者,并执行迭代更改矩阵的值。 CO Mat的用户,只会读取值并用于进一步的计算
  2. PR 应先写入数据,然后等待 CO 读取并使用Matrix的值,然后发出信号 PR < / strong>继续进行进一步的迭代,它应该像这样继续。
  3. 我目前使用的是 -

    PRODUCER

    #include <unistd.h>
    #include  <stdio.h>
    #include  <stdlib.h>
    #include  <sys/types.h>
    #include  <sys/ipc.h>
    #include  <sys/shm.h>
    
    #define  NOT_READY  -1
    #define  FILLED     0
    #define  TAKEN      1
    
    #define  nx (400)
    #define  ny (400)
    #define  nz (400)
    
    struct Memory {
         int      status;
         double   u_x[nx+1][ny+2][nz+2];
    }
    
    int 
    main(int  argc, char *argv[])
    {
         key_t          ShmKEY;
         int            ShmID;
         struct Memory  *ShmPTR;
         int            i, j, k;
         int            niter = 5;
    
         int            sumX[niter],sumY[niter],sumZ[niter];
    
         ShmKEY = ftok(".", 'x'); // getting the unique identifier key from directory location
         ShmID = shmget(ShmKEY, sizeof(struct Memory), IPC_CREAT | 0666);
         if (ShmID < 0) {
              printf("*** shmget error (server) ***\n");
              exit(1);
         }
    
         ShmPTR = (struct Memory *) shmat(ShmID, NULL, 0);
         if ((int) ShmPTR == -1) {
              printf("*** shmat error (server) ***\n");
              exit(1);
         }
         printf("Server attached the memory to its virtual space...\n");
    
         ShmPTR->status  = NOT_READY; // setting the status to be not ready before filling it
    
         for (int m = 0; m < niter; m++){
            for (i=0; i<=nx; i++) for (j=0; j<=ny+1; j++) for (k=0; k<=nz+1; k++) 
               ShmPTR->u_x[i][j][k] = m; // filling the array with iteration number (just for depiction purpose)
            ShmPTR->status = FILLED; // change the status to Filled
            //printf("Please start the client in another window...\n");
            while (ShmPTR->status != TAKEN)
                 sleep(1);
         }
    
         printf("Server has detected the completion of its child...\n");
         shmdt((void *) ShmPTR);
         printf("Server has detached its shared memory...\n");
         shmctl(ShmID, IPC_RMID, NULL);
         printf("Server has removed its shared memory...\n");
         printf("Server exits...\n");
         exit(0);
    }
    

    CONSUMER

    #include  <stdio.h>
    #include  <stdlib.h>
    #include  <sys/types.h>
    #include  <sys/ipc.h>
    #include  <sys/shm.h>
    
    #define  NOT_READY  -1
    #define  FILLED     0
    #define  TAKEN      1
    
    #define  nx (400)
    #define  ny (400)
    #define  nz (400)
    
    struct Memory {
        int      status;
        double   u_x[nx+1][ny+2][nz+2];
    }
    
    int
    main(void)
    {
        key_t          ShmKEY;
        int            ShmID;
        struct Memory  *ShmPTR;
        int            i, j, k;
    
        int            niter = 5;
        int            sumX[niter];
    
        ShmKEY = ftok(".", 'x');
        ShmID = shmget(ShmKEY, sizeof(struct Memory), 0666);
        if (ShmID < 0) {
              printf("*** shmget error (client) ***\n");
            exit(1);
        }
        printf("Client has received a shared memory...\n");
    
        ShmPTR = (struct Memory *) shmat(ShmID, NULL, 0);
        if ((int) ShmPTR == -1) {
            printf("*** shmat error (client) ***\n");
            exit(1);
        }
        printf("Client has attached the shared memory to it's virtual memory space...\n");
    
        for (int m =0; m<niter; m++){
            sumX[m] = 0;
            while (ShmPTR->status != FILLED)
                ;
            printf("Client found the data is ready, performing sanity check...\n");
            // read the integers and check for the sum
    
            for (i=0; i<=nx; i++) for (j=0; j<=ny+1; j++) for (k=0; k<=nz+1; k++) 
                sumX[m] +=  ShmPTR->u_x[i][j][k];
            printf("Cycle %d : sumX-> %d\n", m,sumX[m);
    
            ShmPTR->status = TAKEN;
            printf("Client has informed server data have been taken...\n");
        }
        shmdt((void *) ShmPTR);
        printf("Client has detached its shared memory...\n");
        printf("Client exits...\n");
        exit(0);
    }
    

    我现在正在做的是使用名为status的结构的成员来防止竞争条件。从我读到现在开始,信号量在IPC中允许类似的事情。

    问题:如何在这个中使用信号量,使得需要共享的内存空间只是数组而没有将它包装在带有自定义标志的结构中?

    Edit1:或者mutex,如果它比这个应用程序的信号量更好的话。

    Edit2:关注适用于此代码的@Stargateur答案,但在nxnynz是变量的生产代码中,如何定义共享内存结构由一个可变长度多维数组的成员组成? (当然,它会一直有效,直到通话shmdtshmctl

2 个答案:

答案 0 :(得分:1)

我建议你使用两个信号量来实现你的功能,一个用于解锁产品,一个用于解锁消费者。

  

如何在这个中使用信号量,使得需要共享的内存空间只是数组而没有将它包装在带有自定义标志的结构中?

是的,但为什么要将数据和信号量与数据分开?

我会做以下事情:

struct Memory {
     sem_t    prod;
     sem_t    cons;
     double   u_x[nx+1][ny+2][nz+2];
};

// produser
sem_init(&ShmPTR->cons, !0, 0);
sem_init(&ShmPTR->prod, !0, 1);

 for (int m = 0; m < niter; m++) {
     sem_wait(&ShmPTR->prod);
     // ...
     sem_post(&ShmPTR->cons);
 }

// consumer
for (int m =0; m<niter; m++) {
     sem_wait(&ShmPTR->cons);
     // ...
     sem_post(&ShmPTR->prod);
}
  

或者可能是互斥量,如果它比信号量更好的话   应用

互斥锁无法在进程之间共享。

顺便说一句,你使用int来迭代一个数组,你应该使用size_t

答案 1 :(得分:0)

如果您计划将 PR CO 保留为单独的进程,则可以尝试从其他进程中分叉其中一个进程以同步它们。在这种特殊情况下,我的建议是从 PR 进程中分叉 CO 。以下是我的想法:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>


int main(int argc, char* argv[])
{
    const char *name="SHARED";
    const int SIZE = 4096;


    pid_t pidA;
    pidA = fork();

    if (pidA < 0)
    {
        printf("forkA Failed" );
        return 1;
    }
    else if (pidA == 0) // Child process A
    {


        // Read from the shared memory object.


        exit(0);
    }
    else // Parent process
    {

        int shm_fd;
        /* pointer to shared memory obect */
        void* ptr;
        /* create the shared memory object */
        shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
        /* configure the size of the shared memory object */
        ftruncate(shm_fd, SIZE);
        /* memory map the shared memory object */
        ptr = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0);



        /* write to the shared memory object */



        // Wait for child to read matrix
        wait(NULL);


        printf("Program finished------\n");
    }
}

但我们都知道线程更强轻量级,因此更好。您可以使用两个线程的信号量,如下所示:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

sem_t can_read,can_write; // declare two global semaphore

void* threadPR(void* arg)
{
  while(true)
  {
    //wait
    sem_wait(&can_write);


    //Write to matrix


    //signal
    sem_post(&can_read);
  }
}

void* threadCO(void* arg)
{
  while(true)
  {
    //wait
    sem_wait(&can_read);


    //Read the matrix


    //signal
    sem_post(&can_write);
  }
}


int main()
{
    // initialize the semaphore
    sem_init(&mutex, 0, 1);
    // declare two threads
    pthread_t t1,t2;
    pthread_create(&t1,NULL,threadPR,NULL); // Run the PR thread

    // do whatever needed before running CO

    pthread_create(&t2,NULL,threadCO,NULL); // Run the CO thread

    // wait for threads to join
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);

    // free the semaphore
    sem_destroy(&mutex);
    return 0;
}

您可以将所需的初始化作为全局变量添加到此实现中。