Skip to content

File Operations

File Operation Implementation

Character devices in Linux interact with user-space applications through a set of file operations. These operations—open(), read(), write(), and release()—define how the kernel handles device access. Proper implementation ensures reliable communication between user-space programs and the kernel module.


Implementing open()

The open() function is called when a user-space process opens the device file. It's used to initialize device-specific resources and track open file descriptors.

static int my_dev_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "my_dev: Device opened\n");
    // Increment usage count or initialize resources
    file->private_data = kzalloc(sizeof(struct my_dev_data), GFP_KERNEL);
    if (!file->private_data)
        return -ENOMEM;
    return 0;
}
  • Parameters:
  • inode: Points to the inode structure for the device.
  • file: Points to the file structure representing the open file.
  • Return: 0 on success, negative error code on failure.

Implementing read()

The read() function transfers data from the device to user-space. It must handle buffer copying and return the number of bytes read.

static ssize_t my_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {
    struct my_dev_data *data = file->private_data;
    ssize_t bytes_read = 0;

    if (*ppos >= data->buffer_size)
        return 0; // End of data

    if (count > data->buffer_size - *ppos)
        count = data->buffer_size - *ppos;

    if (copy_to_user(buf, data->buffer + *ppos, count))
        return -EFAULT;

    *ppos += count;
    bytes_read = count;
    printk(KERN_INFO "my_dev: Read %zd bytes\n", bytes_read);
    return bytes_read;
}
  • Parameters:
  • file: File structure.
  • buf: User-space buffer.
  • count: Number of bytes requested.
  • ppos: Pointer to the current offset.
  • Return: Number of bytes read, or negative error code.

Implementing write()

The write() function copies data from user-space to the device. It must validate input and update internal state.

static ssize_t my_dev_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
    struct my_dev_data *data = file->private_data;
    ssize_t bytes_written = 0;

    if (count > data->buffer_size - *ppos)
        count = data->buffer_size - *ppos;

    if (copy_from_user(data->buffer + *ppos, buf, count))
        return -EFAULT;

    *ppos += count;
    bytes_written = count;
    printk(KERN_INFO "my_dev: Wrote %zd bytes\n", bytes_written);
    return bytes_written;
}
  • Parameters:
  • file: File structure.
  • buf: User-space buffer.
  • count: Number of bytes to write.
  • ppos: Pointer to the current offset.
  • Return: Number of bytes written, or negative error code.

Implementing release()

The release() function is called when the device is closed. It's used to clean up resources and release memory.

static int my_dev_release(struct inode *inode, struct file *file) {
    struct my_dev_data *data = file->private_data;
    kfree(data);
    printk(KERN_INFO "my_dev: Device closed\n");
    return 0;
}
  • Parameters:
  • inode: Inode structure.
  • file: File structure.
  • Return: 0 on success.

The file_operations Structure

All file operations are grouped into a file_operations structure, which is registered with the kernel:

static struct file_operations my_dev_fops = {
    .owner = THIS_MODULE,
    .open = my_dev_open,
    .read = my_dev_read,
    .write = my_dev_write,
    .release = my_dev_release,
};
  • owner: Must be THIS_MODULE to ensure the kernel knows which module owns the operations.
  • Function pointers: Link each operation to its implementation.

Key takeaways

  • Implement open(), read(), write(), and release() to handle device interaction.
  • Use copy_to_user() and copy_from_user() for safe data transfer between kernel and user-space.
  • Always manage resource allocation and deallocation in open() and release().
  • The file_operations structure ties all operations together and must be registered with the kernel.