第三课 Linux文件系统与设备文件

1、Linux文件系统目录结构

  • bin:存放基本命令,如lscp等。
  • sbin:存放系统命令,如modprobeifconfig等,大多涉及系统管理的命令。
  • dev:设备文件存储目录,应用程序通过读写、控制这些文件来访问实际的设备。
  • etc:系统配置文件,如用户账号、密码、启动脚本。
  • lib:系统库文件。
  • mnt:一般用于存放挂载储存设备的挂载目录。
  • opt:可选目录,一般用于安装软件包。
  • proc:进程及内核信息。是伪文件系统proc的挂载目录,只存在于内存中。
  • tmp:存放用户程序运行时产生的临时文件。
  • usr:系统存放程序的目录,如用户命令、用户库等。
  • var:该目录内容经常变动,可用于存放系统日志等。
  • sys:Linux2.6以后的内核所支持的sysfs文件系统会被映射到此目录。设备驱动模型中的总线、驱动、设备都可在该目录中找到相应的节点。

2、Linux文件系统与设备驱动

文件系统与设备驱动之间的关系如下图所示:
文件系统与设备驱动之间的关系
应用程序与虚拟文件系统VFS之间的接口是系统调用;

虚拟文件系统VFS与文件系统和设备文件之间的接口是struct file_operations结构体,其包含了对文件进行打开open、关系close、读写read/write、控制ioctl等成员函数。

字符设备的访问方法:
字符设备的file_operations成员函数直接由设备驱动提供,虚拟文件系统调用这些成员函数访问字符设备。

块设备的访问方法:

方法一:不通过文件系统,而是直接访问裸设备。

Linux内核实现了统一的、用于块设备的struct file_operations结构体实例def_blk_fops
当运行类似dd if=/dev/sdb1 of=sdb1.img命令将整个/dev/sdb1裸分区复制到sdb1.img时,内核就是使用def_blk_fops实例。
裸分区、裸设备:个人理解,就是没有在这个存储空间上套一层文件系统,操作这块存储空间时,数据是无结构的、无组织的。

方法二:通过文件系统访问块设备。

通过文件系统访问块设备,struct file_operations结构体的实现由文件系统(ext2fat等)完成,设备驱动层感知不到struct file_operations的存在,也不需要实现这个结构体。
文件系统会把针对文件的读写转换为针对块设备原始扇区的读写。

设备驱动程序设计中,需关注两个结构体struct filestruct inode
file结构体代表一个打开的文件,每次打开一个文件时,内核都会创建一个file实例与该文件关联,并将该实例传递给在文件上进行操作的任何函数。

struct file结构体中的主要内容:

struct file{
	...
	struct inode *f_inode;
	const struct file_operations *f_op; // 和文件关联的操作
	unsigned int f_flags; // 文件标志,如O_RDONLY、O_NONBLOCK、O_SYNC等
	fmode_t f_mode; // 文件读写模式
	void *private_data; // 文件私有数据,大多指向设备驱动自定义的用于描述设备的结构体
	...
};

inode结构体表示设备文件,VFS的inode结构体包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。是管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁。

struct inode{
	...
	dev_t i_rdev;
	union{
		struct block_device *i_bdev;
		struct cdev *i_cdev;
	}
	...
};

成员i_rdev包含设备编号,分为主设备编号和次设备编号。
主设备编号占dev_t的高12位,次设备编号占dev_t的低20位。可通过如下两个函数从inode中获得主设备号和次设备号。

unsigned int imajor(struct inode *inode);//返回主设备号
unsigned int iminor(struct inode *inode);//返回次设备号

查看/proc/devices可获知系统中注册的设备信息,如设备类型、名称、主设备号。
查看/dev目录可获取系统中包含的所有设备文件,其中有设备的主设备号和次设备号信息。

主设备号:用于区分不同类型的设备。同类型的设备使用相同的主设备号,一个主设备号通常对应一个驱动。
次设备号:用于区分同一类型设备下的多个设备
例如:有多个LED,它们都使用同一个驱动程序,那主设备号相同,次设备号区分LED0、LED1。

设备文件的管理方式:
动态建立/删除设备节点文件,不需要用户再手动使用mknod命令在/dev目录下创建设备节点文件。

  • devfs设备文件系统:Linux2.6及以后的内核中已抛弃该管理方式。
  • udev+sysfs文件系统:

devfs设备文件系统(了解即可):
优点:

  • 可以通过程序在设备初始化时在/dev目录下创建设备文件,卸载设备时将它删除。
  • 设备驱动程序可以指定设备名、所有者和权限位,用户空间程序仍可以修改所有者和权限位。
  • 不再需要为设备驱动程序分配主设备号以及处理次设备号,可通过向register_chrdev()函数传递0主设备号,由内核分配可用的主设备号,并在devfs_register()函数中指定次设备号。

缺点:

  • 存在无法修复的bug
  • 运行在内核空间,但其工作内容完成可在用户空间实现

遗留的两个API:

/* 申请主设备号 */
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)

/* 释放主设备号 */
void unregister_chrdev(unsigned int major, const char *name)

udev设备管理方式:
udev完全在用户态下工作,利用设备加入或移除时内核所发送的热插拔事件(Hotplug Event)进行工作。

工作过程:
当热插拔时,内核通过套接字netlink将设备的详细信息(uevent)发送出来。udev进程接收netlink发送的uevent信息,根据该信息内容以及用户设置的udev规则完成匹配工作(包括SUBSYSTEMACTIONattribute等),并在/dev目录下创建设备文件节点。
对于冷插拔设备,由于其在开机时已经存在,且是在udev启动前就已经被插入。为了获得该设备的uevent信息,内核提供了sysfs下的uevent节点(/sys/module/xxx/uevent)。向该节点写一个add,内核会重新发送该设备相关的netlink消息。

udev的特点:

  • udev在设备被发现时(即产生热插拔事件)加载驱动模块,并创建对应的设备节点,而不是在访问设备时加载驱动模块。
  • 工作在用户空间
  • 动态建立/删除设备文件
  • 允许每个不用关心主/次设备号,而提供LSB名称(Linux标准规范)
  • 可固定/dev下的设备节点文件名称

udev规则文件:

  • 以行为单位,每一行代表一个规则
  • #开头的行表示注释行
  • 每条规则可分成一个或多个匹配部分和赋值部分
  • 匹配部分用匹配专用的关键字表示:ACTION行为、KERNEL内核设备名、BUS总线类型、SUBSYSTEM子系统名、ATTR属性等。
  • 赋值部分的关键字:NAME创建的设备文件名、SYMLINK符号创建链接名、OWNER设置设备所有者、GROUP设置设备的组、IMPORT调用外部程序、MODE节点访问权限等。
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:35:be:ff", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME=="eth1"

udevdevfs在命名方面的差异:
如果系统中有两个USB打印机,设备节点文件分别为/dev/usb/lp0/dev/usb/lp1,但哪个文件对应哪台打印机是无法确定的。而且,打印机与设备节点文件的映射关系也会因设备发现的顺序、打印机本身关闭等原因而不确定。
因此,最好是基于打印机的序列号或其他标识信息来确定打印机与设备节点文件的映射关系,devfs无法实现这一点,而udev可以做到。
udevadmudev配套的工具 。
嵌入式系统中,可使用mdev(轻量版udev),mdev集成在busybox中。

sysfs文件系统:
该文件系统是虚拟的文件系统,可以产生一个包括所有系统硬件的层级视图,可以展示设备驱动模型中各组件的层次关系。
sysfs文件系统把连接在系统上的设备和总线组织成一个分级的文件,这些文件可由用户空间存取,向用户空间导出内核数据结构以及它们的属性。
sysfs文件系统顶层目录结构:

/sys
	|---block //包含所有块设备
	|---bus   //包含系统中所有总线类型
		|---devices //挂载在该总线上的设备,是指向/sys/devices目录中文件的符号链接
		|---drivers //挂载在该总线上的驱动,是指向/sys/drivers目录中文件的符号链接
	|---class //包含系统中的设备类型,也包含许多对/sys/devices下文件的符号链接
	|---dev
	|---devices //包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构
	|---firmware
	|---fs
	|---hypervisor
	|---kernel
	|---module
	|---power

Linux设备模型与设备、驱动、总线和类的现实状况是直接对应的,如图所示。
Linux设备模型
Linux 2.6以后的内核的设备驱动核心层代码已处理好设备、总线和类的关系,内核中的总线和其他内核子系统会完成与设备模型的交互。因此,驱动工程师编写底层驱动代码时几乎不需要关心设备模型,只需要按照每个框架的要求,填写xxx_driver里面各种回调函数(xxx表示总线名)。

结构体bus_typedevice_driverdevice分别描述总线、驱动和设备:

struct bus_type{
	...
	int (*match)(struct device *dev, struct device_driver *drv); /* 匹配驱动和设备 */
	...
};

struct device_driver{
	...
	const char *name;
	struct bus_type *bus; /* 驱动挂载的总线 */
	int (*probe)(struct device *dev);
	int (*remove)(struct device *dev);
	...
};

struct device{
	...
	struct bus_type *bus; /* 设备挂载的总线 */
	struct device_driver *driver; /* 设备使用的驱动 */
	...
};

Linux内核中,设备和驱动分别独立注册,注册设备或驱动时,不要求相应的驱动或设备已经存在。
当向内核注册设备或驱动时,总线bus_typematch()成员函数将为该设备或驱动匹配相应的驱动或设备。
当驱动和设备匹配成功后,结构体xxx_driverprobe()函数就被执行(xxx表示总线名,如platformi2c)。

总线、驱动和设备分别对应sysfs下的一个目录,这三者都可认为是kobject的派生类,kobject可看作所有总线、驱动和设备的抽象基类,一个kobject对应sysfs中的一个目录。

总线、驱动和设备中各个attribute对应sysfs中一个文件。
sysfs中的目录来源于bus_typedevice_driverdevice,而目录中的文件则来源于attribute

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值