5

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?

Manuel Durando
  • 1,493
  • 3
  • 19
  • 30
  • I don't understand why both instances don't wait forever. You create the semaphore with no units, then wait on it? – Martin James Sep 24 '14 at 12:08
  • If both programs are executed on the host machine, only the first one create a semaphore: since the name passed to the `sem_open()` is the same, the second program does not create a new semaphore but instead uses the one created by the first application. Then the second application wait on the semaphore that is unlocked by the first one with the `sem_post()` and so on... – Manuel Durando Sep 24 '14 at 12:11
  • As you probably tried already, running both on the same host as standalone programs works fine (the second program waits, then does the `sem_post()` operation and closes the semaphore). The problem happens really when you try to share the POSIX semaphore between the host and Docker, which by the way uses resource isolation. It's supposed to work that way, at least by default. I don't know if there is a way to change this behavior. – Jay Sep 24 '14 at 12:14
  • @Jay Indeed the question is: *is it possible in some way to use POSIX semaphores between Docker containers or between a container and the host?* – Manuel Durando Sep 24 '14 at 12:22
  • see related question http://stackoverflow.com/questions/23889187/is-it-possible-to-share-memory-between-docker-containers – Thomasleveil Sep 27 '14 at 01:03
  • @Thomas that question is also mine... – Manuel Durando Sep 27 '14 at 09:00
  • @ManuelDurando lol sorry about that. I guess you found no evidence that docker made progress regarding namespace sharing then – Thomasleveil Sep 27 '14 at 09:09
  • @Thomas I modified and recompiled Docker excluding the IPC namespace in order to get shared memory by `shmget()` etc. – Manuel Durando Sep 30 '14 at 07:42
  • Glad you got it working in the end. You should answer your own question detailing what you did and why, this will surely help more users – Thomasleveil Sep 30 '14 at 08:35

1 Answers1

2

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.

Community
  • 1
  • 1
Manuel Durando
  • 1,493
  • 3
  • 19
  • 30