一、Linux设备分类
(一)linux的文件种类:
- -:普通文件:文件IO
- d:目录文件
- p:管道文件
- s:本地socket文件:网络编程
- l:链接文件
- c:字符设备
- b:块设备
1、Linux设备一切皆文件,大部分设备是文件。
2、普通文件在硬盘分区,存放文件内容,文件名,文件元信息inode(修改权限,修改时间,创建者...)
3、socket(服务端客户端的地址值)、管道文件(通信机制)没有文件内容。
4、链接文件:软连接另外文件的路径,硬链接是别名。
5、字符设备和块设备没有文件内容。
(二)按驱动程序分类
Linux内核按驱动程序实现模型框架的不同,将设备分为三类:
- 字符设备:按字节流形式进行数据读写的设备,一般情况下按顺序访问,数据量不大,一般不设缓存
- 块设备:按整块进行数据读写的设备,最小的块大小为512字节(一个扇区),块的大小必须是扇区的整数倍,Linux系统的块大小一般为4096字节,随机访问,设缓存以提高效率
- 网络设备:针对网络数据收发的设备
1、块设备一般不面向应用设备,一般面向文件系统,设备缓存,提高效率,应用程序通过文件系统访问块设备。
2、网络设备:不用文件名命名,另外的命名方式,网卡(有线,无线),蓝牙。
二、设备号
内核用设备号来区分同类里不同的设备,设备号是一个无符号32位整数,数据类型为dev_t,设备号分为两部分:
- 主设备号:占高12位,用来表示驱动程序相同的一类设备
- 次设备号:占低20位,用来表示被操作的哪个具体设备
应用程序打开一个设备文件时,通过设备号来查找定位内核中管理的设备。
1、设备号MKDEV宏
MKDEV宏用来将主设备号和次设备号组合成32位完整的设备号,用法:
dev_t devno;
int major = 251;//主设备号
int minor = 2;//次设备号
devno = MKDEV(major,minor);
主设备号相同,用一套驱动设备,用次设备号区分。
例:驱动多个鼠标,鼠标主设备号相同,次设备号不同。
2、取设备号
MAJOR宏用来从32位设备号中分离出主设备号,用法:
dev_t devno = MKDEV(249,1);
int major = MAJOR(devno);
MINOR宏用来从32位设备号中分离出次设备号,用法:
dev_t devno = MKDEV(249,1);
int minor = MINOR(devno);
3、创建一个设备文件
如果已知一个设备的主次设备号,应用层指定好设备文件名,那么可以用mknod命令在/dev目录创建代表这个设备的文件,即此后应用程序对此文件的操作就是对其代表的设备操作,mknod用法如下:
@ cd /dev
@ mknod 设备文件名 设备种类(c为字符设备,b为块设备) 主设备号 次设备号 //ubuntu下需加sudo执行
在应用程序中如果要创建设备可以调用系统调用函数mknod,其原型如下:
int mknod(const char *pathname,mode_t mode,dev_t dev);
pathname:带路径的设备文件名,无路径默认为当前目录,一般都创建在/dev下
mode:文件权限 位或 S_IFCHR/S_IFBLK
dev:32位设备号
返回值:成功为0,失败-1
三、申请和注销设备号
(一)申请设备号
字符驱动开发的第一步是通过模块的入口函数向内核添加本设备驱动的代码框架,主要完成:
- 申请设备号
- 定义、初始化、向内核添加代表本设备的结构体元素
1、手动申请
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:手动分配设备号,先验证设备号是否被占用,如果没有则申请占用该设备号
参数:
from:自己指定的设备号
count:申请的设备数量
name:/proc/devices文件中与该设备对应的名字,方便用户层查询主设备号
返回值:
成功为0,失败负数,绝对值为错误码
2、动态申请
int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count, const char *name)
功能:动态分配设备号,查询内核里未被占用的设备号,如果找到则占用该设备号
参数:
dev:分配设备号成功后用来存放分配到的设备号
baseminior:起始的次设备号,一般为0
count:申请的设备数量
name:/proc/devices文件中与该设备对应的名字,方便用户层查询主次设备号
返回值:
成功为0,失败负数,绝对值为错误码
分配成功后在/proc/devices 可以查看到申请到主设备号和对应的设备名,mknod时参数可以参考查到的此设备信息
void unregister_chrdev_region(dev_t from, unsigned count)
功能:释放设备号
参数:
from:已成功分配的设备号将被释放
count:申请成功的设备数量
释放后/proc/devices文件对应的记录消失
代码:
/fs4412/mydrivercode/mychar
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
int major = 11;
int minor = 0;
int mychar_num = 1;
int __init mychar_init(void){
int ret = 0;
dev_t devno = MKDEV(major,minor);
ret = register_chrdev_region(devno,mychar_num,"mychar");
if(ret){
ret = alloc_chrdev_region(&devno,minor,mychar_num,"mychar");
if(ret){
printk("get devno failed\n");
return -1;
}
major = MAJOR(devno);//容易遗忘,动态分配后主设备号有可能改变,次设备号为0
}
return 0;
}
void __exit mychar_exit(void)
{
dev_t devno = MKDEV(major,minor);
unregister_chrdev_region(devno,mychar_num);
}
MODULE_LICENSE("GPL");
module_init(mychar_init);
module_exit(mychar_exit);
将编译好的字符设备插入内核模块
字符设备
块设备: