Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible in some way to use POSIX semaphores between Docker containers or between a container and the host?

I'm trying to migrate a multiprocess application to Docker. Different processes will be placed in different Docker container.

The application uses shared memory to exchange data and semaphores to synchronize. I already recompiled Docker in order to do not use the IPC namespace and I effectively checked with sudo ipcs -m that the shared memory buffers are accessible from the different containers.

The problem is that semaphores are not working. I wrote these simple programs to check the behavior of POSIX semaphores in Docker:

/*  To be compiled with -lpthread */

#include <stdio.h>
#include <fcntl.h>
#include <semaphore.h>

int main(void) {

    int ret, val;
    sem_t * mySem;

    printf("[ONE] Opening the semaphore...\n");

    mySem = sem_open("sem1", O_CREAT, 0777, 0);
    if (mySem == SEM_FAILED) {
        printf("[ONE] Error on sem_open()\n");
        return -1;
    }

    ret = sem_post(mySem);

    getchar(); // Awful way to block execution of [ONE] for a while...

    printf("[ONE] Waiting for [TWO]...\n");
    ret = sem_wait(mySem);
    printf("[ONE] Wait ended\n");

    ret = sem_unlink("sem1");
    printf("[ONE] Semaphore destroyed\n");

    return 0;
}

The second program is:

/* To be compiled with -lpthread */

#include <stdio.h>
#include <fcntl.h>
#include <semaphore.h>

int main(void) {

    int ret;
    int val;
    sem_t * mySem;

    printf("[TWO] Opening the semaphore...\n");

    mySem = sem_open("sem1", O_CREAT, 0777, 0);
    if (mySem == SEM_FAILED) {
        printf("[TWO] Error on sem_open()\n");
        return -1;
    }

    ret = sem_getvalue(mySem, &val);
    printf("[TWO] Semaphore's value is %d\n", val);

    printf("[TWO] Waiting for [ONE]...\n");
    ret = sem_wait(mySem);
    printf("[TWO] Wait ended\n");

    printf("[ONE] Doing sem_post() on semaphore...\n");
    ret = sem_post(mySem);

    ret = sem_close(mySem);
    printf("[TWO] Semaphore closed\n");

    return 0;
}

In both I omitted lots of controls like if (ret != 0) {...} in order to maintain readability of the question.

I run the first program on the host machine, the second one in a Docker container. The result is that the second program waits forever...

The question is: is it possible in some way to use POSIX semaphores between Docker containers or between a container and the host?

like image 912
Manuel Durando Avatar asked Nov 01 '22 16:11

Manuel Durando


1 Answers

I solved the problem using shared memory between Docker containers as explained in this question.

The following code is a modified version of this tutorial.

File server.c

/* To be compiled with -lpthread */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <semaphore.h>

#define SHM_SIZE     1000

int main(void) {

    int shmid;
    key_t key;
    char *shm;

    sem_t * mySem;

    /* We'll name our shared memory segment "5678" */
    key = 5678;

    /* Create the segment.*/
    if ((shmid = shmget(key, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    /* Now we attach the segment to our data space */
    if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
        perror("shmat");
        exit(1);
    }

    /* Create a new semaphore */
    mySem = sem_open("sem1", O_CREAT, 0777, 0);

    /* Copy the semaphore on the shared memory segment */  
    memcpy(shm, mySem, sizeof(*mySem));

    /* Do stuff ... */

    /* REMEMBER TO USE THE SHARED MEMORY SEGMENT */
    /* AND NOT THE LOCAL mySem, USE (sem_t*)shm INSTEAD! */

    /* Finally, we wait until the other process 
     * changes the first character of our memory
     * to '*', indicating that it has read what 
     * we put there.
     */
    while (*shm != '*')
        sleep(1);

    /* Mark the memory segment to be destroyed */
    shmctl(shmid, IPC_RMID, NULL);

    /* Detach of the memory segment */
    shmdt(&shm);

    sem_unlink("sem1");

    return 0;
}

File client.c

/* To be compiled with -lpthread */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <semaphore.h>

#define SHM_SIZE     1000

int main(void) {

    int shmid;
    key_t key;
    char *shm;

    int ret, val;

    key = 5678;

    if ((shmid = shmget(key, SHM_SIZE, 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
        perror("shmat");
        exit(1);
    }

    /* SEMAPHORE IS IN THE SHARED MEMORY SEGMENT */
    /* USE (sem_t*)shm TO ACCESS IT */

    *shm = '*';

    shmdt(&shm);

    sem_close("sem1");

    return 0;
}

The code examples miss lots of controls due to readability purposes.

I run the server on the host machine, the client inside a Docker container and checked that the semaphore was accessible from both processes.

like image 160
Manuel Durando Avatar answered Nov 08 '22 08:11

Manuel Durando