在Ubuntu 22.04.2 LTS系统下使用C语言编写内核模块文件chardev.c和Makefile文件,实现简单的字符设备驱动程序,要求能对字符设备执行打开、读、写、I/O控制和关闭这些基本操作,然后编写测试程序test.c,访问创建的字符设备文件,并使用gcc编译这个字符设备文件然后运行
时间: 2024-03-03 17:51:54 浏览: 70
最简单的嵌入式字符设备驱动源代码和Makefile编译文件
好的,您的问题是如何在Ubuntu 22.04.2 LTS系统下使用C语言编写内核模块文件chardev.c和Makefile文件,实现简单的字符设备驱动程序,然后编写测试程序test.c,访问创建的字符设备文件,并使用gcc编译这个字符设备文件然后运行。下面是一个简单的实现过程和步骤:
1. 创建chardev.c文件并编写内核模块代码
```c
#include <linux/module.h>
#include <linux/fs.h> // 定义file_operations等
#include <linux/uaccess.h> // copy_to_user等
#define DEVICE_NAME "chardev" // 设备名称
static int major_num; // 主设备号
static int device_open_count = 0; // 设备打开计数器
// 设备打开函数
static int device_open(struct inode *inode, struct file *file)
{
device_open_count++;
printk(KERN_ALERT "Device has been opened %d time(s)\n", device_open_count);
return 0;
}
// 设备关闭函数
static int device_release(struct inode *inode, struct file *file)
{
printk(KERN_ALERT "Device has been closed\n");
return 0;
}
// 设备读函数
static ssize_t device_read(struct file *file, char __user * buffer, size_t length, loff_t * offset)
{
char *msg = "Hello, this is chardev!\n";
int bytes_read = 0;
while (length && *msg) {
put_user(*(msg++), buffer++);
length--;
bytes_read++;
}
return bytes_read;
}
// 设备写函数
static ssize_t device_write(struct file *file, const char __user * buffer, size_t length, loff_t *offset)
{
printk(KERN_ALERT "Sorry, this operation isn't supported yet.\n");
return -EINVAL;
}
// 设备I/O控制函数
static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
printk(KERN_ALERT "Sorry, this operation isn't supported yet.\n");
return -EINVAL;
}
// file_operations结构体,定义了设备的基本操作函数
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.unlocked_ioctl = device_ioctl,
.open = device_open,
.release = device_release,
};
// 加载模块函数
static int __init chardev_init(void)
{
major_num = register_chrdev(0, DEVICE_NAME, &fops);
if (major_num < 0) {
printk(KERN_ALERT "Failed to register a major number\n");
return major_num;
}
printk(KERN_INFO "Chardev has been registered with major number %d\n", major_num);
return 0;
}
// 卸载模块函数
static void __exit chardev_exit(void)
{
unregister_chrdev(major_num, DEVICE_NAME);
printk(KERN_INFO "Chardev has been unregistered.\n");
}
module_init(chardev_init);
module_exit(chardev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple character device driver");
```
2. 创建Makefile文件并编写编译规则
```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
```
3. 编译内核模块
在终端中进入chardev.c和Makefile所在的目录,使用以下命令编译内核模块:
```bash
make
```
如果没有错误,会生成chardev.ko文件。
4. 编写测试程序test.c
```c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define DEVICE_NAME "/dev/chardev" // 设备文件路径
#define BUF_SIZE 256 // 读写缓冲区大小
int main()
{
int fd;
char buf[BUF_SIZE];
fd = open(DEVICE_NAME, O_RDWR); // 打开设备文件
if (fd < 0) {
printf("Failed to open the device file\n");
return -1;
}
// 读取设备文件中的内容
if (read(fd, buf, BUF_SIZE) < 0) {
printf("Failed to read from the device file\n");
return -1;
}
printf("%s", buf); // 打印读取的内容
close(fd); // 关闭设备文件
return 0;
}
```
5. 编译测试程序
在终端中进入test.c所在的目录,使用以下命令编译测试程序:
```bash
gcc -o test test.c
```
6. 运行测试程序
在终端中进入test.c所在的目录,使用以下命令运行测试程序:
```bash
./test
```
如果一切正常,应该能够在终端中看到设备文件中的内容被打印出来了。
注意:在运行测试程序之前,需要确保已经加载了chardev模块,可以使用以下命令加载模块:
```bash
sudo insmod chardev.ko
```
阅读全文