Creating a processes and IPC (Inter process communication)
In this example we will show how to create a new process and communicate with it in a portable way using the Ecore_exe module.
In this example we will have two process and both will communicate with each other using messages. A father process will start a child process and it will keep sending messages to the child until it receives a message to quit. To see the full source use the links:
Let's start the tutorial. The implementation of the child it's pretty simple. We just read strings from stdin and write a message in the stdout. But you should be asking yourself right know. "If I'm receiving data from an other
process why I'm reading and writing in stdin/stdout?". That's because, when you spawn a process using the Ecore_Exe module it will create a pipe between the father and the child process and the stdin/stdout of the child process will be redirected to the pipe. So when the child wants to receive or send data to the father, just use the stdin/stdout. However the steps to send data from the father to the child is quite different, but we will get there.
The child will register a fd handler to monitor the stdin. So we start registering the ecore FD handler:
_fd_handler_cb,
NULL, NULL, NULL);
Ecore_Fd_Handler * ecore_main_fd_handler_add(int fd, Ecore_Fd_Handler_Flags flags, Ecore_Fd_Cb func, const void *data, Ecore_Fd_Cb buf_func, const void *buf_data)
Adds a callback for activity on the given file descriptor.
Definition: ecore_main.c:1449
@ ECORE_FD_READ
Fd Read mask.
Definition: Ecore_Common.h:1388
If you don't remember the parameters of ecore_main_fd_handler_add, please check its documentation.
Now that we have our handler registered we will start the ecore's main loop:
void ecore_main_loop_begin(void)
Runs the application main loop.
Definition: ecore_main.c:1311
Now let's take a look in the callback function. Its a simple function that will read from stdin 3 times and at the third time will say to the father: "quit".
{
static int numberOfMessages = 0;
char message[BUFFER_SIZE];
if (!fgets(message, BUFFER_SIZE, stdin))
numberOfMessages++;
if (numberOfMessages < 3)
{
printf("My father sent this message to me:%s\n", message);
fflush(stdout);
}
#define ECORE_CALLBACK_RENEW
Return value to keep a callback.
Definition: Ecore_Common.h:153
unsigned char Eina_Bool
Type to mimic a boolean.
Definition: eina_types.h:527
#define EINA_UNUSED
Used to indicate that a function parameter is purposely unused.
Definition: eina_types.h:339
else
{
printf("quit\n");
fflush(stdout);
}
#define ECORE_CALLBACK_DONE
Return value to stop event handling.
Definition: Ecore_Common.h:156
void ecore_main_loop_quit(void)
Quits the main loop once all the events currently on the queue have been processed.
Definition: ecore_main.c:1321
}
int
main(void)
{
goto error;
#ifdef _WIN32
_fd_handler_cb,
NULL);
#else
_fd_handler_cb,
NULL, NULL, NULL);
#endif
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
Ecore_Win32_Handler * ecore_main_win32_handler_add(void *h, Ecore_Win32_Handle_Cb func, const void *data)
Creates a Ecore_Win32_Handler object and add it to the win32_handlers list.
Definition: ecore_main.c:1587
EAPI int ecore_shutdown(void)
Shuts down connections, signal handlers sockets etc.
Definition: ecore.c:371
EAPI int ecore_init(void)
Sets up connections, signal handlers, sockets etc.
Definition: ecore.c:230
You may notice that we are sending the messages to stdout, and our father will receive it. Also our string must have a "\n" because the string will be buffered in the pipe until it finds EOF or a "newline" in our case we won't have a EOF unless we close the pipe, so we use the "\n" char.
One more thing, we use fflush(stdout) because probably our message won't fill our entire buffer and the father would never receive the message. So we use this function to flush the buffer and the father can receive as fast as possible.
Now that we have our child ready, let's start our work in the father's source code.
We start creating the child process like this:
Ecore_Exe * ecore_exe_pipe_run(const char *exe_cmd, Ecore_Exe_Flags flags, const void *data)
Spawns a child process with its stdin/out available for communication.
Definition: ecore_exe.c:64
@ ECORE_EXE_PIPE_READ
Exe Pipe Read mask.
Definition: ecore_exe_eo.h:50
@ ECORE_EXE_PIPE_WRITE
Exe Pipe Write mask.
Definition: ecore_exe_eo.h:51
@ ECORE_EXE_PIPE_READ_LINE_BUFFERED
Reads are buffered until a newline and split 1 line per Ecore_Exe_Event_Data_Line.
Definition: ecore_exe_eo.h:53
With the command above we are creating our child process, the first parameter is the command to be executed, the second are the pipe flags and in our case we will write and read in the pipe so we must say what we are doing in the pipe. You may notice the flag ECORE_EXE_PIPE_READ_LINE_BUFFERED, this means that reads are buffered until I find a newline. And the third parameter is data that we would like to send to the process in its creating. This case we are sending nothing, so just use NULL.
Then we check if the process was created:
if (childHandle == NULL)
{
fprintf(stderr, "Could not create a child process!\n");
}
After this we get the PID of the child process and just print it in the screen. The PID stands for Process identification. This is just an internal identifier of your process:
if (childPid == -1)
fprintf(stderr, "Could not retrieve the PID!\n");
pid_t ecore_exe_pid_get(const Ecore_Exe *exe)
Retrieves the process ID of the given spawned process.
Definition: ecore_exe.c:215
The way that Ecore_exe works is: when we want to read data sent from our child we must use an ecore event. So let's start register our event listener:
Now to send messages to our child we will use a timer, so every 1 second we will send a message to the child.
After all this we start the main loop. Now let's pass to the callback functions.
Now we will see how we actually send the data and receive it. Let's start with _sendMessage:
_sendMessage(void *data)
{
static int numberOfMessages = 0;
char msg[BUFFER_SIZE];
sprintf(msg, " Message: %d\n", numberOfMessages);
numberOfMessages++;
fprintf(stderr, "Could not send my name to the child\n");
else
printf(
"I'm the father and I sent this message to the child: %s\n", msg);
}
Eina_Bool ecore_exe_send(Ecore_Exe *exe, const void *data, int size)
Sends data to the given child process which it receives on stdin.
Definition: ecore_exe.c:115
#define EINA_TRUE
boolean value TRUE (numerical value 1)
Definition: eina_types.h:539
Opaque handle to manage Ecore Exe objects.
We use ecore_exe_send to send data to the child process, it's pretty simple. To know what the parameters stands for, check the docs.
- Note
- The function ecore_exe_send will never block your program, also there is no partial send of the data. This means either the function will send all the data or it will fail.
Now let's take a look in our event callback and see how we retrieve the messages.
{
char msg[BUFFER_SIZE];
if (dataFromProcess->
size >= (BUFFER_SIZE - 1))
{
printf("Data too big for bugger. error\n");
}
Ecore exe event data structure.
Definition: ecore_exe_eo.h:33
int size
The size of this data in bytes.
Definition: ecore_exe_eo.h:37
strncpy(msg, dataFromProcess->
data, dataFromProcess->
size);
msg[dataFromProcess->
size] = 0;
if (strcmp(msg, "quit") == 0)
{
printf("My child said to me, QUIT!\n");
}
void * data
The raw binary data from the child process received.
Definition: ecore_exe_eo.h:36
It's just like an normal event, we get a reference to Ecore_Exe_Event_Data, extract the data and then show it in the screen.
And that's it, after all it's not complicated to create a process and communicate with it.