日期:2014-05-16  浏览次数:20903 次

Linux 字符设备文件 简介

P { MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px }

设备号

创建设备文件

设备注册

重要数据结构

设备操作

?

设备号

用ls -l查看,设备文件项中有两个数(中间有逗号),是为主次设备编号

?

字符设备文件调用字符设备驱动访问硬件

?

主设备号用来标识驱动程序,次设备号被驱动程序用来辨别操作的是那个设备

?

主设备号反映设备类型,次设备号用来区分同类型的设备

?

内核中使用dev_t这个类型描述设备号,为unsigned int 32位,其中高12位为主设备号,低20位为次设备号

?

MAJOR(dev_t dev)提取主设备号

MINOR(dev_t dev)提取次设备号

?

内核采用静态申请、动态申请的方法给设备分配主设备号

?

静态申请

根据documenta/devices.txt,找出没有使用的主设备号,使用regist_chrdev_region函数注册设备号

可能导致冲突

?

int register_chrdev_region(

dev_t from,?????? 希望申请的设备号

unsigned count,? 设备数量

const char *name 设备名称,体现在/proc/devices

)

?

动态申请

alloc_chrdev_region分配设备号

优点:易于推广

缺点:是无法在安装驱动前创建设备文件(还没分配主设备号)

解决办法:

安装驱动后,从/proc/devices中查询设备号

?

int alloc_chrdev_region(

dev_t *dev, 无需传值,写入

unsigned baseminor, 起始次设备号

unsigned count, 设备数目

const char *name 设备名称

)

?

设备注销

不再使用设备时,应注销掉设备号

?

void unregistor_chrdev_region( dev_t from, unsigned count)

?

创建设备文件

?

1、使用mknod命令手工创建

?

mknod filename type major minor

参数:文件名、类型、主设备号、次设备号

如:mknod serial0 c 100 0

?

2、自动创建

在驱动程序中,通过函数调用

?

设备文件的作用

设备文件本身无任何内容,只是在应用程序与设备驱动之间建立桥梁,帮助应用程序

?

文件数据结构

?

Struct file 代表一个打开的文件。每个打开的文件在内核空间都有一个关联的struct file,记录文件读写位置。一个文件打开10次,则有10个Struct file。内核打开文件时创建,关闭后释放

主要成员:

loff_t f_pos 文件读写位置

struct file_operations *f_op

?

Struct inode 记录文件物理上的信息

Struct file_operations 一个函数指针的集合,定义能在设备上进行的操作 。结构中的成员指向驱动中的函数

?

strunct file_operations mem_fops = {

.owner = THIS_MODULE,

.llseek = mem_seek,

.read = mem_read,

.write = mem_write,

.ioctl = mem_ioctl,

.open = mem_open,

.release = mem_release,

};

?

字符设备的注册

在linux 2.6内核中,字符设备使用struct cdev这个结构来描述:

1、分配 cdev

struct cdev *cdev_alloc(void)

2、初始化 cdev

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

3、添加 cdev

init cdev_add(struct cdev *p, dev_t dev, unsigned count)

参数:设备、设备号、数目

?

实现file_operations里面注册的函数

int (*open)(struct inode *, struct file *)

打开这个设备,并不要求驱动程序一定要实现, open = NULL表示设备永远打开,不需要手动打开

?

void (*release)(struct inode *, struct file *)

关闭设备

?

read 读

write 写

poll 对应select系统调用

ioctl 控制设备

mmap 将设备映射到进程虚拟地址空间

llseek 修改文件的当前读写位置,并将新位置作为返回值

?

?

open函数

int (*open)(struct inode *, struct file *)

初始化设备、标明次设备号

?

读和写

ssize_t xxx_read(struct file * filp, char __user * buff, size_t count, loff_t * offp);

ssize_t xxx_write(struct file * filp, char __user * buff, size_t count, loff_t * offp);

filp=文件指针,(正在操纵的文件)

count=请求传输的数据量,对应系统调用里传入的count

buff = 参数指向数据缓存 ,对应系统调用里传入的fuff, 是用户空间指针,不能被内核代码直接引用(用户空间指针在内核空间可能是无效的,没有那个地址的映射)

offp = 指出文件的当前访问位置(字节索引) 内核会把Struct file里面的loff_t f_pos 植入这个参数

?

内核提供了专门的函数访问用户空间指针

int copy_from_user(void *to, const void_user *from, int n)? == write 数据从用户流向驱动

int copy_to_user(void __user *to, const void *from, int n) ==read 数据从驱动流向用户

?

设备注销

int cdev_del(struct cdev *p)

?

字符设备驱动程序 memdev.c

?

module_init() 内核模块