Linux 文件系统与设备文件系统(2)
成于坚持,败于止步
Linux 文件系统目录结构
进入 Linux 根目录(即“/”,Linux 文件系统的入口,也是处于最高一级的目录),运行“ls –l”命令,可以看到 Linux 系统包含以下目录。
1./bin
包含基本命令,如 ls、cp、mkdir 等,这个目录中的文件都是可执行的。
2./boot
Linux 系统的内核及引导系统程序所需要的文件,如 vmlinuz、initrd.img 文件都位于这个目录中。
3./dev
设备文件存储目录,应用程序通过对这些文件的读写和控制就可以访问实际的设备。
4./etc
系统配置文件的所在地,一些服务器的配置文件也在这里,如用户账号及密码配置文件。
5./home
普通用户的家目录。
6./lib
库文件存放目录。
7./lost+found
在 Ext2 或 Ext3 文件系统中,当系统意外崩溃或机器意外关机时会产生一些文件碎片放在这里。
8./mnt
/mnt 这个目录一般是用于存放挂载储存设备的挂载目录的,比如有 cdrom 等目录,可以参看/etc/fstab 的定义。有时我们可以把让系统开机自动挂载文件系统,把挂载点放在这里也是可以的。
9./opt
opt 是“可选”的意思,有些软件包会被安装在这里,比如在 Fedora Core 5.0 中的 OpenOffice 就是安装在这里,用户自己编译的软件包也可以安装在这个目录中。
10./proc
操作系统运行时,进程及内核信息(比如 CPU、硬盘分区、内存信息等)存放在这里。/proc 目录为伪文件系统 proc 的挂载目录,proc 并不是真正的文件系统,它存在于内存之中。
11./root
Linux 超级权限用户 root 的家目录。
12./sbin
存放可执行文件,大多是涉及系统管理的命令,是超级权限用户 root 的可执行命令存放地 ,普通用户无权限执行这个目录下 的命令 ,这个目录和/usr/sbin;/usr/X11R6/sbin 或/usr/local/sbin 目录是相似的。
13./tmp
有时用户运行程序的时候会产生临时文件,/tmp 用来存放临时文件。
14./usr
这个是系统存放程序的目录,比如命令、帮助文件等,它包含很多文件和目录,Linux 发行版提供的软件包大多被安装在这里。
15./var
var 表示的是变化的意思,这个目录的内容经常变动,如/var 的/var/log 目录被用来存放系统日志。
16./sys
Linux 2.6 内核所支持的 sysfs 文件系统被映射在此目录。Linux 设备驱动模型中的总线、驱动和设备都可以在 sysfs 文件系统中找到对应的节点。当内核检测到在系统中出现了新设备后,内核会在 sysfs 文件系统中为该新设备生成一项新的记录。
17./initrd
若在启动过程中使用了 initrd 映像作为临时根文件系统,则在执行完其上的/linuxrc 挂接真正的根文件系统后,原来的初始 RAM 文件系统被映射到/initrd 目录。
Linux 文件系统与设备驱动
图 5.1 所示为 Linux 系统中虚拟文件系统、磁盘文件(存放于 RamDisk、Flash、ROM、SD 卡、U 盘等文件系统中的文件也属于磁盘文件)及一般的设备文件与设备驱动程序之间的关系。
应用程序和 VFS 之间的接口是系统调用,而 VFS 与磁盘文件系统以及普通设备之间的接口是 file_operations 结构体成员函数,这个结构体包含对文件进行打开、关闭、读写、控制的一系列成员函数。
由于字符设备的上层没有磁盘文件系统,所以字符设备的 file_operations 成员函数就直接由设备驱动提供了,file_operations 正是字符设备驱动的核心。
而对于块存储设备而言,ext2、fat、jffs2 等文件系统中会实现针对 VFS 的file_operations 成员函数,设备驱动层将看不到 file_operations 的存在。磁盘文件系统和设备驱动会将对磁盘上文件的访问最终转换成对磁盘上柱面和扇区的访问。
在设备驱动程序的设计中,一般而言,会关心结构体 file 和 inode 这两个结构体。
1.file 结构体
文件结构体代表一个打开的文件(设备对应于设备文件),系统中每个打开的文件在内核空间都有一个关联的 struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。在内核和驱动源代码中,struct file 的指针通常被命名为 file 或 filp(即 file pointer)。代码清单给出了文件结构体的定义。
-
struct file {
-
/*
-
* fu_list becomes invalid after file_free is called and queued via
-
* fu_rcuhead for RCU freeing
-
*/
-
union {
-
struct list_head fu_list;
-
struct rcu_head fu_rcuhead;
-
} f_u;
-
struct path f_path;
-
#define f_dentry f_path.dentry
-
#define f_vfsmnt f_path.mnt
-
const struct file_operations *f_op;
-
spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */
-
#ifdef CONFIG_SMP
-
int f_sb_list_cpu;
-
#endif
-
atomic_long_t f_count;
-
unsigned int f_flags;
-
fmode_t f_mode;
-
loff_t f_pos;
-
struct fown_struct f_owner;
-
const struct cred *f_cred;
-
struct file_ra_state f_ra;
-
-
u64 f_version;
-
#ifdef CONFIG_SECURITY
-
void *f_security;
-
#endif
-
/* needed for tty driver, and maybe others */
-
void *private_data;
-
-
#ifdef CONFIG_EPOLL
-
/* Used by fs/eventpoll.c to link all the hooks to this file */
-
struct list_head f_ep_links;
-
struct list_head f_tfile_llink;
-
#endif /* #ifdef CONFIG_EPOLL */
-
struct address_space *f_mapping;
-
#ifdef CONFIG_DEBUG_WRITECOUNT
-
unsigned long f_mnt_write_state;
-
#endif
-
};
文件读/写模式 mode、标志 f_flags 都是设备驱动关心的内容,而私有数据指针private_data 在设备驱动中被广泛应用,大多被指向设备驱动自定义用于描述设备的结构体。
驱动程序中经常会使用如下类似的代码来检测用户打开文件的读写方式。
-
if (file->f_mode & FMODE_WRITE) //用户要求可写
-
{
-
}
-
if (file->f_mode & FMODE_READ) //用户要求可读
-
{
-
}
下面的代码可用于判断以阻塞还是非阻塞方式打开设备文件。
-
if (file->f_flags & O_NONBLOCK) //非阻塞
-
pr_debug("open: non-blocking\n");
-
else //阻塞
-
pr_debug("open: blocking\n");
2.inode 结构体
VFS inode 包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。它是 Linux 管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁,inode 结构体的定义如代码所示。
-
struct inode {
-
/* RCU path lookup touches following: */
-
umode_t i_mode;
-
uid_t i_uid;
-
gid_t i_gid;
-
const struct inode_operations *i_op;
-
struct super_block *i_sb;
-
-
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
-
unsigned int i_flags;
-
unsigned long i_state;
-
#ifdef CONFIG_SECURITY
-
void *i_security;
-
#endif
-
struct mutex i_mutex;
-
-
-
unsigned long dirtied_when; /* jiffies of first dirtying */
-
-
struct hlist_node i_hash;
-
struct list_head i_wb_list; /* backing dev IO list */
-
struct list_head i_lru; /* inode LRU list */
-
struct list_head i_sb_list;
-
union {
-
struct list_head i_dentry;
-
struct rcu_head i_rcu;
-
};
-
unsigned long i_ino;
-
atomic_t i_count;
-
unsigned int i_nlink;
-
dev_t i_rdev;
-
unsigned int i_blkbits;
-
u64 i_version;
-
loff_t i_size;
-
#ifdef __NEED_I_SIZE_ORDERED
-
seqcount_t i_size_seqcount;
-
#endif
-
struct timespec i_atime;
-
struct timespec i_mtime;
-
struct timespec i_ctime;
-
blkcnt_t i_blocks;
-
unsigned short i_bytes;
-
struct rw_semaphore i_alloc_sem;
-
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
-
struct file_lock *i_flock;
-
struct address_space *i_mapping;
-
struct address_space i_data;
-
#ifdef CONFIG_QUOTA
-
struct dquot *i_dquot[MAXQUOTAS];
-
#endif
-
struct list_head i_devices;
-
union {
-
struct pipe_inode_info *i_pipe;
-
struct block_device *i_bdev;
-
struct cdev *i_cdev;
-
};
-
-
__u32 i_generation;
-
-
#ifdef CONFIG_FSNOTIFY
-
__u32 i_fsnotify_mask; /* all events this inode cares about */
-
struct hlist_head i_fsnotify_marks;
-
#endif
-
-
#ifdef CONFIG_IMA
-
atomic_t i_readcount; /* struct files open RO */
-
#endif
-
atomic_t i_writecount;
-
#ifdef CONFIG_FS_POSIX_ACL
-
struct posix_acl *i_acl;
-
struct posix_acl *i_default_acl;
-
#endif
-
void *i_private; /* fs or device private pointer */
-
};
对于表示设备文件的 inode 结构,i_rdev 字段包含设备编号。Linux 2.6 设备编号分为主设备编号和次设备编号,前者为 dev_t 的高 12 位,后者为 dev_t 的低 20 位。
下列操作用于从一个 inode 中获得主设备号和次设备号:
unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode);
查看/proc/devices 文件可以获知系统中注册的设备,第 1 列为主设备号,第 2 列为设备名,如下所示:
-
Character devices:
-
1 mem
-
2 pty
-
3 ttyp
-
4 /dev/vc/0
-
4 tty
-
5 /dev/tty
-
5 /dev/console
-
5 /dev/ptmx
-
7 vcs
-
10 misc
-
13 input
-
21 sg
-
29 fb
-
128 ptm
-
136 pts
-
171 ieee1394
-
180 usb
-
189 usb_device
-
-
Block devices:
-
1 ramdisk
-
2 fd
-
......
杳看/dev 目录可以获知系统中包含的设备文件,日期的前两列给出了对应设备的主设备号和次设备号,如下所示:
-
crw-rw---- 1 root uucp 4, 64 Jan 30 2003 /dev/ttyS0
-
brw-rw---- 1 root disk 8, 0 Jan 30 2003 /dev/sda
主设备号是与驱动对应的概念,同一类设备一般使用相同的主设备号,不同类的设备一般使用不同的主设备号(但是也不排除在同一主设备号下包含有一定差异的设备)。因为同一驱动可支持多个同类设备,因此用次设备号来描述使用该驱动的设备的序号,序号一般从 0 开始。
内核 Documents 目录下的 devices.txt 文件描述了 Linux 设备号的分配情况,它由LANANA ( The Linux Assigned Names And Numbers Authority , 网 址 :)组织维护,Torben Mathiasen 是其中的主要维护者。需要注意的是,LANANA 给出的设备号标准并不是硬性规定,在具体的设备驱动程序中,尽管一般会遵循 LANANA,但是也可以有例外。
udev 设备文件系统
udev 与 devfs 的区别
尽管 devfs 有这样和那样的优点,但是,在 Linux 2.6 内核中,devfs 被认为是过时的方法,并最终被抛弃,udev 取代了它。
udev 完全在用户态工作,利用设备加入或移除时内核所发送的热插拔事件(hotplug event)来工作。在热插拔时,设备的详细信息会由内核输出到位于/sys 的 sysfs 文件系统。
udev 的设备命名策略、权限控制和事件处理都是在用户态下完成的,它利用 sysfs 中的信息来进行创建设备文件节点等工作。
由于 udev 根据系统中硬件设备的状态动态更新设备文件,进行设备文件的创建和删除等,因此,在使用 udev 后,/dev 目录下就会只包含系统中真正存在的设备了。 devfs 与 udev 的另一个显著区别在于:采用 devfs,当一个并不存在的/dev 节点被打开的时候,devfs 能自动加载对应的驱动,而 udev 则不能。这是因为 udev 的设计者认为 Linux 应该在设备被发现的时候加载驱动模块,而不是当它被访问的时候。udev的设计者认为 devfs 所提供的打开/dev 节点时自动加载驱动的功能对于一个配置正确
的计算机是多余的。系统中所有的设备都应该产生热插拔事件并加载恰当的驱动,而udev 能注意到这点并且为它创建对应的设备节点。
阅读(1579) | 评论(0) | 转发(1) |