Beej's Guide to Unix IPC (2010)
beej.us
beej.us
[0] http://beej.us/guide/bgc/ [1] http://beej.us/guide/bgnet/
But, armed with the knowledge of sockets, processes, etc. and how this correlates to performance/scaling, I've been able to carve out roles where I get to spend a decent amount of time working on problems that I get excited about.
But even before I was able to work the knowledge into my day job, nothing beats the dopamine rush of learning things that fascinate you! The rabbit holes that learning the basics of Unix have opened feel like they could occupy my hobbyist hours for the rest of my life.
On OS X there's notify(3) which is very nice. Any good options on Linux, other than writing my own socket server?
For example, if I was implementing something that is usually associated with user events (rare-ish, basically zero bandwidth, complex signal with stateful messaging semantics), I would probably just write a simple server to manage it all. This has an advantage of centralizing any messaging complexity and lets you manage any multi-message state easily. Rebroadcasting messages to allow peer-to-peer messaging would be a trivial addition. This would probably UNIX sockets if the connections are persistent (which can changed into AF_INET{,6} sockets easily, if you wanted to add network support).
For something that requires very low latency (e.g. audio/MIDI), in the past I have had to use shared memory, which has zero overhead once it is setup (no syscalls or context switches). Here, the need for low latency dictated the design. Of course, this means managing locks. Not fun, but a cost that is sometimes worth paying.
There really isn't a one-size-fits all solution.
edit:
Or, as troglobit said, TIPC. I keep forgetting that we now have it has an option. :(
I did originally use a Unix domain socket server, but that added a lot of complexity: one has to arrange for it to be launched, guard against the possibility that it gets stuck, version it, deal with permissions, etc.
My new solution on Linux is a total hack: there's a FIFO, and to post a notification, you write to it. Clients see that the FIFO became readable, and that change represents the notification. The sender then drains the data it wrote, so that the FIFO becomes unreadable again. This is a total abuse of FIFOs, but it's proven to be much simpler than trying to manage a separate server.
I've never heard of TIPC. From a little searching it looks like it's very capable but geared towards clusters, and is overkill for my use case. What do you think?
Depending on your permissions requirements[1], and if you really only need a signaling flag, have you considered the filesystem? Just touch a file in a well-defined directory named after the event that happened, and poll it periodically. Removing the file clears the flag. Signals can coalesce, but you should never drop any. You can poll a directory (that will normally be empty) without much CPU load (the directory inode will be cached most of the time). You could setup a multiple listeners by giving them their own "inbox" directory, like. e.g.:
# send a notification
touch "${HOME}/.${app_name}rc/messages/${destination}/${signal_flag_name}"
Using the filesystem opens up the possibility of a message sender being anything that can generate - even indirectly - an open(O_CREAT). Your signals also persist across programs shutdowns and crashes - you can send and receive even when the other side isn't running - and your state can persist across reboots. Also, you can leverage some of the guarantees provided by the kenel's vfs layer. For example, rename(2) is atomic, so you can send small data payloads by writing to a different name first. FLAG_PATH="${HOME}/.${app_name}rc/messages/${destination}/${signal_flag_name}"
# using the PID ($$) to not collide with other message senders
TEMP_PATH="${FLAG_PATH}-new-$$"
echo -e "foo=bar\nbaz=quux\ncount=42" > "${TEMP_PATH}"
mv "${TEMP_PATH}" "${FLAG_PATH}"
As an optional linux-specific feature, you can extend that technique to be event-driven (no polling loop) by telling the kernel to notify you about file-create events by listening to the directory (NOT the file) with inotify(7) for the IN_CREATE messages. Those events can be received either in a simple blocking style by letting poll(2) wake-up your process. Alternatively, you can receive events in in a non-blocking style with poll(2) if you give inotify_init1(2) the IN_NONBLOCK flag. The man oage inotify(7) should have an example.[1] this can get annoyingly complicated - but certainly not impossible - if you have to care about user/group permissions, esp. on the directory. Making a group specific to the message sending can help.
As, I originally said, though, there is certainly no one-size-fits-all solution, these are just a few of the available options, which may not be apropriate for your situation.
Beware: I tried that once, and it was unreliable. Only some clients woke up.
see: man killpg, man setpgid
also, "zeromq" has a pubsub mode, though I've never used it and I'm not sure about its limitations
From here: http://www.kohala.com/start/mcast.api.txt
"More than one process may bind to the same SOCK_DGRAM UDP port if the bind() is preceded by:
int one = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))
"In this case, every incoming multicast or broadcast UDP datagram destined to the shared port is delivered to all sockets bound to the port. For backwards compatibility reasons, THIS DOES NOT APPLY TO INCOMING UNICAST DATAGRAMS -- unicast datagrams are never delivered to more than one socket, regardless of how many sockets are bound to the datagram's destination port."
In general you have to be very careful how you handle it and consider various consistency and failures scenarios.
The main part of memory layout looks something like this:
[write_counter][......buffer.....]
This is owned and updated by the writer. Readers have a read_counter that they maintain in their own context (not shared).You'd probably have to declare this using the 'volatile' keyword. Otherwise compilers will optimize away access to part of this (seemingly) unused variables.
Then it works like this:
The writer_counter value is always counting up when writer writes. Data gets written to buffer. Then "write_counter" is incremented. Both reader and writer index into buffer by using {write|reader}_counter%buffer_size.
Also, note these counters also function as total counts of items written and so each reader cand determine how far ahead the writer is.
Another note: depending on the sizes of your counter it will not necessarily be updating atomically. Compiler could separate the update as multiple instructions and say, increment the lower part of the value, then the upper part. Writer could get pre-empted between those two instructions, so you could get this strange torn value. In this case because of the % you'd still fall into the range of the buffer. But, you might be reading data you didn't expect. Whether that works for your use case or not you'll have to see.
EDIT: Don't also forget about the slow and stupid multicast mechanism -- writing to a file. Some file operations can be atomics (renaming a file). And some operating systems let you watch the files for changes.
Thank you for your efforts, Brian!
Besides, I'm biased. "BJ" happens to be my wife's unofficial name. She's a remarkably smart person, so I was predisposed to think "BeeJ" would know what he's talking about and it turns out he did.