共计 3846 个字符,预计需要花费 10 分钟才能阅读完成。
今天丸趣 TV 小编给大家分享一下 linux 字符设备放在哪个目录中的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
linux 字符设备放在“/dev”目录中。字符设备是指只能一个字节一个字节进行读写操作的设备,一般每个字符设备或者块设备都会在“/dev”目录下对应一个设备文件,并且每个设备文件都必须有主 / 次设备号,主设备号相同的设备是同类设备,使用同一个驱动程序。
1、Linux 设备驱动分类
Linux 系统将设备分为三个类:字符设备、块设备、网络设备,在这三大类中,字符设备相对比较简单,应用程序通过字符设备文件来访问字符设备,本讲主要介绍字符设备,如果对块设备和网络设备感兴趣的话,可以参看相关资料,并对其进行深入了解。
2、什么是字符设备?
字符设备是指只能一个字节一个字节进行读写操作的设备,不能随机读取设备中的某一数据、读取数据要按照先后顺序。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和 LED 等。
一般每个字符设备或者块设备都会在 /dev 目录下对应一个设备文件,并且每个设备文件都必须有主 / 次设备号,主设备号相同的设备是同类设备,使用同一个驱动程序。
Linux 用户层程序通过设备文件来使用驱动程序操作字符设备或块设备。
可以通过
cat /proc/devices
命令查看当前已经加载的设备驱动程序的主设备号。
通过在 /dev 目录下执行命令
ls -l
可以看到所有设备文件的主设备号和次设备号:
对常见设备文件作如下说明:
/dev/hd[a-t]:IDE 设备
/dev/sd[a-z]:SCSI 设备
/dev/fd[0-7]:标准软驱
/dev/md[0-31]:软 raid 设备
/dev/loop[0-7]:本地回环设备
/dev/mem:内存
/dev/null:无限数据接收设备, 相当于黑洞
/dev/zero:无限零资源
/dev/tty[0-63]:虚拟终端
/dev/ttyS[0-3]:串口
/dev/lp[0-3]:并口
/dev/console:控制台
/dev/fb[0-31]:framebuffer
/dev/cdrom = /dev/hdc
/dev/modem = /dev/ttyS[0-9]
/dev/pilot = /dev/ttyS[0-9]
3、如何建立设备文件?
建立设备文件有两种方式,一是通过系统调用 mknod(),编程中调用该函数可以建立一个新的设备文件名,另外一种就是通过 mknod 命令,命令的第一个参数为设备文件名,第二个参数为设备类型,比如 c 表示字符设备,第三、四个参数为设备文件的主设备号和次设备号,比如 231 和 0。主设备号和次设备号合起来唯一的确定一个设备,同一个设备不同类型的主设备号是一样的,次设备号不同,比如一个硬盘的多个分区就有不同的次设备号,通过主设备号就可以把设备文件与驱动程序关联起来。
mknod filename type major minor
filename:要创建的设备文件名;
type:设备类型,c 代表一个字符设备,b 代表一个块设备;
major:主设备号;
minor:次设备号;
4、如何描述字符设备?
Linux 内核中抽象出 struct cdev 结构体来表示一个字符设备,cdev 定义于 linux/cdev.h 中其中,其中最关键的是 file_operations 结构,它是实现字符设备的操作集。
struct cdev {
struct kobject kobj; // 内嵌内核对象
struct module *owner; // 该字符设备所在的内核模块
const struct file_operations *ops; // 文件操作结构体
struct list_head list; // 已注册字符设备链表
dev_t dev; // 由主、次设备号构成的设备号
unsigned int count;// 同一主设备号的次设备号的个数
};
Linux 使用 file_operations 结构访问驱动程序的函数,这个结构的每一个成员的名字都对应着一个系统调用。
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
};
用户进程利用在对设备文件进行诸如 read,write 操作的时候,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这是 Linux 的设备驱动程序工作的基本原理。
5、字符设备与文件系统的接口
如图,在 Linux 内核中,最左边,使用 cdev 结构体来描述字符设备; 通过其成员 dev_t 来定义设备号(分为主、次设备号)以确定字符设备的唯一性; 通过其成员 file_operations 来定义字符设备驱动提供给虚拟文件系统 VFS 的接口函数,如常见的 open()、read()、write() 等,这些函数真正的操作硬件设备。
在上一个图的基础上我们看这个图,字符设备驱动程序是以内核模块的形式加载到内核中的,首先模块加载函数按静态或者动态方式获取设备号;然后字符设备初始化函数建立 cdev 与 file_operations 之间的连接,通过注册函数向系统添加一个 cdev 以完成注册; 模块卸载时与加载对应,要注销 cdev,并释放设备号。
在用户程序中,可以通过系统调用 open(), read(), write() 等调用驱动程序在内核中所实现的这些函数。这样用户态到内核驱动之间的通路就打通了。
6、编写简单的字符设备驱动程序
如图,编写字符设备驱动分为三大步骤:
驱动的初始化,其中又分为四个步骤,调用相关的函数达到。
实现设备的操作,具体的操作取决于你自己所要实现的功能,这里只列出了基本的操作
驱动的注销,注销就是释放资源。
其中调用的接口函数功能如下:
第 1 个函数是分配函数,动态申请 cdev 的内存,给该结构分配内存空间。
第 2 个函数是初始化函数,初始化 cdev 的成员,并建立 cdev 和 file_operations 之间关联.
第 3 个函数注册 cdev 设备对象,也就是把字符设备添加到字符设备表中,就像大家入学时进行注册一样。
第 4 个函数是注销驱动程序调用,将 cdev 对象从系统中删除。
第 5 个函数释放 cdev 数据结构所占的内存。
6.1 设备号的申请和释放
一个字符设备或块设备都有一个主设备号和一个次设备号。主设备号用来标识与设备文件相连的驱动程序,用来反映设备类型。次设备号被驱动程序用来辨别操作的是哪个设备,用来区分同类型的设备。注册时申请设备号,注销时释放设备号,就像大家入学是有一个学号,毕业离开时就释放掉这个学号。
6.2 用户空间与内核空间数据的传送
当我们在用户程序中调用 read()函数时,陷入内核空间,实际上要通过内核的 copy_to_user() 函数把内核空间缓冲区中的数据拷贝到用户空间的缓冲区,反之,当我们调用 write() 函数时,内核通过调用 copy_from_user() 函数把用户空间的数据拷贝到内核缓冲区。
以上就是“linux 字符设备放在哪个目录中”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,丸趣 TV 小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注丸趣 TV 行业资讯频道。