Tuesday, July 20, 2010

Shared memory and forking in C

Something rather interesting happened at work today. One of my colleagues was going through his book shelf and found his original copy of The Unix Programming Environment (Kernigham & Pike 1983). slipped in one of the pages was a slip of paper from an old email - the date was December 1985. There was no writing in the email, just a C program that appeared to demonstrate shared memory and fork(). The code took a bit of cleaning up, and I've added some bits in to improve it as a demonstration, but here goes.

There are two main elements to this. A simple structure, that we're going to change in the child (and see the change in the parent. And a static int that we're going to change in the parent, but not see change in the child.

Our program is going start by defining out structure and int.
struct msg {
 int i;
 char str[100];
} msg
static int statInt = 1;
Then create some shared memory to put a single instance of the structure in
key = getpid();
shmflg = IPC_CREAT | 0770;
shmid = shmget(key, sizeof(msg), shmflg
Next, we need to attach the shared memory block to our process and we'll write some stuff to it.
segadr = (struct msg*)shmat(shmid, 0, 0);
segadr->i = 10;
sprintf((char*)segadr->str,"A message from parent %d\n", getpid());
Now we're going to call fork and depending on the return, we'll know if we are in the child or parent process.
if (fork() == 0) {
    /* Is child*/
}
else {
    /* Is parent */
}
The Child
Our child is now going to print the value of statInt and is also going to attach itself to the shared memory block above, then read from it.
printf("In Child: statInt %d\n", statInt);
shmid = shmget(key, sizeof(msg), 0);
chdadr = (struct msg *)shmat(shmid, 0, 0);
fprintf(stdout, "This is child %d, gets %s\n", getpid(),chdadr->str)
Now our child is going to write back a message
sprintf((char*)chdadr->str, "A message from child\n");
printf("In Child: We've written a message back\n");
And then detach itself from shared memory
if (shmdt(chdadr) == -1) {
    perror("Child - detach");
}
We're now going to sleep for 1 second, then look at statInt one last time, to see if it's changed.
sleep(1);
printf("In Child: statInt %d\n", statInt);
The Parent
Our parent (the block of code in the else) Is going to start by changing statInt to 2. The it's going to wait for the child to finish.
statInt = 2;
printf("In Parent: We just changed statInt to %d\n", statInt);
wait(&stat)
wait() will block until the child dies, at this point we're going to read from the shared block, we don't need to attach again because our Pid hasn't changed.
fprintf(stdout, "In parent: segadr = %s\n", segadr->str);
Then finally, like good citizens, we're going to mark our shared block to be destroyed.
cmd = IPC_RMID;
if (shmctl(shmid, cmd, buf) == -1) {
    perror("Parent - remove");
}

That's the whole thing done. What you should see, (depending on timing), is the parent change statInt, but the child won't see the change. That's because fork() takes a snapshot of the state. So the child wont' see any changes in that state, except by using shared memory. You'll see it read from this and the parent will see the change made by the child. This is an example:
In parent: shmid =  40730676
shmget errno Success
shmat errno Success
In Parent: We just changed statInt to 2
In Child: statInt 1
In child: shmid = 40730676
In Child: shmat errno Success
This is child 6751, gets A message from parent 6749

In Child: We've written a message back
In Child: statInt 1
In parent: segadr = A message from child

You can see the full code listing on my public git repo This includes some debug printing, and some comment about what I had to change in the original to get it working.

Wii Nunchuck for arduino with Python serial reader

Today I followed the excellent tutorial on Windmeadow about how to read data from a wii nunchuck using an arduino. It's my first real dive into electronics. But I'll do my best to explain.

My setup used an arduino duemilanove. The advantage compared to the board used in the Windmeadow post is that the duemilanove actually has a 3.3V supply (which is what the nunchuck wants apparently). I already had some male-male jumper wires so I didn't need to strip apart my nunchuck, I was able to push the wires into the right places. If you can imagine the back of the nunchuck connector looking like this:
Clock Empty Ground
Empty
3.3V Empty Data

Then the connections need to be made to the arduino as below.

Wii Arduino
Top Left (Clock) Analog In 4
Top Right (Ground) Ground4
Bottom Left (3.3v) 3.3V
Bottom Right (Data) Analog In 5

From this point you should be able to follow the code in the Windmeadow post to get some working firmware, The only alteration I made was to print to serial in a way that was going to be easy to parse:
void print_for_python(int x, int y) {
    Serial.print(0x00, BYTE);    
    Serial.print(x, BYTE);
    Serial.print(0x01, BYTE);
    Serial.print(y, BYTE);
}

I could then write a simple python script using pySerial
import serial
import struct
ser = serial.Serial('/dev/ttyUSB1')
ser.baudrate = 19200
print ser.portstr
while True:
    line = ser.read(1)
    b = struct.unpack('<B', line)[0]
    if b == 0x00:
        x = ser.read(1)
        x = struct.unpack('<B', x)[0]
        print 'X: %s' % x
    elif b == 0x01:
        y = ser.read(1)
        y = struct.unpack('<B', y)[0]
        print 'Y: %s' % y
    else:
        print b b

This example only uses the joystick, but I've since modified it to read the button presses as well and I will be testing it out on our companies robot this week. The results I've got seem pretty good, there's no noticeable delay between using the joystick and seeing the results in my python script.




Tuesday, July 6, 2010

A Lesson in talking to customers

Last week we got a visit from some of our suppliers. They had a new version of firmware that they needed to role out across their existing hardware (it seems a component had changed which required them to update the firmware). As is quite understandable, they had taken this opportunity to add loads more features that they wanted to discuss with us, and help us through some of the other code changes we needed to make to support this level of firmware.

The trouble was, all of these features were useless to us, it was heartbreaking. I understand the pride you feel when you are showing of a feature that you think is great, i get it a lot. But these features weren't needed by us at all. But it's too late. It now looks like we're being forced into an upgrade - which is going to take a lot fo effort on the part of our company (development and roll out/ upgrade costs)and are unlikely to go with this supplier on the next project we do.

It goes to show. Talk to your customers!