【Linux】基本IO操作 文件描述符 文件系统

标准库IO操作:

  • FILE * fopen(const char* path, const char* mode);

参数:
path字符串包含欲打开的文件路径及文件名。
mode字符串则代表着流形态,流形式有如下几种:
r :打开只读文件,该文件必须存在。
r+ :打开可读写的文件,该文件必须存在。
w :打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
w+ :打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a:以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。
a+:以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。
b:二进制操作
返回值:
返回一个FILE*的文件流指针作为文件的操作句柄;失败返回NULL。实际上FILE是一个新的数据类型。将FILE理解为一个包括了文件管理有关信息的数据结构, 即在打开文件时必须先定义一个文件指针。

  • size t fread(char buf, size t block_size, size t block_count, FILE
    fp): //(缓冲区,块大小,块个数,文件流指针);

  • size t fwrite(char *data, size t block_size, size t block_count, FILE *fp); //(数据首地址,块大小,块个数,文件流指针);

注意:
fread/fwrite操作的数据实际大小是块大小*块个数;假如块大小=10,块个数=2;意味着要写入/读取20个字节的数据;
返回值:
返回实际操作的块个数 例如:读取一个文件size= 10,count=2;如果文件大小足够则返回2/但是若文件大小只有16字节,则会返回1 ,因为第二块没有读满,fread如果读到了文件末尾会返回0若读取1000个字节,块个数为1,文件大小只有512字节,虽然读取了512的数据但是也会返回0

  • int fseek(FILE *fp, long offset, int whence);//(将文件的读写指针从whence位置偏移offset个字节)

whence从何处开始偏移:该参数只有三个选项,不可随意填写。
SEEK_SET表示文件开头;
SEEK_CUR表示当前位置;
SEEK_END表示文件末尾;
whence 值为SEEK_CUR 或SEEK_END 时, 参数offet 允许负值的出现.

  • int fclose(FILE* fp)//关闭文件流指针,释放资源。

系统调用接口:

  1. int open( const char * pathname,int flags, mode_t mode);

功能: 如果文件存在就直接打开,如果文件不存在,就按照mode指定的文件权限,创建一个该名字的新文件。也就是说三个参数时,不仅包含打开已存在文件的功能,还多了一个创建文件的功能。
返回值: 如果打开成功,返回一个非负整数的文件描述符。
如果打开失败,返回-1,并且设置错误号给系统定义的全局变量errno,用于标记函数到底出了什么错误。
参数:
pathname参数: 指向欲打开的文件路径字符串。
mode参数: 创建文件时,用于指定文件的原始权限.
flags参数:
O_RDONLY: 以只读方式打开文件
O_WRONLY : 以只写方式打开文件
O_RDWR : 以读和写的方式打开文件
上面三个只能选择一个,下面的可以合理的任意组合:

O_CREAT :打开文件,如果文件不存在则建立文件(建立文件一定要指定文件的权限mode)
O_EXCL :单独使用没有意义,通常与O_CREAT结合使用:如果文件不存在新建一个,如果存在报错。这样的意义是保证每次open的是一个新的文件,如果文件以前就存在,提醒你open的不是一个新文件。

O_TRUNC 将文件的长度截为0
O_APPEND 强制write()从文件尾开始
对于终端文件,上面四个是无效,提供了两个新的标志:
O_NOCTTY 停止这个终端作为控制终端
O_NONBLOCK 使open()、read()、write()不被阻塞。

  1. ssize_t read(int fd, void *buf, size_t count);

  2. ssize_t write(int fd,void *buf, size_t count);

参数:
fd: 将要读取数据的文件描述符。
buf:指缓冲区,即读取的数据会被放到这个缓冲区中去,或者从该缓冲区写到文件中去。
count: 表示调用一次read操作或者write操作,应该读或写多少数量的字符。
返回值:
返回所读取或写入的字节数;0(读到EOF);-1(出错)。

  1. off_t lseek(int fd, off_t offset,int whence);

参数:
fd;文件描述符
offset:偏移量,每一个读写操作所需要移动的距离,单位是字节,可正可负(向前移,向后移)
whence:
SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小
SEEK_CUR:当前位置为指针的位置,新位置为当前位置加上偏移量
SEEK_END:当前位置为文件的结尾,新位置为文件大小加上偏移量的大小
返回值:
当调用成功时则返回目前的读写位置,也就是距离文件开头多少个字节。失败:返回-1

文件描述符:

文件描述符概念:

文件描述符(file descriptor) 是内核为了高效管理已被打开的文件所创建的索引,它是一个非负整数,因为它实际是一个数组的下标,当进程打开一个文件“test.txt”,系统会创建一个结构体,struct file用于操作被打开的文件,同时将刚创建的结构体的地址放进系统的另一个结构体 stru file_struct 的数组 struct file* fd_arry[]中。这样我们就可以通过数组fd_aarry[]找到并操作文件“test.txt”了。而文件描述符正是这个数组的下标。

程序刚刚启动的时候,系统会默认打开三个文件,0是标准输入,1是标准输出,2是标准错误。如果此时去打开一个新的文件,它的文件描述符会是3。POSIX标准要求每次打开文件时必须使用当前进程中最小可用的文件描述符号码,也就是将新文件结构体的地址放在数组下标最小并且未使用的空间。

为什么打开一个文件一定要关闭?

因为数组的大小是有限的,当若不关闭文件,数组满了,进程就无法打开新的文件了。关闭文件数组的相应位置就可以填入新文件对应结构体的位置了。
在这里插入图片描述

文件描述符 和文件流指针的关系:

通过文件描述符是和文件流指针我们都可以找到并操作文件,前者在系统调用接口中使用,后者在库函数中使用。而库函数实际上是对系统调用接口的封装。运行库函数最终还是调用了对应的系统调用接口,通过文件流指针FILE* fp 操作文件,实际上还是找到 对应的文件描述符,然后通过文件描述符操作文件的。FILE是一个结构体,该结构体中有一个成员变量fileno–就是文件描述符。我们通常所说的缓冲区,实际上也是文件流指针结构体中定义的缓冲区; 这个缓冲区通常被称之为用户态缓冲区。
调用fwrite()向文件中写入数据,实际上是先将数据写入缓冲区,刷新缓冲区的时候才会将数据写入文件。而系统调用接口write()会直接将数据写入文件,系统调用接口没有缓冲区,只有库函数才有缓冲区。

重定向:

其实printf并非一定要把数据写到标准输出文件中,而是因为printf函数中文件描述符是1,它只是会固定的将数据写入fd_arr[1]所对应的文件中。通常情况下,fd_arry[1]所对应的文件就是标准输出。但是如果改变fd_arry[1]的内容,假如换成“test.txt”对应的信息。printf就会写入“test.txt”。这个改变的过程就是重定向我们可以用函数int dup2(int oldfd , int newfd);让newfd这个描述符也指向oldfd所指向的文件,这时候oldfd和newfd都能够操作 oldfd所指向的文件

文件系统:

文件系统的大致理解:

文件系统就是硬盘上管理文件的系统,Linux的正规文件系统为Ext2。Ext2文件系统为了管理磁盘,将磁盘上的数据划分成了以下几个部分:

  • Block块:Block块就是实际记录文件内容的存储块,每块有自己的块号,若文件太大时,会占用多个block块.且一个文件占据的多个块不是连续的,正是为了实现离散存储,才将存储区域分块。离散存储可以提高磁盘存储的利用率。

  • inode块:一个文件的存储可能会占多个block块,为了对这些block块进行统一控制,每个文件在磁盘上都有一个inode节点,它里边记录了文件存储的block块的位置信息,除此之外还有一些别的文件信息,例如:(1)该文件的访问模式(rwx)(2)该文件的所有者与组(owner/group)(3)该文件的大小(4)该文件创建或状态改变的时间(5)最近一次读取的时间(6)最近修改的时间

  • super block (超级块): 记录此文件系统的整体信息,包括inode/block的总量,使用量,剩余量,以及文件系统的格式与相关信息等

  • inode bitmap (inode位图): 磁盘上边inode块的个数是一定的,一个文件对应一个inode,当一个文件存入磁盘,就需要一个inode,这时候可以通过(inode bitmap)直到哪些inode使用了,哪些没有使用。

  • block bitmap(block 位图):与inode bitmap一样,磁盘上block个数是固定的,存入文件时,可以根据(inode bitmap)查看哪些block可以使用。

目录树与磁盘分区的关系:

每一块磁盘可以有很多分区,至少有一个分区是交换分区,剩下的是文件系统分区,每一个文件系统分区,都有一套自己的文件系统不同的分区文件系统可以不同。
每一个磁盘分区可以挂载到Linux目录树的目录上,根目录一到要有文件分区挂载,并且一定要先于其他目录挂载。根目录被挂载后子目录如果被单独的挂载了磁盘分区,那这个目录就有了自己文件系统。

软连接文件和硬链接文件

在Linux下面的连接文件有两种,一种是类似于Windows快捷方式功能的文件,可以让你快速连接到目标文件或目录(软连接,符号连接);另一种则是通过文件系统的inode连接来产生新文件名,而不是产生新文件,称为硬连接(hard link).

hard link:
只是在某个目录下新建一条文件名连接到某inode号码的关联记录。使用该模式连接文件时,磁盘的空间与inode数目都不会改变。其限制主要是不能跨文件系统,不能连接到目录。

symbolic link:
symbolic link就是在创建一个独立的文件,而这个文件会让数据的读取指向它连接的那个文件的文件名。会占用掉inode与block.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值