[Linux系统编程]文件IO

一.系统调用

什么是系统调用?
只有系统调用(系统函数)才能进入内核空间,库函数也是调用系统函数,才得以访问底层。
系统调用由操作系统实现并提供给外部应用程序的编程接口。是应用程序同系统之间数据交互的桥梁。
换句话说,系统调用就是操作系统封装好的函数接口,使得用户能够利用它调用底层驱动,从而控制硬件。
在这里插入图片描述
操作系统如何管理文件
通过dentry(目录项)和 inode(存放文件信息的数据结构) 来管理文件。
一个文件有自己的目录项和inode,
在目录项中,存放文件名和指向inode的指针。
在inode数据结构中,存放着文件的权限,类型等信息,同时还有一个参数存放文件在磁盘中存放的位置。
在这里插入图片描述软链接: 软连接相当于快捷方式,使用ln -s 原文件 软链接文件名 创建一个文件软连接,仅仅包括所含链接文件的路径名字,当原文件被删除时,软链接失效。

硬链接:实现原理其实是多个dentry指向同一个inode,它并非快捷方式,而是多个目录项共指向同一个inode。

二.open函数,read函数,write函数
1.open
所需头文件:

#include<fcntl.h>
#include<unistd.h>

open函数用来打开文件,具体有以下两种重载类型。
正常打开返回一个int整数(文件描述符),打开出错返回-1

在这里插入图片描述
path : 路径名 flags: 打开方式 mode:生成文件时的权限(4位8进制数)
第一种是文件存在时,指定打开方式
第二种是文件不存在,且要求创建文件时,按mode指定权限

flags分为以下几种:

O_RDONLY :只读
O_WRONLY:只写
O_RDWR:读写
O _APPEND:追加写
O_CREAT:无文件时创建
O_EXCL:是否存在
O_TRUNC:文件清空再打开
O_NONBLOCK:以非阻塞方式打开(使用在设备,网络文件下)
#include<fcntl.h> 这些包含在头文件<fcntl.h>中,所以需要引入#include<fcntl.h>

使用close(文件描述符) ----------关闭流

2.read
read函数返回读入的字节数,读出错误返回-1
read(文件描述符,字符串缓冲区,读出字节数)
在这里插入图片描述
3.write
write函数将缓冲区内容读入文件,返回写入的字节数,写入失败返回-1
write(文件描述符,字符串缓冲区,写入字节数)
在这里插入图片描述

4.案例:使用read write实现cp操作
cp 文件1 文件2
对main函数传递两个参数,代表文件名
文件一,使用只读打开方式
文件二,无文件时创建文件。
使用buffer缓冲区(1024字节),循环输入文件,当read函数读入字符数为空时,read函数返回0,此时退出循环。在这里插入图片描述

二.预读入缓输出

库函数和系统调用执行的速度,一定是系统调用速度更快吗?
答案是不一定,如fputc库函数与write/read 系统函数,若将缓冲区buffer设置为1,使它们每次都读入一个字节,谁的速度更快?
首先明确,由于向磁盘写入数据较慢,在内核区中会设置一个缓冲区,直到缓冲区有足够数据时再一次性写入磁盘。在用户区中,也会有一个缓冲区,它存在的目的是希望数据暂时读入到缓冲区里,然后再一次性输入到内核区的缓冲区里。这是因为从用户区到内核区是需要时间开销的,通过预读入就可以避免多次用户/内核区之间的跳转。
所以,若设置系统调用write/read的自定义缓冲区为1个字节,那么每读入一个字节,就要执行一次从用户区到内核区的跳转,时间开销大。
但是库函数fputc开的缓冲区较大(默认4096字节),先在用户区中预读入,然后再一次性执行系统调用,就避免了大量的时间开销,所以,虽然每次都是读入一个字节,底层实现原理是预读入缓输出,这跟开的缓冲区大小有关。

在这里插入图片描述

三.文件描述符

一个进程在内存中有自己的内存地址空间,其中,PCB(进程控制块)被存放在内存中,用来管理该进程,PCB本质上是一种数据结构,维护着进程的一些信息,其中就包含了文件描述符表。
PCB维护了一个指向该表的指针,通过这个指针可以访问到文件描述符表,文件描述表中的每一个节点,都保存了一个指针,指向某个文件结构体。我们上文所说的文件描述符,实际上是它的下标

默认最大保存文件数:1024
一个进程中运行时会有0:标准输入文件 1:标准输出文件 2:标准错误文件 ,这也解释了为什么open一个文件时,会从3号开始返回。
0:STDIN_FILENO
1:STDOUT_FILENO
2:STDERR_FILENO

三.文件描述符

四.阻塞和非阻塞

常规文件不会产生阻塞等待,只有在打开设备文件,网络文件时才会产生阻塞。
例如标准输入文件STDIN_FILENO
默认打开设备/网络文件是以阻塞方式打开的,可以在open时使用O_NONBLOCK以非阻塞方式打开。
当文件再以非阻塞方式打开式,且无数据输入,read返回-1,且errno = EAGAIN 或者EWOULDBLOCK,此时并不是说读文件错误,而是说明文件可打开,但是没有数据输入。
如下,处理标准输入
在这里插入图片描述

在这里插入图片描述

五.fcntl函数修改文件访问属性
fcntl可以不重新打开,就可以修改文件访问属性
获取当前访问属性: fcntl(“文件描述符”,F_GETFL),返回一个int类型,是一个位图,表示当前访问属性
对于该返回值,设其为flgs, 可以通过 | 为其添加属性---------- flgs |= O_XXXXX
设置属性:fcntl(“文件描述符”,F_SETFL,flgs) ,

在这里插入图片描述

六.lseek函数

在打开文件,进行writr,read操作时,当前的指针位置是共享的,我们可以通过lseek函数来更改当前指针位置。在这里插入图片描述lseek函数的作用

1.移动文件指针到文件头: lseek(fd, 0, SEEK_SET);
2.获取当前文件指针的位置 lseek(fd, 0, SEEK_CUR);

3.获取文件长度 lseek(fd, 0, SEEK_END);
4.拓展文件的长度,当前文件10b, 增加100个字节 lseek(fd, 100, SEEK_END)
注意:拓展完需要再写一次数据,否则拓展无效

七.stat/lstat系统调用

stat函数是用来获取文件信息的。

在这里插入图片描述
其中,pathname是文件路径,buf是一个stat结构体指针,所需头文件 #include <sys/stat.h>,#include <unistd.h>

stat函数返回值为整数,正常获取返回0,获取错误返回-1

buf是一个传出参数,buf指针维护一个结构体,这个结构体中存放了文件的各种信息。
在这里插入图片描述在使用stat函数后,可以通过对buf的访问,得到文件的信息,例如,st.size是该文件的大小,类型为int。

st_mode保存该文件的类型和权限,st_mode实际上是一个整数型,相当于一个位图,可以通过该这串数字获取类型和执行权限。
可以通过以下宏命令,将st_mode作为参数传入,获取文件类型信息。
在这里插入图片描述
穿透软连接
stat 与 lstat 大致相同,不同的点在于对软连接文件进行操作时,stat会穿透软连接文件,去到软连接指向的那个文件,而lstat不会穿透软连接,会直接操作这个软链接文件。

八.link函数与unlink函数

link函数用来创建一个文件的硬链接
unlink函数用来根据文件名删除掉一个硬链接,当一个inode中硬链接减为0时,这个文件才能被删除。(删除操作不是立即执行的,而是等到 调用该文件的进程全部结束时才会被删除)

在这里插入图片描述
在这里插入图片描述
以文件名为参数进行操作, Linux中的指令 ln 就是依据这个系统调用所执行。

unlink函数的特征:
清除文件时,如果文件的硬链接数到0了,没有dentry对应,该文件仍不会马上被释放。
要等到所有打开该文件的进程关闭该文件,系统才会挑时间将该文件释放。

隐式回收机制
当我们打开文件流,开辟内存等操作,而没有进行回收或释放,操作系统会帮我们在程序结束时做这个操作
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值