Skip to content
Advertisement

Why am I getting an error message when trying to “cat” my char device driver?

I’ve written a simple character device driver for Linux.

It is a simple message storing/retrieving system where the messages are stored in kernel space.

I should be able to do something like this:

echo "message 1" > /dev/mydevice

and then retrieve the message with

cat /dev/mydevice

The messages are stored in a queue.

When I try to retrieve a message that I hard coded in for testing (the message is “hello”), I get the following command line output:

cat /dev/mydevice
hellocat: /dev/mydevice: Resource temporarily unavailable

So I get the hello message as intended, but clearly I’m doing something not quite right.

Here is the function that handles device reads.

static ssize_t device_read(struct file *filp, char *buffer,
               size_t length, loff_t * offset) {
  unsigned long result;
  int message_size;
  struct message_list* message = pop_message(&global_message_list);

  if (!message) return -EAGAIN;

  message_size = message -> message_length;

  result = copy_to_user(buffer, message -> message, message_size);

  printk(KERN_ALERT "res: %lu, msg_size: %d, len: %dn", result, message_size, length);
  if (result == 0) return message_size;
  else return message_size - result;
}

Advertisement

Answer

The cat utility calls read more than once for each file, until it reaches EOF (which is signified by read returning 0).

This is because not all data may be available right away. If the file is bigger than cats internal buffer, it will of course need to call read multiple times to get the full data. Even if the number of bytes returned by read is less that the length of the buffer, it will need to call read again in case more data is available later (as could be the case if the input is a TTY or pipe). Therefore, you need to return 0 in order to get cat to think it’s at the end of the file and stop reading.

(For more detail on how cat works, you can check the source code, and the safe_read function.)

A simple way to handle this would be to put a zero-length message in your queue after each “real” message, so that the next read will return EOF. However, this won’t work correctly if you have multiple readers at the same time; in that case, one reader might read a message, then the other reads the EOF, then the first reads another message, so that one reader gets two messages and the other gets zero. It’s up to you and/or your instructor whether to make your device threadsafe.¹

This also indicates another potential problem with your code, which you only partially handle: if you have a message bigger than the buffer passed in to read, you discard the rest of the message instead of saving it for the next read. Again, this might be an acceptable shortoming, or not.


¹ I’m not sure if it’s possible to make it threadsafe; that depends on how well you can distinguish different readers, and I don’t know enough about kernel code or writing character devices to say if that’s possible.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement