Cogs and Levers A blog full of technical stuff

Unix IPC: Message Queues

Message queues are pretty common structures for inter-process communication. A common queue is created by one of the processes, from there it can be connected to by any other process and have messages submitted to it.

This snippet shows the creation of a queue and sending a message. Another block below this will show receiving a message from the queue.

char buf[20];
int msqid;
key_t key;

/* --- message sender --- */

/* make a key */
key = ftok("first", 'B');
/* create the message queue */
msqid = msgget(key, 0644 | IPC_CREAT));

/* put a message onto the queue */
msgsnd(msqid, &buf, 20, 0);

/* destroy (remove) the message queue */
msgctl(msqid, IPC_RMID, NULL);


/* --- message receiver --- */

/* make a key */
key = ftok("second", 'B');
/* connect to the message queue */
msqid = msgget(key, 0644);
/* receive the message off the queue */
msgrcv(msqid, &buf, 20, 0, 0);

Further reading

Unix IPC: Memory Mapped Files

This snippet will show you how to open a file and map it into memory.

int fd, pagesize;
char *data;

/* open the file */
fd = open("somefile", O_RDONLY);

/* get the current page size */
pagesize = getpagesize();

/* map the 2nd page into memory */
data = mmap((caddr_t)0, pagesize, PROT_READ, MAP_SHARED, fd, pagesize);

/* start using the data pointer */

Further reading

The mmap system call

Unix IPC: Locking Files

Locking a file helps assure your program that no other processes can tamper with it or a region of it.

This snippet will show you how to lock and unlock a file.struct flock fl; int fd;

/* fill out the lock structure */
fl.l_type   = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start  = 0;
fl.l_len    = 0;
fl.l_pid    = getpid();

/* open the file */
fd = open("filename", O_WRONLY);

/* lock the file */
fcntl(fd, F_SETLKW, &fl);

/* --- complete any work here with the file locked --- */

/* unlock the file now */
fl.l_type   = F_UNLCK;
fcntl(fd, F_SETLK, &fl);

Further reading

The fcntl system call

Unix IPC: Forking and Waiting

In order to create processes within the Unix environment, you must fork. Forking a process establishes the parent / child relationship which is where the waiting comes into it. All good parents wait for their children to die before terminating.

It’s just good manners, you know?

So, the snippet for this will be a fork and wait set:

/* fork execution here */
pid_t pid = fork();

int child_exit_code;

if (pid < 0) {
 /* -1 indicates that fork failed */
 exit(1);
} else if (pid == 0) {
 /* 0 indicates that this is the child process */
 exit(0);
} else {
 /* the pid being returned indicates it's the parent */
 /* wait for the child to finish and
    capture its exit code */
 wait(&child_exit_code);
}

Further reading

Calling Assembly from C (32 bit)

One of the biggest advantages of being able to write assembly code is to optimise any bits of your application that you want. That way you can maintain your code base in a half-sane language (like C) and roll your sleeves up to speed up the smaller parts that you want.

This blog post will show you how to call a routine that you’ve defined in assembly language from your C code. The example that I’ll show has been done in a Linux environment using NASM as the assembler and GCC for the C compiler.

First of all, let’s write our C program. This will simply add two integers and output the results using printf.

#include <stdio.h>

/** Forward declaration for our add function */
int add(int, int);

int main() {

   /* add some numbers */
   int x = add(5, 4);

   /* print the result out */
   printf("5+4=%d\n", x);

   return 0;
}

There isn’t anything of great interest in here. We have a forward declaration for our add function. That’s about it. Now we have to supply an implementation for our add routine. For this we’ll be using assembly language.

global add

section .text

add:
   mov   eax, [esp+4]    ; get the 1st param
   mov   ecx, [esp+8]    ; get the 2nd param
   add   eax, ecx        ; add them together
                         ; leaving the return value in eax

   ret

This is all pretty straight forward. We define a symbol “add” as global. In the code (or .text) section, we supply an implementation for it. The trickiest part here is being able to retrieve parameters that are passed in from the C level. You can see that we’re addressing stack pointer to do so.

Add takes two parameters. In this scenario (c-calling convention) the parameters to the function are pushed onto the stack in reverse order. So, the second parameter goes onto the stack first and then the first. Once all of the pushing has complete, the first parameter is at [esp+4] and the second is at [esp+8]. Remember - an integer (on this architecture) has 4 bytes (32 bits).

Return values are always left in eax. You’ll see that after the arithmetic completes, it’ll be eax that holds the answer. It’s what will be given back to the caller.

Finally, all we need to do is compile these files and link together their object files. We do this from the Linux console with the following commands.

gcc -m32 -c -g add.c -o add.o
nasm -felf32 maths.asm -o maths.o
gcc -m32 add.o maths.o -o add

We’re done. I’ll be doing another one of these sorts of blog posts shortly demonstrating how we’ll do this in the 64-bit arena.