The io_read handler is responsible for returning data bytes to the client after receiving an _IO_READ message. Examples of functions that send this message are read(), readdir(), fread(), and fgetc(). Let's start by looking at the format of the message itself:
struct _io_read { uint16_t type; uint16_t combine_len; uint32_t nbytes; uint32_t xtype; uint32_t zero; }; struct _io_read64 { uint16_t type; uint16_t combine_len; uint32_t nbytes; uint32_t xtype; uint32_t nbytes_hi; }; typedef union { struct _io_read i; struct _io_read i64; /* unsigned char data[nbytes]; */ /* nbytes is returned with MsgReply */ } io_read_t;
As with all resource manager messages, we've defined a union that contains the input (coming into the resource manager) structure and a reply or output (going back to the client) structure. The io_read handler is prototyped with an argument of io_read_t *msg—that's the pointer to the union containing the message.
Since this is a read(), the type member has the value _IO_READ or _IO_READ64. The client library uses the _IO_READ64 form only when the length is greater than 4 GB. The items of interest in the input structure are:
num_bytes = _IO_READ_GET_NBYTES(msg);
We'll create an io_read handler that actually returns some data (the fixed string "Hello, world\n"). We'll use the OCB to keep track of our position within the buffer that we're returning to the client.
When we get the _IO_READ message, the nbytes member tells us exactly how many bytes the client wants to read. Suppose that the client issues:
read (fd, buf, 4096);
In this case, it's a simple matter to return our entire "Hello, world\n" string in the output buffer and tell the client that we're returning 13 bytes, i.e., the size of the string.
However, consider the case where the client is performing the following:
while (read (fd, &character, 1) != EOF) { printf ("Got a character \"%c\"\n", character); }
Granted, this isn't a terribly efficient way for the client to perform reads! In this case, we would get msg->i.nbytes set to 1 (the size of the buffer that the client wants to get). We can't simply return the entire string all at once to the client—we have to hand it out one character at a time. This is where the OCB's offset member comes into play.