Luke Gorrie's blog

21st Century Network Hacking

Shared Memory: A Moment of Appreciation

Today I’m feeling appreciative of shared memory.

There are a lot of communication mechanisms in widespread use: C APIs, system calls, RESTful services, JSON-RPC, AMQP, and plenty more besides. Some mechanisms survive, a lot influence new successors, and quite a lot of them just fall out of fashion and turn to dust. Shared memory is one with a timeless quality. Year after year it’s the glue that holds our computers together.

Shared memory is a simple mechanism. Many different programs are concurrently accessing the physical RAM chips in the ocmputer. The programs are written in completely different programming languages, they are executing on different microchips, and some of them are implemented as hardware rather than software. The programs all share a common abstraction: LOAD and STORE operations towards a big array of machine words. The rest is a matter of conventions, design patterns, and data structures. That’s all we need!

I really enjoy the versatility. How sweet it is to be able to drive hardware memory controller so directly.

Playing around

Just for fun, here’s a shared memory interface I’m playing with for ethernet networking. That is: to use shared memory to send and receive packets between a “host” and a “device”. The host and device can be written in any programming language, or indeed in hardware, and this is roughly how hardware packet I/O interfaces really look.

Here’s an example memory layout. I sketched it using C syntax and defined a packet ring buffer for transmision in each direction.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
enum {
    RING_SIZE = 1024,
    PACKET_SIZE = 1504,
};

// One ethernet packet.
struct packet
{
    unsigned int length;         // length of packet
    char data[PACKET_SIZE];      // on-the-wire packet data
};

// Ring buffer containing ethernet packets.
struct ring
{
    unsigned int head;           // position of first unread packet
    unsigned int tail;           // position of last unread packet
    unsigned int overflow_count; // number of packets dropped due to overflow
    struct packet packets[RING_SIZE];
};

// Ethernet device with two ring buffers for transmit and receive.
struct dev
{
    unsigned int magic;          // magic number identifying the structure
    unsigned int version;        // version of memory layout
    struct ring dev2host;        // ring of packets from host to device
    struct ring host2dev;        // ring of packets from device to host
};

Here’s a snippet using the data structure from C:

1
2
3
bool is_empty(struct ring ring) {
    return ring->head == ring->tail;
}

and here’s a snippet from LuaJIT:

1
2
3
function is_full (ring)
   return ring.head == (ring.tail + 1) % C.RING_SIZE
end

and with any luck I’ll find an excuse to see how it looks in a hardware description language like Verilog too.

That’s all. I really appreciate shared memory. Hardware people use it all the time. It’s not the most high level communication protocol around, but it is simple and accessible, and it can be fun for software people too. I think so, anyway :-)