作者|cxuan
来源|Java建设者(ID:javajianshe)
在Linux中,最直观、最可见的部份就是文件系统(filesystem)。下边我们就来一起阐述一下关于Linux中国的文件系统,系统调用以及文件系统实现背后的原理和思想。这种思想中有一些来始于MULTICS,如今早已被Windows等其他操作系统使用。Linux的设计理念就是小的就是好的(SmallisBeautiful)。尽管Linux只是使用了最简单的机制和少量的系统调用,并且Linux却提供了强悍而高贵的文件系统。
Linux文件系统基本概念
Linux在最初的设计是MINIX1文件系统,它只支持14字节的文件名,它的最大文件只支持到64MB。在MINIX1以后的文件系统是ext文件系统。ext系统相较于MINIX1来说,在支持字节大小和文件大小上均有很大提高,而且ext的速率仍没有MINIX1快,于是,ext2被开发下来,它还能支持长文件名和大文件,并且具有比MINIX1更好的性能。这使他成为Linux的主要文件系统。只不过Linux会使用VFS曾支持多种文件系统。在Linux链接时,用户可以动态的将不同的文件系统挂载倒VFS上。Linux中的文件是一个任意宽度的字节序列,Linux中的文件可以包含任意信息,例如ASCII码、二补码文件和其他类型的文件是不加分辨的。为了便捷起见,文件可以被组织在一个目录中,目录储存成文件的方式在很大程度上可以作为文件处理。目录可以有子目录,这样产生有层次的文件系统,Linux系统下边的根目录是/linux服务器代维,它一般包含了多个子目录。字符/还用于对目录名进行分辨,比如/usr/cxuan表示的就是根目录下边的usr目录,其中有一个称作cxuan的子目录。下边我们介绍一下Linux系统根目录下边的目录名在Linux中,有两种路径,一种是绝对路径(absolutepath),绝对路径告诉你从根目录下查找文件,绝对路径的缺点是太长并且不太便捷。还有一种是相对路径(relativepath),相对路径所在的目录也称作工作目录(workingdirectory)。假如/usr/local/books是工作目录,这么shell命令
cp books books-replica
就表示的是相对路径,而
cp /usr/local/books/books /usr/local/books/books-replica
则表示的是绝对路径。在Linux中时常出现一个用户使用另一个用户的文件或则使用文件树结构中的文件。两个用户共享同一个文件,这个文件坐落某个用户的目录结构中,另一个用户须要使用这个文件时,必须通过绝对路径才会引用到他。假如绝对路径很长,这么每次输入上去会变的十分麻烦,所以Linux提供了一种链接(link)机制。举个反例,下边是一个使用链接之前的图
以上所示,例如有两个工作帐户jianshe和cxuan,jianshe想要使用cxuan帐户下的A目录,这么它可能会输入/usr/cxuan/A,这是一种未使用链接以后的图。使用链接后的示意如下
如今雨林木风linux,jianshe可以创建一个链接来使用cxuan下边的目录了。‘当一个目录被创建下来后,有两个目录项也同时被创建下来,它们就是.和..,后者代表工作目录自身,前者代表该目录的父目录,也就是该目录所在的目录。这样一来,在/usr/jianshe中访问cxuan中的目录就是../cxuan/xxxLinux文件系统不分辨c盘的,这是哪些意思呢?通常来说,一个c盘中的文件系统互相之间保持独立,假如一个文件系统目录想要访问另一个c盘中的文件系统,在Windows中你可以像下边这样。
两个文件系统分别在不同的c盘中,彼此保持独立。而在Linux中,是支持挂载的,它容许一个c盘挂在到另外一个c盘上,这么里面的关系会弄成下边这样
挂在以后,两个文件系统就不再须要关心文件系统在那个c盘上了,两个文件系统彼此可见。Linux文件系统的另外一个特点是支持加锁(locking)。在一些应用中会出现两个或则更多的进程同时使用同一个文件的情况,这样很可能会造成竞争条件(racecondition)。一种解决方式是对其进行加不同细度的锁,就是为了避免某一个进程只更改某一行记录因而造成整个文件都不能使用的情况。POSIX提供了一种灵活的、不同细度级别的锁机制linux 文件系统优化,容许一个进程使用一个不可分割的操作对一个字节或则整个文件进行加锁。加锁机制要求尝试加锁的进程指定其要加锁的文件,开始位置以及要加锁的字节Linux系统提供了两种锁:共享锁和互斥锁。假如文件的一部份早已加上了共享锁,这么再加排他锁是不会成功的;假如文件系统的一部份早已被加了互斥锁,这么在互斥锁解除之前的任何加锁都不会成功。为了成功加锁、请求加锁的部份的所有字节都必须是可用的。在加锁阶段,进程须要设计好加锁失败后的情况,也就是判别加锁失败后是否选择阻塞,假如选择阻塞式,这么当早已加锁的进程中的锁被删掉时,这个进程会解除阻塞并替换锁。假如进程选择非阻塞式的,这么就不会替换这个锁,会立即从系统调用中返回,标记状态码表示是否加锁成功,之后进程会选择下一个时间再度尝试。加锁区域是可以重叠的。下边我们演示了三种不同条件的加锁区域。
如上图所示,A的共享锁在第四字节到第命理节进行加锁
如上图所示,进程在A和B上同时加了共享锁,其中6-8字节是重叠锁
如上图所示,进程A和B和C同时加了共享锁,这么第六字节和第七字节是共享锁。假如此时一个进程尝试在第6个字节处加锁,此时会设置失败并阻塞linux 文件系统优化,因为该区域被ABC同时加锁,这么只有等到ABC都释放锁后,进程能够加锁成功。
Linux文件系统调用
许多系统调用就会和文件与文件系统有关。我们首先先看一下对单个文件的系统调用,之后再来看一下对整个目录和文件的系统调用。为了创建一个新的文件,会使用到creat方式,注意没有e。这儿说一个小插曲,当初有人问UNIX创始人KenThompson,假如有机会重新写UNIX,你会如何办,他回答自己要把creat改成create,哈哈哈哈。这个系统调用的两个参数是文件名和保护模式
fd = creat("aaa",mode);
这段命令会创建一个名为aaa的文件,并按照mode设置文件的保护位。这种位决定了那个用户可能访问文件、如何访问。creat系统调用不仅仅创建了一个名为aaa的文件,就会打开这个文件。为了容许后续的系统调用访问这个文件,这个creat系统调用会返回一个非负整数,这个就称作文件描述符(filedescriptor),也就是前面的fd。假如在早已存在的文件上调用了creat系统调用,这么该文件中的内容会被消除,从0开始。通过设置合适的参数,open系统调用也就能创建文件。下边让我们看一看主要的系统调用,如下表所示
为了对一个文件进行读写的前提是先须要打开文件,必须使用creat或则open打开,参数是打开文件的形式,是只读、可读写还是只写。open系统调用也会返回文件描述符。打开文件后,须要使用close系统调用进行关掉。close和open返回的fd总是未被使用的最小数目。哪些是文件描述符?文件描述符就是一个数字,这个数字标识了计算机操作系统中打开的文件。它描述了数据资源,以及访问资源的方法。当程序要求打开一个文件时,内核会进行如下操作文件描述符由惟一的非负整数组成,系统上每位打开的文件起码存在一个文件描述符。文件描述符最初在Unix中使用,而且被包括Linux,macOS和BSD在内的现代操作系统所使用。当一个进程成功访问一个打开的文件时,内核会返回一个文件描述符,这个文件描述符指向全局文件表的entry项。这个文件表项包含文件的inode信息,字节位移,访问限制等。诸如右图所示
默认情况下,前三个文件描述符为STDIN(标准输入)、STDOUT(标准输出)、STDERR(标准错误)。标准输入的文件描述符是0,在终端中,默认为用户的按键输入标准输出的文件描述符是1,在终端中,默认为用户的屏幕与错误有关的默认数据流是2,在终端中,默认为用户的屏幕。在简单聊了一下文件描述符后,我们继续回到文件系统调用的阐述。在文件系统调用中,开支最大的就是read和write了。read和write都有三个参数这就是所有的参数了,这个设计十分简单轻巧。其实几乎所有程序都按次序读取和写入文件,并且个别程序须要才能随机访问文件的任何部份。与每位文件相关联的是一个表针,该表针指示文件中的当前位置。次序读取(或写入)时,它一般指向要读取(写入)的下一个字节。假如表针在读取1024个字节之前坐落4096的位置,则它将在成功读取系统调用后手动移至5120的位置。Lseek系统调用会修改表针位置的值,便于后续对read或write的调用可以在文件中的任何位置开始,甚至可以超出文件末尾。lseek=Lseek,段首小写。lseek防止称作seek的缘由就是seek早已在之前16位的计算机上用于搜素功能了。Lseek有三个参数:第一个是文件的文件描述符,第二个是文件的位置;第三个告诉文件位置是相对于文件的开头,当前位置还是文件的结尾
lseek(int fildes, off_t offset, int whence);
lseek的返回值是修改文件表针后文件中的绝对位置。lseek是惟一从来不会导致真正c盘查找的系统调用,它只是更新当前的文件位置,这个文件位置就是显存中的数字。对于每位文件,Linux就会跟踪文件模式(常规,目录,特殊文件),大小,最后更改时间以及其他信息。程序才能通过stat系统调用见到这种信息。第一个参数就是文件名,第二个是指向要放置恳求信息结构的表针。这种结构的属性如右图所示。fstat调用和stat相同,只有一点区别,fstat可以对打开文件进行操作,而stat只能对路径进行操作。pipe文件系统调用被拿来创建shell管线。它会创建一系列的伪文件,来缓冲和管线组件之间的数据,但是返回读取或则写入缓冲区的文件描述符。在管线中,像是如下操作
sort <in | head –40
sort进程将会输出到文件描述符1,也就是标准输出,写入管线中,而head进程将从管线中读入。在这些方法中,sort只是从文件描述符0中读取并写入到文件描述符1(管线)中,甚至不晓得它们已然被重定向了。假如没有重定向的话,sort会手动的从鼠标读入并输出到屏幕中。最后一个系统调用是fcntl,它拿来锁定和解锁文件,应用共享锁和互斥锁,或则是执行一些文件相关的其他操作。现今我们来关心一下和整体目录和文件系统相关的系统调用,而不是把精力放到单个的文件上,下边列举了这种系统调用,我们一上去看一下。
可以使用mkdir和rmdir创建和删掉目录。并且须要注意,只有目录为空时才可以删掉。创建一个指向已有文件的链接时会创建一个目录项(directoryentry)。系统调用link来创建链接,oldpath代表已有的路径,newpath代表须要链接的路径,使用unlink可以删掉目录项。当文件的最后一个链接被删掉时,这个文件会被手动删掉。使用chdir系统调用可以改变工作目录。最后四个系统调用是用于读取目录的。和普通文件类似,她们可以被打开、关闭和读取。每次调用readdir就会以固定的格式返回一个目录项。用户不能对目录执行写操作,而且可以使用creat或则link在文件夹中创建一个目录,或使用unlink删掉一个目录。用户不能在目录中查找某个特定文件,并且可以使用rewindir作用于一个打开的目录,使他能在此从头开始读取。
更多精彩推荐
☞跑路后再删库?思科前员工离职后恶意删库,损失达 240 万美元
点分享 点点赞 点在看