基本概念:
文件描述符:(文件描述符有时称为文件句柄)(进程级)
文件描述符是一个简单的整数,用以标明每一个 被 进程所打开的文件和socket. 通常0 1 2被标准输入、标准输出、标准错误(默stdin,stdout,stderror)认占用,然后从3开始依次递增。
当一个进程成功打开一个文件,内核会返回一个文件描述符。linux操作系统通常给每个进程能打开的文件数量强加一个限制。通常系统默认限制是1024,即(0~1023)此限制可能会极大的影响性能,当所有的文件描述符用完之后之后,会导致新的连接服务被拒绝。
当然可以尝试修改文件描述符的个数 使用 ulimit -HSn 65535 来修改文件描述符个数.不过这只是临时修改,只对当前窗口有效。也有永久修改的方式,网上有很多教程。
文件描述符只在一个进程内部有效。两个不同进程可以使用同样的文件描述符,但二者并不一定指向同一个文件。如果要指向同一个文件,就是必须两个文件描述符要指向内核中相同的文件表项。
两个不同的文件描述符,若指向同一个打开文件句柄,将共享同一文件偏移量。因此,如果通过其中一个文件描述符来修改文件偏移量(由调用read()、write()或lseek()所致),那么从另一个描述符中也会观察到变化,无论这两个文件描述符是否属于不同进程,还是同一个进程,情况都是如此。
可以看到unix系统/proc进程表下每一个目录代表一个进程,目录号即为进程号。进入任意目录如/proc/2285/fd,这个目录里存的就是文件描述符。
与每个文件描述符相关联的是:
1)文件描述符标志(close-on-exec)
2)指向一个文件表项的指针.
注:其中close-on-exec标志,如果某个文件符设置了该标志,fcntl(fd,F_SETFD,1),则在该进程调用exec函数之前为exec族函数释放
文件表:(系统级)
内核为所有打开文件维持一张文件表,每个文件表项包括:
1).文件状态标志:读,写,添写,同步,非阻塞等.
2).当前文件偏移量.
3).指向该文件v节点表项的指针.
v节点:(系统级)
.每个打开文件都有个v节点结构,v节点包含了文件类型和对此文件进行各种操作的函数指针. 对于大多数文件,v节点还包含了该文件的i节点. 这些 信息是在打开文件时从磁盘上读入内存的,所以所有关于文件的信息都是随时快速可供使用的.( linux下没有使用v节点,而是使用了通用i节点结 构. 虽然两种实现有所不同,但是在概念上,v节点与i节点是一样的. 两者都指向文件系统特有的i节点结构 )
现在我们来大致看看这个结构吧:
文件描述符、文件表、v节点时存在内存中的。inode与block时磁盘管理的概念。
i节点:(inode)
Inode其实就是索引号,便于我们寻找我们文件所存储的数据块block,索引式文件系统在查找信息,读写操作上都比原来的文件系统要快,我们可以通过inode中记录的block信息一次性将数据读取出来,但是采用fat文件系统我们需要先找出第一个block,在通过第一个找第二个数据块,再通过第三个找第四个,这样如果数据不是在一个磁道上,我们可能需要将磁盘读取好几遍才能读出整个文件。
inode记录的信息:
文件的权限(rwx)
所有者,所属组
链接数
时间信息(atime,mtime,ctime)
文件大小
文件指向数据块(block)的指针
inode 也会消耗硬盘空间 每个inode的大小 一般是128字节或256字节。使用df -i命令可以查看每个硬盘分区的inode总数和已经使用的数量。
磁盘block:
文件存储在硬盘上,硬盘最小存储单位是“扇区”,每个扇区存储512字节。连续的八个扇区组成一个block,是文件存取的最小单位。
block是用来存放数据内容的,一般块的大小有1k,2k,4k,8k等。
存放规则:
- 一个block只能存放一个文件的数据
- 如果一个block放不下一个文件的数据就会找到另一个block继续存放
- 即使一个文件数据小于一个block,这个block也不能在存储其他文件数据。其他文件只能存在一个新的block中
我们可以把一个磁盘分成一个或多个分区
在图下中有两个目录项指向同一 i节点。每个i节点中都有一个连接计数,其值是指向该 i节 点的目录项数。只有当连接计数减少为 0时,才可删除该文件。
inode和block关系:
文件系统在磁盘格式化的时候就将 inode 与 block 规划好了,操作系统会自动把硬盘分为两个区,一个区域来存储我们的数据信息,另一个区域来存储我们的inode结点信息。除非重新格式化(或者利用resize2fs等指令变更文件系统大小),否则 inode 与 block 固定后就不再变动。
Inode中还有一个十分重要的信息,是一个指针数组,这些指针指向了数据磁盘块的地址,如果这些指针出现错误那操作系统就不能读取这个文件的数据。所以我们经常操作的删除文件,只不过是让对应的inode结点和对应的磁盘数据分开了,所以有一些工具是可以帮助我们找到我们之前所删除掉的文件。所以真正删除文件只能是通过格式化,或者通过大文件来把他覆盖掉。
一个文件只有一个inode,但一个文件可以有很多block。在inode中,存在着指向数据块的指针,我们新创建一个文件,默认没有数据,当我们开始存储数据时,inode中的指针开始指向一个block,如果一个block装不下,就会指向另一个block存储数据,然后还又二级指针,即inode中的指针指向一个block后,这个block充当指针来分别指向不同的block,这样就可以存储大量的数据。
虚拟内存:
它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换
程序在被运行起来后,也即进程,拥有自己独立的虚拟地址空间,这个虚拟地址空间的大小由计算机硬件平台所决定,具体的是由CPU地址线的数量所决定,例如16位CPU决定了其虚拟地址空间的地址为 0 到 216−1216−1,也即 0x0000 ~ 0xFFFF
,而32位CPU则具有32位寻址能力。
虚拟内存并不是真正的内存,在32位平台上,它是通过映射的方法使得虚拟内存达到4GB。对于超过物理内存的数据可以将不活跃的数据缓存在磁盘上。需要的时候再加载回内存。
假设你正在使用的计算机实际物理内存只有 1GB 大小,而当前系统运行了三个进程,Linux 会将 PM 【虚拟内存(VM)物理内存(PM)】中的某些内存映射为三个大小均为 4GB 的虚拟内存 ,让每个进程都以为自己独自拥有了完整的内存空间,他们虽然得到的物理内存是完全不一样,但是从进程的角度来看他们三个得到的内存确实一模一样的。
虚拟内存布局分为内核空间、栈、堆、数据段、代码段和一个不允许访问的空间(相当于一堵墙)。
open过程
fd=fopen()是一个系统调用。用于依据文件名称打开一个文件。返回该文件的文件描写叙述符,文件打开后进程便能够依据文件描写叙述符fd进行其它操作,比方读,写,关闭等操作。
各个操作系统打开文件的过程是类似的,本文以Unix为例,介绍打开一个文件操作系统所做的工作。正式介绍这个过程之前先简要介绍几个概念。
PCB(process control block)进程控制块。它是一个内核数据结构,相当于一个档案,是操作系统感知进程存在的唯一标识。包含进程状态,进程id,PC,寄存器,内存信息,文件打开信息等,例如以下图所看到的
FCB(file control block)文件控制块,是文件系统的一部分。在磁盘上通常会创建一个文件系统,文件系统中包括文件夹信息。以及文件的FCB信息。FCB一半包括文件的读写模式。全部者,时间戳,数据块指针等信息。unix的FCB称为inode。其结构例如以下图所看到的
文件打开的步骤例如以下图所看到的(从右往左看)
首先,操作系统依据文件名称a,在系统文件打开表中查找
第一种情况:
假设文件a已经打开。则在进程文件打开表中为文件a分配一个表项,然后将该表项的指针指向系统文件打开表中和文件a相应的一项;
然后再PCB中为文件分配一个文件描写叙述符fd,作为进程文件打开表项的指针,文件打开完毕。
另外一种情况:
假设文件a没有打开。查看含有文件a信息的文件夹项是否在内存中。假设不在,将文件夹表装入到内存中,作为cache。
依据文件夹表中文件a相应项找到FCB在磁盘中的位置。
将文件a的FCB装入到内存中的Active inode中。
然后在系统文件打开表中为文件a添加新的一个表项,将表项的指针指向Active Inode中文件a的FCB;
然后在进程的文件打开表中分配新的一项,将该表项的指针指向系统文件打开表中文件a相应的表项。
然后在PCB中,为文件a分配一个文件描写叙述符fd,作为进程文件打开表项的指针,文件打开完毕。
了解完上面的概念之后再理解不同进程之间open文件的关系过程就方便了。
如果两个独立进程各自打开了同一个文件,则就会出现下图的这种情况:
我们假定第一个进程在文件描述符3上打开该文件,而另一个进程在文件描述符4上打开该文件. 打开该文件的每个进程都获得各自的一个文件表项,但对一个给定的文件只有一个v节点表项.之所以每个进程都获得自己的文件表项,是因为这可以使每一个进程都有它自己的对该文件的当前偏移量。
同一个文件描述符的文件表项是fork()出来的子进程和父进程共用的资源,所以父子进程共用同一份文件项,共用一份V节点表,共用同一个i节点表。fork会将父进程的所有资源,包括: 代码段、数据段、堆、栈、缓冲区以及cpu的状态都拷贝一份给子进程。这么说其实还不准确,子进程拥虽然有与父进程相同但独立的地址空间,但并不会立即复制,而是利用COW技术(copy on write——写时拷贝)减少开销。注:下面情况open文件在fork出子进程之前。
进程创建fork后,子进程继承父进程的文件描述表,不继承共享文件表项和iNode。
父进程创建一个子进程后,文件表项中的引用计数器加1变为2,当父进程操作close操作后,计数器减1,子进程还是可以使用文件表项,只有当计数器为0时,才会释放文件表项。
磁盘管理之inode与block - 走看看 (zoukankan.com)
(52条消息) Linux讲解 文件系统 inode节点_Hanani_Jia的博客-CSDN博客
linux — 浅析文件描述符 文件表项 v节点表项-蒲公英云 (dandelioncloud.cn)
深入理解linux系统(block与inode的介绍) - HSping - 博客园 (cnblogs.com)
(53条消息) 带你深入理解Linux文件系统(inode与block的详解、5分钟搞懂硬链接与软链接)_Xucf1的博客-CSDN博客