20201220蔡笃俊《信息安全系统设计与实现》第七、八章学习笔记
一、任务内容
- 自学教材第7,8章,提交学习笔记(10分)
- 知识点归纳以及自己最有收获的内容 (3分)
- 问题与解决思路(2分)
- 实践内容与截图,代码链接(3分)
- ...(知识的结构化,知识的完整性等,提交markdown文档,使用openeuler系统等)(2分)
二、知识点归纳以及自己最有收获的内容
(一)知识点归纳
第七章:文件操作
7.1 文件操作级别
文件操作级别 文件操作分为五个级别,按照从低到高的顺序排列如下:
- 硬件级别:这个级别的文件操作包括:
- fdisk∶将硬盘、U盘或SDC盘分区。
- mkfs∶格式化磁盘分区,为系统做好准备。
- fsck∶检查和维修系统。
- 碎片整理∶压缩文件系统中的文件。
其中大多数是针对系统的实用程序。普通用户可能永远都不需要它们,但是它们是创建和维护系统不可缺少的工具。
- 操作系统内核中的文件系统函数
每个操作系统内核均可为基本文件操作提供支持。下面列出了类 Unix 系统内核中的一些函数,其中前缀k表示内核函数。
点我查看文件系统函数
kumount(),kumount()
(mount/umount file systems)
kmkdir(),krmdir()
(make/remove directory)
kchair(),kgetCwd()
(change directory,get CWD pathname)
klink(),kunlink()
(hard link/unlink files)
kchmod(),kchown(),kutime() (change r|w|x permissions,owner,time)
kcreat(),kopen()
(create/open file for R,W,RW,APPEND)
kread(),kwrite() (read/write opened files)
klseek(),kclose()
(Lseek/close file descriptors)
keymlink(),kreadlink ()
(create/read symbolic 1ink files)
kstat(),kfstat(),klatat() (get file status/information)
kopendir(),kreaddir()
(open/read directories)
-
系统调用∶用户模式程序使用系统调用来访问内核函数。
open()、read()、lseek()和 close()函数都是C语言库函数。每个库函数都会发出一个系统调用,使进程进入内核模式来执行相应的内核函数,例如open可进入kopen(),read可进入kread()函数,等等。当进程结束执行内核函数时,会返回到用户模式,并得到所需的结果。在用户模式和内核模式之间切换需要大量的操作(和时间)。因此,内核和用户空间之间的数据传输成本昂贵。对于读/写文件,最好的方法是匹配内核的功能。内核会按数据块大小(从1KB到8KB)来读取/写入文件。(在Linux 中,硬盘的默认数据块大小是4KB,软盘的是1KB) -
I/O库函数
用户通常需要读/写单独的字符、行或数据结构记录等。如果只有系统调用,用户模式程序则必须自己从缓冲区执行这些操作。C语言库提供了一系列标准的I/O函数,同时也提高了运行效率。I/O库函数包括:
FILE mode I/O: fopen(), fread(), fwrite(), fseek(), fclose(),fflush()
char mode I/o: gete(), getchar(), ugetc(), putc(), putchar()
line mode I/O: gets(), fgets(), putc(), puts(), fputs()
formatted I/O: scanf(), fscanf(), sscanf(), printf(), fprintf(), sprintf()
-
用户命令
用户可以使用Unix/Linux命令来执行文件操作,而不是编写程序。
更多文件命令操作可以参考https://www.runoob.com/linux/linux-command-manual.html
-
sh脚本
sh语言包含所有的有效Unix/Linux命令,它还支持变量和控制语句,如if、do、for、while、case等。除此之外,Perl和Tcl等其他许多脚本语言也使用广泛。我们在面对复杂的GUI操作时可以采用sh脚本来帮助我们解决这个问题。
7.2 文件I/O操作
分为用户模式和内核模式操作:
7.3 低级别文件操作
这部分理论部分比较抽象,直接上实践图
虚拟磁盘映像创建:
在磁盘映像文件上运行fdisk:
帮助文档:
分区:
格式化分区:fdisk只是将一个存储设备划分为多个分区。每个分区都有特定的文件系统类型,但是分区还不能使用。为了存储文件,必须先为特定的文件系统准备好分区。该操作习惯上称为格式化磁盘或磁盘分区。在Linux中,它被称为mkfs,表示Make文件系统。Linux支持多种不同类型的文件系统。下面使用1440个块把vdisk格式化为ext2文件系统:
格式化后的磁盘应是只包含根目录的空文件系统。但是,Linux的mkfs始终会在根目录下创建一个默认的lost+found目录。完成mkfs之后,设备就可以使用了。在Linux中,还不能访问新的文件系统。它必须挂载到根文件系统中的现有目录中。/mnt目录通常用于挂载其他文件系统。由于虚拟文件系统不是真正的设备,它们必须作为循环设备挂载。下面把vdisk挂载到/mnt目录里边。
df -h查看挂载点,然后卸载设备:
挂载分区:
用dd命令创建一个虚拟磁盘映像:
在vdisk上运行fdisk来创建一个分区P1:
用以下扇区数在vdisk的分区1上创建一个循环设备并将所有循环设备显示为 /dev/loopN:
从下一步开始格式化/dev/loopN时,出现了错误。5号错误表示分区表无效或者被破坏。很可能是因为之前创建的分区提示的losetup: vdisk: Warning: file does not fit into a 512-byte sector; the end of the file will be ignored.导致的。
这一步导致了我无法把它格式化成EXT2文件系统,从而导致我无法挂载循环设备,显示缺失:
问题出在分区所分的空间不对,这个具体怎么计算的教材上摸棱两可的带过去了,导致我现在也很迷= =
7.4 EXT2文件系统
前面我们对它进行了部分的学习,接下来我们来继续了解一下。
首先是简单的EXT2文件系统布局中的磁盘块的内容:
- Block#0:引导块,文件系统不会使用它。它用于容纳从磁盘引导操作系统的引导程序。
- Block#1:超级块(在硬盘分区中字节偏移量为1024)。用于容纳关于整个文件系统的信息。
- Block#2:块组描述块(硬盘上的s_first_data_blocks-1) EXT2将磁盘块分成几个组。每个组有8192个块(硬盘上的大小为32K)。每组用一个块组描述符结构体描述。
- Block#8:块位图是用来表示某种项的位序列,例如:磁盘块或索引节点。
- Block#9:索引节点位图,一个索引节点就是用来代表一个文件的数据结构。
- Block#10:索引(开始)节点块,每个文件都用一个128节点的独特索引节点结构体表示。其中i_block[15]数组包括指向文件磁盘块的指针,这些磁盘块有:
- 直接块:指向直接磁盘块;
- 间接块:每个块编号指向一个磁盘块;
- 双重间接块:每个块指向256个磁盘块;
- 三重间接块:对于小型的“EXT2”文件系统,可以忽略这个块。
- 数据块:紧跟在索引节点块后面的是文件存储块。
第八章:使用系统调用进行文件操作
常用系统调用:
stat 获取文件状态信息
open 打开一个文件进行读、写、追加
close 关闭打开的文件描述符
read 读取打开的文件描述符
write 写入打开的文件描述符
lseek 重新定位文件描述符的读/写偏移量
dup 将文件描述符复制到可用的最小描述编号中
dip2 将oldfd复制到newfd中,如果newfd一打开,先将其关闭
link 将新文件硬链接到旧文件
unlink 减少文件的链接数;如果链接数达到零,则删除文件
symlink 为文件创建一个符号链接
readlink 读取符号链接文件的内容
umask 设置文件创建掩码;文件权限为 (mask&~umasl)
8.3 使用系统调用进行文件操作
系统调用
以两种不同的模式运行,即内核模式和用户模式,用户模式的权限非常有限,不能执行任何需要特殊权限的操作,有特殊权限的操作必须在Kmode下执行。
系统调用必须由程序自身发出,实例如下
int syscall(int a,int b,int c,int d);
其中syscall() 执行一个系统调用,第一个参数是系统调用编号,后面的参数是对应内核函数的参数。
系统调用可让进程从用户模式切换到内核模式,内核的系统调用处理程序根据系统调用编号number将调用路由到一个相应的内核函数。当进程结束执行内核函数后会返回到用户模式并得到所需结果;如果失败,错误编号会记录在errno中,可以通过strerror获取错误对应的描述字符串。成功返回0,失败返回-1。
因为没有新目录,所以系统调用成功,返回值为0.如果我们接下来再运行一次,就会因为目录存在而失败了:
8.5 链接文件
硬链接文件命令:ln oldpath newpath
软连接文件命令:ln -s oldpath newpath
8.6 stat系统调用
所有的stat系统调用都以stat结构体形式返回信息,其中包含以下片段:
struct stat{
dev_t st_dev;
ino_t st_ino;
mode_t st_mode;
st_nlink; nlink_t
uid_t st_uid;
gia_t st_gid;
dev_t st_rdev;
off_t st_size;
u32 st_blksize;
u32 st_block8;
time_t st_atime;time_E
st_mtime time_t st_ctime;
}
这部分书上主要是介绍了stat的具体结构,想要深入学习的话可以参考这篇链接:https://blog.csdn.net/qq_43471489/article/details/124935173
接下来看看opendir-readdir函数:
opendir()、readdir()和closedir()这三个函数主要用来遍历目录,使用前需要包括sys/types.h和dirent.h这两个头文件。closedir与opendir搭配使用,我们可以与C语言中的fopen和fclose函数联系,其作用是相同的。
8.7 open-close-lseek系统调用
-
open:打开一个文件进行读、写、追加
-
close:关闭打开的文件描述符
-
read:读取打开的文件描述符
-
write:写入打开的文件描述符
-
lseek:将文件描述符的字节偏移量重新定位成偏移量
-
umask:设置文件创建掩码;文件权限(mask&~umask)
这一部分也与c语言很类似,可以参考这个学习:https://blog.csdn.net/S5242/article/details/115719892
(二)最有收获的内容
7、8章围绕着文件操作展开,让我们更加深入的了解了Linux的文件系统。最让我收获颇丰的应该是直接进行系统调用进行文件操作了。这让我深入了解了一个文件系统是怎么样进行文件操作的。之所以使用系统调用是因为系统资源的有限性以及内核管理的方便,系统调用将上层内的应用开发与底层的硬件实现分开,上层应用不需要关注底层硬件的具体实现。Linux的系统调用使用软中断实现,使用系统调用后,该程序的状态将从用户态切换到内核态。库函数实现最终也要调用系统调用函数,但它封装了系统调用操作,从而增加了代码的可移植性。
三、问题与解决思路
这两章比较抽象,难免遇到了一些问题
比如这个:
看报错知道是路径出了问题,但是按照tree显示的路径来输一直不正确,直到我进入文件直接把它的路径复制下来我才知道中间少了个username……
还有动态库的连接问题
挺抽象的,明明之前静态链接库使用绝对路径指定头文件目录都可以,为什么到动态库就不行了……解决方法很简单,就是用-Iinclude指定头文件的目录,这个也可以在静态链接库那里使用。
还有文件分区的问题,这个在上面提出来过了,解决方法应该是要计算好分区的大小再分区就行。
最后是软链接和硬链接,它们究竟有什么区别?
参考了这篇博客得以解决:https://blog.csdn.net/m0_60861848/article/details/125844816
简单的理解:软链接就像快捷方式,硬链接就像副本。
最后总结一下,这两章确实很抽象,但是我们只要多实践其实也能很好的理解Linux的文件系统和花里胡哨的文件操作方法。