2

I'm looking for advice on the best, simplest IPC method for this scenario:

  • Two programs on the same Linux server.
  • Program 1 has a variable with an integer in it that is periodically updated to a different value, say every 1 second.
  • Program 2 needs to periodically grab this value from Program 1. It just needs the latest value, not any queued ones.
  • Neither reading or writing should block.

I've looked into named pipes, message queues, Unix domain sockets, and haven't been able to find a solution that works yet. I've made it work with a named pipe, but it blocks. When I try to use the nonblocking O_NDELAY flag to open the pipe for writing in Program 1, Program 2 only gets "0"s when reading.

Any general advice on how to best go about this?

turbo
  • 1,233
  • 14
  • 36
user3618656
  • 49
  • 1
  • 7
  • How did you make it with a named pipe? Did you set the file descriptor to non-blocking? Did you use `poll(2)` ? Please show more code§ – Basile Starynkevitch Nov 01 '14 at 02:54
  • use the POSIX or unix messaging methods. give all messages the same priority, then read/save messages in a loop until no message available. then use the last read message. – user3629249 Nov 01 '14 at 03:06

3 Answers3

1

Sound like a job for shared memory. You would just put that value in the shared memory space.

Community
  • 1
  • 1
Turbo J
  • 7,563
  • 1
  • 23
  • 43
1

You can use shared memory; reading/writing integers are atomic on every real hardware.

When things are more complex and you want to do real IPC, you can let program A send the request in a pipe or socket, wait in B for this message and return the value then.

ensc
  • 6,704
  • 14
  • 22
  • If you are using C11, you can use [`atomic_store` and `atomic_load`](http://en.cppreference.com/w/c/atomic) if you don't want to rely on “every real hardware” assumptions. The ordering semantics they provide may be essential even if atomicity is already given by how the machine works. – 5gon12eder Oct 31 '14 at 22:13
  • Thank you very much! I didn't even know about this possibility. I have it working now and that was so much easier than trying to open a domain socket. – user3618656 Oct 31 '14 at 22:20
0

You can use shared memory. The below code provides the scenario that you described. The sender opens a shared memory object, requests the mmap segment and then writes the message into that a long with a counter. The receiver process reads the message along with the counter. I put them in a loop with sleep(1). (Note that the sender should be started first)

/* sender.c */
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>


#define PATH         "/testSendRecv1234"

struct msg_s {
    int counter;
    int val;
};
typedef struct msg_s msg_s;

int main(int argc, char *argv[])
{
    int shmfd;
    int size = (sizeof(struct msg_s));
    msg_s *shared_msg;
    int counter = 0;

    /* create the shared obj */
    if (0 > (shmfd = shm_open(PATH, O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG)))
        perror("shm_open()"), exit(1);

    /* make sure there is enough room */
    ftruncate(shmfd, size);

    /* request shared segment via mmap*/
    if (NULL == (shared_msg = (msg_s *) mmap(NULL, size,
                                PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0)))
        perror("mmap()"), exit(1);

    /* write the message  -- in a loop */
    while (++counter < 10)
    {
        shared_msg->val = 100 + counter;
        shared_msg->counter = counter;
        sleep(1);
    }
    /* clean up: rm the shared obj*/
    if (shm_unlink(PATH) != 0)
        perror("shm_unlink()"), exit(1);

    return 0;
}

/*receiver.c*/
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdlib.h>

#define PATH         "/testSendRecv1234"      

struct msg_s {
    int counter;
    int val;
};
typedef struct msg_s msg_s;
int main(int argc, char *argv[])
{
    int shmfd;
    int size = (sizeof(msg_s));
    msg_s *shared_msg;
    int counter;

    /* create the shared obj */
    if (0 > (shmfd = shm_open(PATH, O_RDWR, S_IRWXU | S_IRWXG)))
        perror("shm_open()"), exit(1);

    /* request the shared segment via mmap() */
    if (NULL == (shared_msg = (msg_s *)mmap(NULL, size,
                                PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0)))
        perror("mmap()"), exit(1);

    while (++counter < 10)
    {
        printf("Message[%d] is %d\n", shared_msg->counter , shared_msg->val);
        sleep(1);
    }

    return 0;
}

And finaly the Makefile:

all: receiver sender
receiver:
        gcc -Wall receiver.c -o receiver -lrt
sender: 
        gcc -Wall sender.c -o sender -lrt
clean:
        rm sender receiver

Some systems including Linux wrap these functions(shm_*) into a posix realtime library. The -lrt is added for that reason. You can run it with:

$ ./sender &
$ ./receiver
vim_
  • 1
  • Wow, thank you. I made it work using smhget before I saw your response, but I will try your solution too since from what I understand, mmap is the better solution. – user3618656 Oct 31 '14 at 22:19
  • Odd. When I run this using your suggested syntax, I get: [1] + Done(1) ./sender instead of any printf. – user3618656 Oct 31 '14 at 22:53
  • Sender does not print anything (see the code). With "&" we send it to the background and start the receiver process. You need to do just as above. Run sender and then receiver. Or use 2 terminals on the same host. – vim_ Oct 31 '14 at 23:41
  • That's what I did, and that's the response I get from running ./sender & ./receiver. If I run ./sender by itself, I get: shm_open(): File exists – user3618656 Nov 03 '14 at 18:03