一、Linux内核虚拟文件系统学习总结

Linux支持各种文件系统,Linux内核通过虚拟文件系统了对各种文件系统共性的进行抽象,并对外提供统一接口,从面向对象编程的角度来看,称为抽象文件系统更为合适。虚拟文件系统(VFS virtual file system)用来管理挂接(mount)各种具体文件系统(如:ext4文件系统)。具体的文件系统可设计成可加载模块,在系统需要时进行加载,例如VFAT就被实现成一个模块,当挂接VFAT文件系统时VFAT文件系统模块将被加载。

挂接具体文件系统时,VFS读取它的超级块,得到具体文件系统的拓扑结构并将这些信息映射到VFS超级块结构中。

当进程或shell命令(如:ls)访问目录和文件时,shell命令及应用程序分解成系统调用,系统调用进入内核空间,遍历虚拟文件系统的VFS节点(inode),而VFS节点指向了具体文件系统的节点,通过底层块I/O函数调用IDE接口,然后再通过块驱动程序访问块设备(如:硬盘),得到了文件数据。文件系统的运行流程示意图如下:

20169211《Linux内核原理与分析》 第九周作业-LMLPHP

虚拟文件系统对文件系统共有的内核上层及底层部分进行了处理,上层处理如:文件路径的查找、文件的读写操作从用户空间向下传递到具体文件系统的部分;底层进行各种缓存的处理,如:块缓存(buffer)。

1、VFS数据结构:

VFS的数据结构定义了VFS对象和对象的操作函数集,它们是一个接口标准,具体文件系统必须提供VFS接口标准对应的数据结构和操作函数。体文件系统在使用前,必须将自己的结构及操作函数映射到VFS中,这样被访问到。

2、超级块操作函数集:

超级块对象的各种操作方法由超级块操作函数集结构super_operations定义,通过超级块对象的成员s_op可找到该操作函数集,如:

sb->s_op->read_inode(inode)。

结构super_operations列出如下:

struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb); /*分配节点对象空间*/
void (*destroy_inode)(struct inode *); /*销毁节点*/ void (*dirty_inode) (struct inode *); /*处理脏节点,ext4用它更新文件系统日志*/
int (*write_inode) (struct inode *, int); /*将节点的内容写回到磁盘节点*/
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *); /*删除节点及对应磁盘上的数据*/
void (*put_super) (struct super_block *); /*释放超级块对象*/
void (*write_super) (struct super_block *); /*将超级块信息写回磁盘*/
/*日志系统用来同步更新文件系统数据结构在磁盘上的数据*/
int (*sync_fs)(struct super_block *sb, int wait);
void (*write_super_lockfs) (struct super_block *); /*锁住文件系统将超级块信息写回磁盘*/
void (*unlockfs) (struct super_block *); /*解锁文件系统*/
int (*statfs) (struct dentry *, struct kstatfs *); /*得到文件系统的一些统计信息*/
int (*remount_fs) (struct super_block *, int *, char *); /*重新挂接文件系统*/
void (*clear_inode) (struct inode *); /*清除节点*/
void (*umount_begin) (struct super_block *); /*开始卸载文件系统的操作*、 int (*show_options)(struct seq_file *, struct vfsmount *); /*显示文件系统特定的选项*/
int (*show_stats)(struct seq_file *, struct vfsmount *); /*显示状态信息*/
#ifdef CONFIG_QUOTA
/*读限额,限额系统调用用该方法读取数据*/
ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
/*写限额,限额系统调用用该方法将数据写入文件*/
ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
#endif
};

二、Linux内核块IO学习笔记

1、基本设备类型

基本的设备类型有字符设备和块设备两种:字符设备按照字符流的方式被有序访问;系统中能够随机而不需要按顺序访问固定大小数据片的设备称为块设备。这些数据片称为块。

2、扇区是硬件单元

块设备中最小的可寻址单元是扇区。扇区的大小是设备的物理属性,扇区是所有块设备的基本单元,块设备无法对比它还小的单元进行寻址和操作,许多块设备能够一次传输多个扇区。

3、扇区的大小

扇区大小一般是2的整数倍,最常见的大小是512字节。

4、块是软件单元

各种软件都会用到自己的最小逻辑可寻址单元——块。块是文件系统的一种抽象,只能基于块来访问文件系统。虽然物理磁盘寻址是按照扇区级进行的,但是内核执行的所有磁盘操作都是按照块进行的。

5、块的大小

由于扇区是设备的最小可寻址单元,所以块不能比扇区还小,只能是扇区的整数倍。另外,内核还要求块大小是2的整数倍,而且不能超过一个页的长度,所以,块的大小必须是扇区大小的2的整数倍,并且要小于页面大小。通常块大小是512字节、1K或4K。

6、别名

扇区——设备的最小寻址单元,有时会被称作“硬扇区”或“设备块”;块——文件系统的最小寻址单元,有时会被称作“文件块”或“I/O块”。

7、缓冲区

当一个块被调入内存时,也就是说,在读入后或等待写出时,它要存储在一个缓冲区中。每个缓冲区与一个块对应,它相当于是磁盘块在内存中的表示。块包含一个或多个扇区,但大小不能超过一个页面,所以一个页可以容纳一个或多个内存中的块。

三、linux内核装载和启动一个可执行程序实验总结

1、实验过程

打开Makefile,首先静态编译了hello.c,生成根文件系统时把init和hello都放入rootfs image里面,这样执行exec的时候就自动的帮我们加载hello这个文件

20169211《Linux内核原理与分析》 第九周作业-LMLPHP

执行结果,hello world! 是新加载的一个可执行程序输出的

20169211《Linux内核原理与分析》 第九周作业-LMLPHP

单步调试,窗口被冻结

20169211《Linux内核原理与分析》 第九周作业-LMLPHP

设置三个断点: sys_execve,load_elf_binary , start_thread

20169211《Linux内核原理与分析》 第九周作业-LMLPHP

list列出来跟踪, 输入s可以进入do_execve的内部。按c继续执行,跑到load_elf_binary。list查看代码,输入n一句一句跟踪,nnnc,追踪到start_thread。

20169211《Linux内核原理与分析》 第九周作业-LMLPHP

看看hello这个可执行程序的入口,发现也是0x8048d0a,和new_ip的位置一样。new_ip是返回到用户态第一条指令的地址。

20169211《Linux内核原理与分析》 第九周作业-LMLPHP

将new_ip和new_sp赋值,并设了一个新堆栈。

二、实验总结

新进程开始。调用系统调用execve(),其中/bin/ls作为一个参数被传递进去;sys_execve()服务例程修改当前进程的执行上下文;以上系统调用终止后,新进程开始执行放在可执行文件中的代码,也就是执行在当前目录下显示文件的功能。 当ELF被load_elf_binary()装载完成后,函数返回至do_execve()在返回至sys_execve()。

静态地拷贝到可执行文件中运行(静态库)缺点:占用大量的磁盘空间 利用动态链接器(ld.so)指向库名。

在程序运行时,被链接(或者说映射)到进程(共享库)

缺点:动态链接的程序启动时间长 直接复制库代码的某些部分。

05-11 17:49