5

I haved a problem to complete my example when asked this question.
I searched for ways of implementing IPC in Google.
I can't decide which way is best for write my program.
I tried lots of implementations and have lots of complications with them.

I want to have:
1. parent process to manage child process - OK ( template )
2. parent and children must have implemented callback for new message signal
3. one process do not know message size from other process ( char * )

My code:

header.h:

#ifndef MESSAGES_H
#define MESSAGES_H

#include <stdio.h>
#include <stdlib.h>

// need here: some includes and definitions

inline char * read_message( /* need here: some params */ ) {
    // need here: read message function
}

inline char * send_message( /* need here: some params */ ) {
    // need here: send message function
}
#endif

parent.c:

#include "header.h"

// parent specyfic includes and definitions

void on_message( /* need here: some parameters */ ) {
    char *message = read_message( /* need here: some other parameters */ );
    // do something with / if message etc.
}

int runChild(key) {
    int pid = fork();
    if (pid == 0) {
        execl("./child", "./child", /* params here */, null);
    }else{
        return pid;
    }
}

int main(int argc, char *argv[]) {
    // need here: prepare IPC
    // need here: on new message event call function "on_message"
    int childPid = runChild(key);
    // loop - for example: gtk_main()
    // need here: close childs
}

child.c

#include "header.h"

// child specyfic includes and definitions

void on_message( /* need here: some parameters */ ) {
    char *message = read_message( /* need here: some other parameters */ );
    // do something with / if message etc.
}

int main(int argc, char *argv[]) {
    // need here: prepare IPC
    // need here: on new message event call function "on_message"
    int pid = getpid();
    int parentPid = getppid();
    printf("Child with pid %d is ready for messages from parent with pid: %d", pid, parentPid);
    // event loop - for example: gtk_main()
}

Which IPC way is better in that example program template ( safe and speed ) ? Can you share a really simple example that matches the above template ?

s77s
  • 304
  • 1
  • 2
  • 11
  • Its a bad practice to write function implementation other than inline in header file. – D3Hunter Dec 09 '14 at 14:58
  • 2
    In a comment below you state that you're using GTK+. GTK+ has [`GtkSocket`](https://developer.gnome.org/gtk3/stable/GtkSocket.html) and [`GtkPlug`](https://developer.gnome.org/gtk3/stable/GtkPlug.html) which allow one GTK+ application to embed widgets from another application (that uses GTK+ or Qt or other widgets that follow the [XEMBED](http://www.freedesktop.org/wiki/Specifications/xembed-spec/) specification). – Nominal Animal Dec 12 '14 at 14:42
  • If you use pipes, named pipes, or sockets for communication, you can use [`fcntl(descriptor, F_SETFL, O_ASYNC)`](http://man7.org/linux/man-pages/man2/fcntl.2.html) to set this descriptor to generate SIGIO signal; it is best to change it to a realtime signal using e.g. `fcntl(descriptor, F_SETSIG, SIGRTMIN+0)`. The signal handler, however, must only use [async-signal safe](http://man7.org/linux/man-pages/man7/signal.7.html) functions, or the application behaviour is undefined. – Nominal Animal Dec 12 '14 at 14:47

2 Answers2

17

There are a number of different ways to implement IPC. For a good comparison, see Stevens' books. The classic is 'Advanced Programming in the UNIX environment', but there is also 'UNIX Network Programming, Volume 2, Second Edition: Interprocess Communications'. I know it is sometimes not considered good form to point to references elsewhere, but whether this is an academic or commercial problem, most UNIX programmers would recognise Stevens as an invaluable resource.

That said, here are your main options for IPC:

  1. Use a pipe() between the processes. The format will always be stream-based; if you are sending datastructures, this can be a pain as you need to worry not only about serialization, but also about buffering and translating the 'packets' back into messages. Pipes are unidirectional, so you will need two for bidirectional communication.

  2. Use a named pipe or fifo. This allows many-to-one communication and for the fifo to persist after one end has quit. Otherwise as per (1).

  3. Use a socketpair between the processes - specifically a unix domain socket. Sockets are bidirectional. You can either use streaming sockets (SOL_STREAM), datagrams (unreliable, ordering not guaranteed - SOCK_DGRAM) or perhaps better sequenced reliable bidirectional packet communication (SOCK_SEQPACKET). Packet based communication means that you can (e.g.) put one datastructure in each packet.

  4. Use signals. Effectively you get to send one integer at a time. Signals do not mix well with threading, handling interrupted system calls is hard, and various race conditions make them unreliable unless you know what you are doing and are not too worried about portability. Best avoided in most cases.

  5. Use system V semaphores (semget etc.) or POSIX semaphores (sem_open etc.). Useful for sending signals between processes to achieve synchronization but not much more.

  6. Use shared memory (shmget etc.) - the same page(s) are made visible to multiple processes. You will need to combine with some method of synchronisation.

  7. System V message queues (msgget etc.) - maintain a queue of packets (messages) between two processes.

  8. Some combination of the above.

I've omitted some things only in forks of the kernel (e.g. Binder) or under development (e.g. KDBus).

Examples of and tutorials for nearly all the above can be found here.

Now, most of these could be used for the application you mention. It looks like you want to send variable size messages, so if you use a stream based protocol, the normal hack is to send the length of the packet as the first 1, 2 or 4 bytes (depending on the maximum length). Packet based protocols are easier here (obviously) but each have their own maximum packet size. Do you care about reliability? Do you care about portability? Do you care about speed? All of these are valid concerns when choosing between them.

Finally, an advantage to the FD-based methods (e.g. pipes, socketpairs) is that you can add them to a normal select() loop; if you have other things going on in your program, this may be helpful.

You asked in comments for some examples of socketpair code. I reiterate the comment at the top re getting hold of Stephens. In the absence of that:

  • Socketpair() in C/Unix shows a good example of setting up a socketpair for IPC using fork().
  • The tutorial mentioned above has a good section on socketpair() here.
Wyck
  • 10,311
  • 6
  • 39
  • 60
abligh
  • 24,573
  • 4
  • 47
  • 84
  • "Do you care about reliability?" and "Do you care about speed?" - Yes it's very important. "Do you care about portability? - At the start not so much, but later yes. I will then use the GTK+ libs that have own loop. – s77s Dec 14 '14 at 16:33
  • 1
    pipes and sockets are surprisingly fast. If you are are working with a GTK+ event loop, I would use a socketpair. – abligh Dec 14 '14 at 16:41
  • Thanks. Can you show an example ? Most important: read message when new message signal. – s77s Dec 14 '14 at 16:57
  • @s77s: I've added a couple of examples. – abligh Dec 14 '14 at 18:55
  • Last question: Is SIGIO in sa_flags good for catching new message signal in socketpair ( section 11.4 ) ? – s77s Dec 14 '14 at 21:29
  • @s77s: not if you can avoid it. See my comment above re the perils of signals. Assuming you are using `socketpair` or something else presenting FDs, you would be far better off with a conventional `select()` loop (or `poll()` if you want to be more modern than me). – abligh Dec 14 '14 at 21:32
  • I need something like singal and handler. Application do something and when new message is available do something other. I cannot wait for message ( which will freeze my program ). With this, is SIGIO in sa_flags good ? – s77s Dec 14 '14 at 22:14
2

Here's some setup code from a multiprocess program I recently wrote, with the use of select to provide non-blocking waiting. This also apparently is one of the better ways to do it in C++ because from what I gather file descriptors aren't well supported by the standard library...

// Parent
int main(int argc, char **argv) {

    // Pipe, fork, exec (to run robot in child)
    int toParent[2], fromParent[2];
    pipe(toParent);
    pipe(fromParent);

    // Redirect childs stdin/stdout
    if (fork()) { // parent
        close(toParent[1]); // [1] == write
        close(fromParent[0]); // [0] == read
    }
    else {
        close(toParent[0]);
        close(fromParent[1]);
        dup2(toParent[1], 1);
        dup2(fromParent[0], 0);
        close(toParent[1]);
        close(fromParent[0]);
        execl("../robot/robot", "../robot/robot", (char *) NULL);
    }


    FILE * output = fdopen(fromParent[1], "w");
    FILE * input = fdopen(toParent[0], "r");

    // Set up for select() read of input pipe
    fd_set set;
    struct timeval timeout;

    // Initialize the file descriptor set.
    FD_ZERO(&set);
    FD_SET(toParent[0], &set);

    // Initialize the timeout data structure
    timeout.tv_sec = 0;
    timeout.tv_usec = 10;

    while(1) {
        // Non-blocking read of pipe
        // NOTE: only expecting to read one pipe so no need to check which pipe got data
        if (select(toParent[0]+1, &set, NULL, NULL, &timeout) > 0) {
            // read the input pipe here
        }
        // Reset select FD -- maybe only do this when an input has been read?
        FD_ZERO(&set);
        FD_SET(toParent[0],&set);
}

The general idea is in to allow the child to communicate to the parent through its stdin/stdout (by using dup2()), and then use the FILE *output and input to write to child.The only caveat is that debug prints to stdout in child might cause unexpected behaviour if the parent isn't dealing with it, so in general its safest to print debug messages to stderr in child.

As for the callbacks, you can use select, which is something that is pretty well documented elsewhere.

chris
  • 4,840
  • 5
  • 35
  • 66
  • What is the reason of using pipe function twice? – pmverma Dec 09 '14 at 07:06
  • to create a separate pipe for each direction of data sending, pipes aren't generally bidirectional. – chris Dec 09 '14 at 07:25
  • How can I do it in async mode ? – s77s Dec 09 '14 at 09:19
  • what do you mean? the child and parent communicate asynchronously – chris Dec 09 '14 at 09:44
  • Pipes do indeed have a read and write end (pipe[0] is read and pipe[1] is write), however What chris is hinting at is that it's difficult to control synchronization of processes if they share a common pipe. For that reason you will often see people use separate pipes for sending and receiving, that way the other end of the pipe and call read() on the pipe, and it will be a blocking call until the pipe has data (from the sender). – SnakeDoc Dec 09 '14 at 15:47
  • With that said, I'd argue there is no "best" IPC, it depends on your application. Many would argue using Sockets for everything is best, but there are also Message Queues, and Shared Memory. Pipes are probably the simplest to implement in your program however. Just make sure to close them in an `atexit()` cleanup routine or similar. – SnakeDoc Dec 09 '14 at 15:49
  • 1
    Just use `socketpair` instead of pipes. – Maxim Egorushkin Dec 09 '14 at 16:05
  • @chris: "This blocks the program until input or output is ready on a specified set of file descriptors, or until a timer expires, whichever comes first." - I do not want this kind of functionality ( timer and blocks program ). select() is not asynchronous, I saw somewhere an sigaction function with first parametr SIGIO, but do not know much about this. Can you fill my template ? Thanks for your time. – s77s Dec 10 '14 at 22:29
  • do you mean select blocks the program? – chris Dec 10 '14 at 23:42
  • @s77s I added an example of how to use select with a timeout which worked for my pretty task where I needed very accurate response with as little blocking as possible – chris Dec 10 '14 at 23:48
  • @chris: I'm not sure if you understand me ... I'd like to write a sample application, the sources of which will use my graphics application (GTK +), with one program manages the rest. The program can send a message to the manager, and vice versa (for example, after clicking on the button). When a new message appears in the program should call the callback function. You wrote your loop ( instead gtk_main() ) that BLOCKS the program, which is what I did not want. – s77s Dec 11 '14 at 07:03