Networking - C Inter-Process Communication in Linux
Hey it's me again drifter1! Today we will get into Inter-Process Communication or IPC for short in Linux using C Language. We will start out talking about how we set up a shared memory location, then get into pipes and lastly talk about message queues. Off course we can also use Sockets, but I will get into this topic separately later on! So, let's get started!
Shared memory:
A shared memory lets 2 or more processes access data in the same logical memory address. It's one way of transfering data from one process to another. We first create a shared memory segment and then any process can attach to it so that it has access to the data inside.
In C Linux we will use the library sys/shm.h that has functions that create, attach, de-attach and control (mostly delete) a shared memory.
We use shmget() to create a shared memory segment and the Code looks like this:
int shm_id; // id of shared memory
int key=1234; // key to access memory
const int size= 2048; // static size of memory
shm_id = shmget (key, size, IPC_CREAT | 0666);
The OR Calculation (|) that we put inside of the 3rd parameter gives access to all Users!
We use shmat() to attach to a shared memory address and the Code looks like this:
Datatype *shm; // address of shared memory
shm = (Datatype*) shmat (shm_id, 0, 0);
The Datatype can be anything we wish and it depends on the application and data that we want to transfer between the processes!
We use shmdt() to de-attach the memory and the Code looks like this:
shmdt (shm);
Lastly with shmctl() we can control the shared memory, but we moslty use it to delete the memory and the Code looks like this:
shmctl (shm_id, IPC_RMID, 0);
To access a value after creating and attaching we simply use the shared memory segment as a pointer/array that stores data! So, we could for example store an integer and read it and print it out using this lines of Code:
shm[0] = 5;
printf("%d\n", shm[0]);
I did this considering that the Datatype* previously was an int*!
Using all these Functions you can now easily create a shared memory segment and use it!
Pipes:
Pipes are another way of IPC! We use them when 2 Processes want to share data, but only in one direction. So, one process sends data to another process that receives it! We will use the stdio.h function pipe() only so that we communicate with parent-child relation.
To create a pipe for IPC between parent-child we use the following Code:
int file_pipes[2]; // store file descriptors of read/write
pipe(file_pipes); // create/open pipe
To send something through the pipe we do:
char buffer[20]; // what to send
int data_processed; // byte amount send
data_processed = write(file_pipes[1], buffer, 19);
To receive something through the pipe we do:
char buffer[20]; // what we received
int data_processed; // byte amount send
data_processed=read(file_pipes[0], buffer, 19);
The child process could also run a different code using exec functions. But, using this kind of Pipes makes only sense when one process sends data to another process and not vise versa. I don't use Pipes that much, but mostly shared memory and message queues that come next!
Message Queues:
Message queues are similar to Pipes, but don't have the complexity of opening/closing. We create a messaging Queue, send or receive things and then delete it. The library that we will use is sys/msg.h and it contains everything we need!
To create/attach to a Messaging Queue we use msgget() and the Code looks like this:
int mid; // queue id
key_t key; // queue key
key = ftok(".", 'z'); // we generate a key using ftok()
mid = msgget(key, IPC_CREAT | 0660);
For sending and receiving we define our own struct that contains information.
typedef struct{
long mtype;
char mtext[30];
}MESSAGE;
This struct for example contains the mtype and a mtext, but you can use anything you like! mtype is needed tho, cause a long parameter is needed in the receive function.
To send a message we use msgsnd() and the Code looks like this:
Message msg;
strcpy(msg.mtext, "ACK"); // add message
msg.mtype=(long)getppid(); // add type
msgsnd(mid, &msg, sizeof(msg.mtext), 0); // send message
To receive a message we use msgrcv() and the Code looks like this:
Message msg;
msgrcv(mid, &msg, sizeof(msg.mtext), (long)getpid(), 0) ; //receive message
printf("%s\n", msg.mtext); // print message
To control and mostly delete the Queue we use msgctl() and the Code looks like this:
msgctl(mid, IPC_RMID, (struct msqid_ds*)0);
Using these send and receive functions you can not only connect processes in parent-child relation, but also different processes by simple creating a message queue using msgget() and using the same on the other process to attach to it! Then we can simply send/receive in any way we like. And never forget to delete the Queue at the end!
This is actually it for today! I will get into more specific examples later on when we get into Classic Synchronization Problems! Next time we will talk about Signals and Signal Handlers to finish off the Basics of Communication inside of PC.
Until next time...Bye!