1.linux驱动开发,读取字符的函数
在Linux驱动开发中,处理字符设备的读写操作是常见的任务。字符设备是一种按字符流处理数据的设备,如串口终端、键盘等。为了读取字符数据,你需要实现字符设备驱动中的相应函数。以下是一个简单的字符设备驱动示例,重点介绍如何读取字符。
1. 引入头文件
首先,引入必要的头文件:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h> // for copy_to_user
#include <linux/slab.h> // for kmalloc and kfree
2. 定义设备结构体
定义一个结构体来保存设备信息:
#define DEVICE_NAME "chardev"
#define BUF_LEN 80
static int major; // major number for the device
static char msg[BUF_LEN]; // buffer to store the message
static char *msg_Ptr;
static int msg_Len;
static int msg_Read_Pos;
static int msg_Written;
3. 文件操作结构体
定义文件操作结构体,并实现其中的读函数:
static ssize_t chardev_read(struct file *filp, char __user *buffer, size_t len, loff_t *offset) {
int bytes_read = 0;
// Check if we have reached the end of the message
if (*msg_Ptr == 0) {
return 0; // End of file
}
// Adjust the length to make sure we don't read beyond the available message
if (*msg_Ptr + len > msg_Len) {
len = msg_Len - *msg_Ptr;
}
// Copy data to user space
if (copy_to_user(buffer, msg_Ptr, len)) {
return -EFAULT;
}
// Update the file offset
*msg_Ptr += len;
bytes_read = len;
return bytes_read;
}
static const struct file_operations fops = {
.owner = THIS_MODULE,
.read = chardev_read,
// You can add other file operations like write, open, release here
};
4. 初始化与退出函数
实现驱动的初始化和退出函数:
static int __init chardev_init(void) {
// Allocate space for the message
msg_Ptr = msg;
msg_Len = 0;
msg_Read_Pos = 0;
msg_Written = 0;
// Register the character device
major = register_chrdev(0, DEVICE_NAME, &fops);
if (major < 0) {
printk(KERN_ALERT "Failed to register the character device\n");
return major;
}
printk(KERN_INFO "Registered character device with major number %d\n", major);
return 0;
}
static void __exit chardev_exit(void) {
// Unregister the character device
unregister_chrdev(major, DEVICE_NAME);
printk(KERN_INFO "Unregistered character device with major number %d\n", major);
}
5. 模块加载与卸载
最后,定义模块加载与卸载宏:
module_init(chardev_init);
module_exit(chardev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple character device driver");
MODULE_VERSION("1.0");
6. 编译与测试
-
编写好上述代码并保存为
chardev.c
。 -
创建
Makefile
,内容如下:obj-m += chardev.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
-
在终端中运行
make
编译驱动。 -
使用
sudo insmod chardev.ko
加载驱动。 -
使用
sudo rmmod chardev
卸载驱动。
7. 创建测试程序
你可以编写一个简单的用户态测试程序来读取设备的数据,如下所示:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define DEVICE "/dev/chardev"
int main() {
int fd = open(DEVICE, O_RDONLY);
if (fd < 0) {
perror("Failed to open device");
return -1;
}
char buffer[80];
ssize_t bytesRead;
bytesRead = read(fd, buffer, sizeof(buffer) - 1);
if (bytesRead < 0) {
perror("Failed to read from device");
close(fd);
return -1;
}
buffer[bytesRead] = '\0';
printf("Read from device: %s\n", buffer);
close(fd);
return 0;
}
编译并运行这个测试程序,你应该能够读取字符设备中的数据。
以上就是Linux字符设备驱动开发中读取字符的基本示例。根据具体需求,你可以扩展和完善这个驱动,比如添加写函数、处理并发访问等。
2.“bytesRead = read(fd, buffer, sizeof(buffer) - 1);” 这里“-1”的作用
在C语言编程中,当使用read
函数从文件或设备读取数据时,read
函数的原型通常如下:
ssize_t read(int fd, void *buf, size_t count); |
其中,fd
是文件描述符,buf
是指向存储读取数据的缓冲区的指针,count
是希望读取的最大字节数。
在表达式read(fd, buffer, sizeof(buffer) - 1)
中,sizeof(buffer)
返回缓冲区buffer
的总大小(以字节为单位)。由于buffer
通常是一个字符数组,并且我们希望在其末尾保留一个空字符(\0
)以便将其作为C字符串处理,因此我们在调用read
时将count
设置为sizeof(buffer) - 1
。
这样做是为了确保在读取数据时不会覆盖缓冲区的最后一个字节,该字节用于存储空字符。这样,即使read
函数读取了sizeof(buffer) - 1
个字节的数据,缓冲区仍然可以以一个空字符结尾,从而保持其作为C字符串的有效性。
在读取操作之后,代码通常会将读取的字节数对应的字符位置设置为空字符:
buffer[bytesRead] = '\0'; // Null-terminate the string |
这样做是为了确保字符串是以空字符结尾的,这对于后续的字符串处理函数(如printf
、strlen
等)来说是必要的。
总结来说,sizeof(buffer) - 1
在read
函数调用中的作用是确保不会读取超过缓冲区容量减去一个字节的数据,以便在缓冲区中保留一个空字符的位置。