Kernel Korner - Easy I/O with IO Channels

IO Channels, a feature of the Glib library, make portable I/O simple and efficient. In this article, Robert shows how.

To be sure, this is an example rooted solely in explanation. It is silly to operate a pipe like this in a single application. Further, the program will continually read from and write to the pipe (you can kill the process with Ctrl-C). Nonetheless, this example serves a good purpose: it demonstrates event-driven programming and the utility of a main loop multiplexing I/O. The natural extension of this program would be to separate it into two processes, a consumer and a producer, and actually communicate interprocess over the pipe. Add a handful of other IO Channels, some GUI events, a few timers, and so on, to the main loop, and you will have a real program!

Creating an IO Channel

There are two ways to create a new IO Channel. The easiest method creates the IO Channel from an existing open file descriptor. The file descriptor can map to any object, including sockets and pipes:

GIOChannel *gio;

gio = g_io_channel_unix_new (fd);
if (!gio)
        g_error ("Error creating new GIOChannel!\n");

As its name suggests, this function is UNIX-specific. Another method is available for creating an IO Channel in a platform-independent manner:

GIOChannel *gio;
GError *err = NULL;

gio = g_io_channel_new_file ("/etc/passwd"
if (!gio)
        g_error ("Error creating new GIOChannel: %s\n",

The second parameter specifies the mode with which to open the file: one of r, w, r+, w+, a or a+. These values have the same meaning as with fopen(). For example, in this code snippet, we are asking to create a read-only IO Channel.

In our example program in Listing 1, we create two IO Channels using g_io_channel_unix_new(), one for each end of the pipe.

Creating a Watch

Given an IO Channel, creating a watch is easy:

guint ret;

ret = g_io_add_watch (gio, G_IO_IN, callback, NULL);
if (!ret)
        g_error ("Error creating watch!\n");

The first parameter, gio, is the IO Channel we want to watch. The second parameter is a mask of one or more conditions for which to watch. The condition G_IO_IN is true when there is data to be read without blocking. Other conditions are G_IO_OUT (data can be written without blocking), G_IO_PRI (urgent data is available to read), G_IO_ERR (an error occurred) and G_IO_HUP (the connection was hung up). The third parameter is the callback function that the Glib main loop will invoke when the event occurs.

Watch callbacks take the following form:

gboolean callback (GIOChannel *gio,
                   GIOCondition condition,
                   gpointer data);