linux文件漫谈

在linux中一切都是文件

什么是系统API
1.API是一些函数,由linux系统提供支持,供应用层程序使用
2.应用层通过调用API来调用操作系统中的各个功能,比如操作系统中的fs文件系统提供了操作文件API等

linux常用文件IO接口
open,close,write,read,lseek

文件操作的一般步骤
1.先open打开一个文件,得到一个文件描述符,然后对文件进行读写或其他操作,最后close关闭文件.

关于文件讲解
文件平时是存在块设备中的文件系统中,这种文件称为静态文件。
当我们open打开一个文件时,linux内核做的操作有:
1.内核在进程(打开文件的程序进程)中建立了一个打开文件的数据结构,记录下我们打开的这个文件。
2.内核在内存中申请一段内存,并将静态文件的内容从块设备中读到内存中特定地址管理存放,这时候叫动态文件
额外的:
现在大多数的系统采用分页机制,在打开文件成功后,文件并没有加载进入内存,而是内核生成了一个文件描述符,文件描述符含有一个指向文件indoe结构的指针,在这个结构中存有文件真正的节点信息和位置。
当读取的时候,cpu首先判定需要读取的位置是否在内存上,如果再则直接读取(没有所谓的按行读取一说,按行读取不过是找文件中的换行标而已,当读到换行符的时候返回结果),
如果不在内存上,则通过内存管理器进行加载,实际上,无论你是读取一个字符还是一行,加载的大小是固定的,
比如系统机制是加载1M,当你文件大于1M时也只加载1M,当不够时全部加载。对不不同系统方式不同。(当然系统如果对读文件有特殊的优化也不同)
当你读取之后,文件是继续留在内存还是释放,这里有另一套极其复杂的机制在管理。因为在你读第一行和第二行的时间里,还有许多事情在并发的执行。

打开文件后,以后对这个文件的读写操作,都是针对内存中这一份动态文件,而并不是针对静态文件,当我们对动态文件进行读写后,
此时内存中的动态文件和块设备中的静态文件就不同步了,当我们close关闭动态文件时,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的静态文件。

为什么读写文件要这么设计?
因为块设备本身有读写限制(读写单位为块,而不是字节),不灵活,而内存就可以按字节为单位来操作,而且可以随机操作(内存就叫RAM random),所以就这么设计了

文件描述符
1.其实就是handle值,其实质是一个数字,来表示动态文件,便于区分各个动态文件
2.操作系统有一张handle_table表来保存这些handle值
3.文件描述符的作用域就是当前进程,出了当前进程这个文件描述符就没有意义了

操作文件的属性flag
1.O_APPEND 新写入的内容追加到原来内容后面
2.O_TRUNC 默认使用,不管原来的内容,替换掉原来的内容
3.O_RDONLY 只读
4.O_WRONLY 只写
5.O_RDWR 可读可写
6.O_CREAT 当前打开的文件并不存在,我们需要去创建它,如果文件存在,则重新重建文件,原来的内容被删除
7.O_EXCL 可以查看当前文件是否存在,可以和O_CREAT结合使用,当文件不存在则创建,当文件存在则提醒
8.O_NONBLOCK 非阻塞
9.O_SYNC 无O_SYNC时,write只是将内容写入底层缓冲区(动态文件)即可,然后底层在合适的时候会将缓冲区buf(动态文件)中的内容一次性同步到硬件设备,这样设计师为了提供硬件寿命,如果想
直接写入硬盘中,设置为O_SYNC
阻塞与非阻塞只用于设备文件,而不用于普通文化

生成的文件具有什么样的权限(也就是说生成的文件是否具有只读,或者只写,或者可执行等权限),则需要在使用O_CREAT时,再添加第三个
参数mode来指定要创建的文件的权限。
mode使用4个数字来指定权限
比如0666,则表示可读可写可执行

如果程序向提前结束
1.在main中使用return,正常终止return 0,异常终止使用return -1
2.正式的终止使用exit(0),_exit(0),_Exit(0)。

文件IO效率和标准IO
1.文件IO就是指open,close等系统API,虽然该体系可以很好的完成文件读写,但是效率并不是很高
2.标准IO是C语言库函数提供了一些读写文件的函数,fopen,fclose等,但其实这些函数都是封装了系统API,在应用层添加了一个缓冲
机制,这样就有两个缓冲机制,还有一个是内核层的缓冲区。所以在使用fwrite的时候,首先先进入应用层的buf,然后标准IO库根据操作系统
单次write的最佳count来选择好的时机来完成write到内核中的buf(动态文件)

linux系统如何管理文件
1.硬盘中的静态文件和inode
(1)硬盘分为一个一个扇区seat(512字节等),一个一个扇区又组成一块block(比如64个扇区),然后又很多块block又组成块设备
(2)文件存在扇区,一个扇区是我们访问硬件最小的单元,所以如果一个文件只有20个字节,照样独占一个扇区,不可能同时有两个文件放在同一个扇区,
所以文件很小的情况下,很浪费硬盘(所以压缩包就可以很多个文件包含在一起,占一个扇区),如果文件10K则需要多个扇区,所以要有一个记录(一张表)哪个扇区属于哪个文件,
(3)一块硬盘可以分为2大区域:
a.硬盘内容管理表项
b.真正存储内容的区域
所以操作系统访问硬盘时,先去读取硬盘内容管理表,从中找到我们要访问的那个文件的扇区级别的信息,然后再利用这个信息去查询真正存储内容的区域,最后得到我们要的文件
(4)硬盘内容管理表中以文件为单位记录各个文件的信息,每一个文件又一个信息列表(inode,其实就是一个就结构体,记录文件名,文件在硬盘上的扇区号,块号等)
每一个文件都有一个inode,每一个inode都对应一个数字编号和一个结构体,该结构体记录了各种信息。
平时的实践:
快速格式化:就删除了硬盘内容管理表inode,而不删除真正存储的数据,所以还是可以恢复的
底层格式化:全删除,基本恢复就没戏了
2.内存中的打开的文件和vnode
(1)在程序中打开的文件就属于某个进程,每个进程都会有一个数据结构来记录这个进程所有信息(进程信息表_Eprocess结构体),表中有指针会指向一个文件管理表
文件管理表中记录了当前进程打开的所有文件及相关信息,文件管理表中用来索引各个打开的文件index其实就是文件描述符fd,所以反过来只要比知道文件描述符fd
就可以对文件进行各种操作。
3.文件与流的概念
流(stream),其实就是文件被读出写入时都只能一个字节一个字节的进行,而不能一次性写入,所以N个字符有序的读出/或者写入就形成流

lseek函数详解off_t lseek(int fd,off_t offset,int whence(参照物,从什么时候开始移动,包含有seek_set,seek_cur,seek_end,默认为seek_cur))
(1)动态文件在内存中的形态是以文件流的形式,我们会通过文件指针(光标)来表征正在操作的位置,这个指针也被记录在文件管理表这个结构体中,这个指针不能
被直接访问,linux系统用lseek函数来访问这个文件指针。其实一般我们在nodepa++来点击鼠标然后移动光标,其实就是调用lseek操作。
(2)假设我们从空文件中先write12个字节,然后再read的时候,会读到空,这是因为write以后,文件指针已经到了末尾,就读不出数据了

使用lseek计算文件长度
在linux中并没有一个函数可以直接返回文件的长度,所以可以直接使用filesize = lseek(fd,0,send_end)就可以了,一般也这么做

用lseek构建空洞文件
1.空洞文件就是这个文件中有一段是空的(不是空格,是真的没有)
2.普通文件中间不能有空的,因为write中的文件指针一次从前到后移动,不能跳着写。
3.所以我们可以用lseek跳过一段以后再写,比如跳过10字节,然后在写4字节,则会有10字节的空洞,但文件大小还是14字节
4.空洞文件有什么用?
可以在多线程中共同操作文件,比如我们创建一个很大的文件,如果从头开始依次构建就很慢,所以可以分为多段然后多线程并行写,注意这个多段写入的是动态文件,
然后当达到最佳count的时候就会写入到块设备,这样还没有写进来的部分就是空洞了。
这样的例子也可以在当我们下载电影的时候,文件的下载都是一段一段乱序的。

多次打开同一个文件与O_APPEND
1.同一进程多次打开同一文件,分别获得文件描述符fd1,fd2,然后再依次执行读操作,则结果为:
fd1和fd2分别读,说明fd1和fd2所对应的文件指针是不同的文件指针,不同的vnode,所以信息都是独立的
2.同一进程多次打开同一个文件并分别写,由上面例子的现象可知,后面写的数据会覆盖掉前面的写的数据,因为文件指针不同。
3.以上都是分别写,那如果想要接续写则需要加0_APPEND

为什么O_APPEND可以实现接续写,关键是文件指针,O_APPEND标志可以让write,read函数内部多做一件事情,就是移动自己指针同时会去把别的文件指针
同时移动。

文件共享的实现方式
就是同时可以多个程序或者线程进行读写
1.文件共享的核心是怎么弄出来多个文件描述符指向同一个文件
2.常见的有3种情况文件共享
(1)同一个进程多次open打开同一个文件,文件指针可以关联
(2)在不同进程中分别使用open打开同一个文件(这时候因为fd在不同的进程中,所以两个fd值可以相同也可以不同),文件指针不可以关联
(3)linux系统提供了dup和dup2两个API来让进程复制文件描述符,只有一个文件指针,就没有办法实现分别写,只能接续写
其实最关键的还是文件指针是否独立
E_Process -> 文件描述符表[fd] -> 文件表(包含文件状态标志,当前文件偏移量,V节点指针,其实就是vnode) ->v节点(I节点信息,当前信息长度)

文件描述符
fd从0开始一次增加
在linux0.11版本中fd最大是20,所以一个进程最多允许打开20个文件
fd中0,1,2已经被系统默认占用了,因此用户进程得到最小的fd值是3,也说明了打开一个进程默认打开了3个文件
这三个文件分别为stdin(标准输入),stdout(标准输出),stderr(标准错误)
标准输入一般对应的是键盘,所以0对应的是键盘的设备文件
标准输出一般是LCD显示器,所以1对应LCD的设备文件,printf函数其实也就是默认输出到标准输出stdout上了
操作系统怎么实现原子操作(一旦执行就不能停,必须执行完)

复制文件描述符dup,dup2
常见的linux操作就是使用> 2.txt符号将输出放入2.txt,其实内部操作就是open+close+dup,opne 2.txt,然后close(1)关闭stdout,然后dup将
1和2.txt文件关联起来

fcntl函数
是一个多功能文件管理的工具箱,接受2个参数(fd,cmd)和1个变参

常用cmd
F_DUPFD 用来复制文件描述符,作用和dup差不多

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值