Linux驱动(三):字符设备驱动之杂项
目录
- 一、Linux设备分类
- 二、设备号与字符设备的编码方式
- 1.设备号
- 2.字符设备的编码方式
- 三、杂项字符设备驱动的初级编写
一、Linux设备分类
Linux下一切皆文件,所有的硬件设备在Linux应用层中都会被抽象成文件,所有对硬件设备的操作到应用层中,也都会被抽象成文件的操作,设备文件通常会被放在/dev目录下。
类型 | 作用 |
---|---|
字符设备(Character Devices) | 字符(char)设备是个能够像字节流(类似文件)一样被访问的设备,由字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少要实现open、close、read和write的系统调用。 |
块设备(Block Devices) | 和字符设备类似,块设备也是通过/dev目录下的文件系统节点来访问。块设备(例如磁盘)上能够容纳filesystem。在大多数的Unix系统中,进行I/O操作时块设备每次只能传输一个或多个完整的块,而每块包含512字节(或2的更高次幂字节的数据)。 |
网络设备(Network Devices) | 任何网络事物都需要经过一个网络接口形成,网络接口就是一个能够和其他主机交换数据的设备(网络设备)。接口通常是一个硬件设备,但也可能是个纯软件设备,比如回环(loopback)接口。 |
详解:
1.字符设备
字符设备是Linux设备类型中占比最多的设备,除了块设备和网络设备,其他设备基本都是字符设备,我们常常写的驱动代码,也往往是字符设备。之所以叫字符设备,是因为操作该设备时是通过字节一位一位(字符流)的去操作的。适用于实时数据流处理和设备交互。文件符号为c
每一个字符设备在应用层的抽象文件也被叫做设备节点,在/dev下执行ls -l ,就可以看到很多创建好的设备节点。
– 代表普通文件
l 代表链接文件 ---- 快捷方式
c 代表字符设备
d 代表目录文件
b 代表块设备
p 代表管道
s 代表套接字
2.块设备
与字符设备不同,块设备以固定大小的数据块进行数据传输。块设备通常用于存储设备,如硬盘、固态硬盘(SSD)、U盘等。适用于需要高效和灵活数据存取的应用场景。文件符号为b
3.网络设备
网络设备用于连接和管理网络中的计算机和其他设备,网络设备的协同工作,确保网络的连接性、性能和安全性。Unix访问网络接口的方法仍然是给它们分配一个唯一的名字(比如eth0),但这个名字在filesystem中不存在对应的节点。内核和网络设备驱动程序间的通信,完全不同于内核和字符以及块驱动程序之间的通信,内核调用一套和数据包相关的函数socket,也叫套接字。
使用ifconfig命令,即可查看网络设备:
二、设备号与字符设备的编码方式
1.设备号
在 Linux 系统中,设备号用于唯一标识设备文件,以便内核正确地访问硬件设备。设备号包括主设备号和次设备号,它们分别用于区分设备类型和具体设备实例。
示例:
简单来讲,设备号就是用来区分具体的硬件设备的。主设备号是先对硬件设备大致分个类,先确定该硬件设备具体属于哪一类,而次设备号就是用来再细分到具体哪一个设备。
主设备号固定是10,次设备号是0-255
2.字符设备的编码方式
我们可以将操作系统大致分为三层,应用层、内核层以及硬件层。我们的所有界面操作都是应用层,所有的驱动代码都在内核层,所有具体的硬件在硬件层。应用层的所有操作,会先通过API接口反馈给驱动层,驱动代码运行后,最后才会反馈给具体的硬件进行需求实现。
作为一个嵌入式软件工程师,我们所编写代码的顺序就是先写驱动后应用层。
编写字符设备驱动代码的方法大致有三种:
1.杂项字符设备驱动。用于处理与内核不直接相关的设备或功能的驱动程序,适用于各种简单或虚拟设备的实现,并为开发者提供了一种简便的方式来处理字符设备
2.Linux2.6 字符设备驱动 。 在 Linux 2.6 内核中编写字符设备驱动提供了一个强大且灵活的方式来与设备进行交互。虽然这种方法有许多优点,如清晰的接口和与文件系统的集成,但也存在一些缺点,如复杂性、性能开销和安全问题。
3.早期经典字符设备驱动。太老了,几乎被淘汰。
三、杂项字符设备驱动的初级编写
目标:通过编写代码实现初级open、close、read、write的驱动编写以及应用层的简单调用。
应用:
app.misc.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd =0;
char buffer[100];
const char *data = "Hello, this is a test write!";
if(argc<2)
{
printf("请输入正确的参数\n");
return -1;
}
fd = open(argv[1],O_RDWR);
if(fd<0)
{
perror("open");
return -1;
}
write(fd, data, strlen(data));
read(fd, buffer, sizeof(buffer) - 1);
close(fd);
return 0;
}
驱动:
misc_test.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
int my_open (struct inode *inode, struct file *fp)
{
printk("Open ok\n");
return 0;
}
int my_release (struct inode *inode, struct file *fp)
{
printk("Release ok\n");
return 0;
}
ssize_t my_read (struct file *fp, char __user *buf, size_t size, loff_t *off)
{
printk("Read ok\n");
return 0;
}
ssize_t my_write (struct file *fp, const char __user *buf, size_t size, loff_t *off)
{
printk("Write ok\n");
return 0;
}
struct file_operations my_filop = {
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write
};
struct miscdevice my_misc={
.minor=255,
.name="my_open",
.fops = &my_filop
};
static int __init my_open_init(void)
{
int a=0;
a = misc_register(&my_misc);
if(a<0)
{
printk("my_misc_register error\n");
return -ENODEV;
}
printk("杂项字符设备注册成功\n");
return 0;
}
static void __exit my_open_exit(void)
{
misc_deregister(&my_misc);
printk("杂项字符设备注销成功\n");
}
module_init(my_open_init);
module_exit(my_open_exit);
MODULE_LICENSE("GPL");
结果: