Linux字符设备:read、write和poll函数实现及完整代码
1. read函数
原型
ssize_t read(struct file *file, char __user *buf, size_t count, loff_t *pos);
实现步骤
- 检查用户缓冲区:使用
copy_to_user
将数据从内核空间复制到用户空间。 - 返回已读取的字节数:
- 如果数据长度少于请求长度,返回实际读取的字节数。
- 如果到达文件末尾,返回
0
。
注意事项
- 确保对用户缓冲区的访问是安全的。
- 返回值必须是已成功读取的字节数或错误码(如
-EFAULT
)。 - 考虑多线程情况下的并发访问,适当加锁。
示例代码
static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{char data[] = "Hello, World!\n";size_t datalen = strlen(data);if (*pos >= datalen)return 0;if (count > datalen - *pos)count = datalen - *pos;if (copy_to_user(buf, data + *pos, count))return -EFAULT;*pos += count;return count;
}
2. write函数
原型
ssize_t write(struct file *file, const char __user *buf, size_t count, loff_t *pos);
实现步骤
- 验证用户数据:使用
copy_from_user
将数据从用户空间复制到内核空间。 - 处理写入数据:通常会将数据存储到设备的内存区域或触发硬件操作。
注意事项
- 确保处理的是合法数据。
- 如果设备有缓冲区,需检查空间是否足够。
- 同样需要考虑多线程并发访问的问题。
示例代码
static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{char kernel_buf[128];if (count > sizeof(kernel_buf) - 1)count = sizeof(kernel_buf) - 1;if (copy_from_user(kernel_buf, buf, count))return -EFAULT;kernel_buf[count] = '\0';printk(KERN_INFO "Received from user: %s\n", kernel_buf);*pos += count;return count;
}
3. poll函数
原型
unsigned int poll(struct file *file, struct poll_table_struct *wait);
实现步骤
- 注册等待队列:使用
poll_wait
将设备的等待队列添加到wait
。 - 返回设备状态:返回一个位掩码,指示设备的可读性、可写性等状态。
注意事项
- 确保等待队列正确唤醒。
- 返回值可以是
POLLIN
(可读)、POLLOUT
(可写)等。 - 处理阻塞和非阻塞模式。
示例代码
static unsigned int my_poll(struct file *file, struct poll_table_struct *wait)
{unsigned int mask = 0;poll_wait(file, &my_wait_queue, wait);if (data_available)mask |= POLLIN | POLLRDNORM; if (space_available)mask |= POLLOUT | POLLWRNORM; return mask;
}
4. 完整代码示例
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/sched.h>#define DEVICE_NAME "my_device"
#define BUFFER_SIZE 128static int major;
static char device_buffer[BUFFER_SIZE];
static size_t buffer_len = 0;static wait_queue_head_t my_wait_queue;
static int data_available = 0;
static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{if (buffer_len == 0) {if (file->f_flags & O_NONBLOCK)return -EAGAIN;wait_event_interruptible(my_wait_queue, data_available);if (buffer_len == 0) return -EINTR;}if (count > buffer_len)count = buffer_len;if (copy_to_user(buf, device_buffer, count))return -EFAULT;buffer_len = 0; data_available = 0;return count;
}
static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{if (count > BUFFER_SIZE - 1)count = BUFFER_SIZE - 1;if (copy_from_user(device_buffer, buf, count))return -EFAULT;device_buffer[count] = '\0';buffer_len = count;data_available = 1;wake_up_interruptible(&my_wait_queue);return count;
}
static unsigned int my_poll(struct file *file, struct poll_table_struct *wait)
{unsigned int mask = 0;poll_wait(file, &my_wait_queue, wait);if (buffer_len > 0)mask |= POLLIN | POLLRDNORM; if (buffer_len < BUFFER_SIZE)mask |= POLLOUT | POLLWRNORM; return mask;
}
static struct file_operations my_fops = {.owner = THIS_MODULE,.read = my_read,.write = my_write,.poll = my_poll,
};
static int __init my_init(void)
{major = register_chrdev(0, DEVICE_NAME, &my_fops);if (major < 0) {printk(KERN_ALERT "Failed to register character device\n");return major;}init_waitqueue_head(&my_wait_queue); printk(KERN_INFO "Device registered with major number %d\n", major);return 0;
}
static void __exit my_exit(void)
{unregister_chrdev(major, DEVICE_NAME);printk(KERN_INFO "Device unregistered\n");
}module_init(my_init);
module_exit(my_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple character device with wait queue.");