文件IO过程

基本概念:

文件描述符:(文件描述符有时称为文件句柄)(进程级)

 文件描述符是一个简单的整数,用以标明每一个 被 进程所打开的文件和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等。

        存放规则:

  1. 一个block只能存放一个文件的数据
  2. 如果一个block放不下一个文件的数据就会找到另一个block继续存放
  3. 即使一个文件数据小于一个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博客

 文件打开的过程——调用fd=open()时操作系统所做的工作 - 走看看 (zoukankan.com)

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值