Linux文件IO

一.C语言文件操作

fopen

fopen用于打开文件,包含在头文件<stdio.h>中,函数原型如下:

FILE* fopen(const char *pathname, const char *mode);

pathname:打开文件路径,可以是绝对路径或相对路径
mode:打开文件的模式
打开文件的最常用模式为:

"r":只读,若文件不存在则报错
"w":只写,若文件不存在则创建,打开时清空文件原有内容
"a":只写,若文件不存在则创建,打开时从文件末尾追加

该函数会返回一个FILE*的指针,C语言中,通过操作这个FILE*来控制文件的IO。当我们打开文件时,每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息。

这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名FILE。我们可以通过操纵这个FILE类型的结构体,来操控文件。大部分情况下,我们可以得到一个指向该结构体的指针FILE*,即文件指针,后续通过文件指针来操控文件。

示例,我们有一个test1.c文件,文件里面以"w"的方式打开一个文件,我们将其运行。

我们发现在当前目录下多了一个file111111111111111111111.txt文件。fopen打开的文件如果不存在则会在当前路径下创建一个该文件,那么同理如果更改了当前路径的工作目录,就可以把文件建立到新的路径里。

chdir

chdir用于改变进程工作路径,包含在头文件<unistd.h>中,函数原型如下:

int chdir(const char* path);

我们在test1.c文件中使用该函数,将其放在 /home/lbk 目录下。

fwrite

fwrite用于将一段字符串写入文件,包含在头文件<stdio.h>中,函数原型如下:

size_t fwrite(const void* ptr,size_t size,size_t nmemb,FILE* stream);

ptr: 字符串,size:字符串的长度,nmemb:字符串一个字符的大小,单位为字节,stream:要写入的文件

示例

二.系统调用接口

C语⾔程序在启动的时候,默认打开了3个流:

stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊。
stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯。
stderr - 标准错误流,⼤多数环境中输出到显⽰器界⾯。

这三个流也是通过文件指针来操作的。

众所周知文件是被存在磁盘里的,而用户想要操作硬件就必须经过操作系统,操作系统就必须提供对应的接口来供用户使用,那么可以得出c语言的标准文件例如printf之类也必定封装了系统调用接口,下面来介绍一些系统调用接口。

open接口

open用于打开文件,需要头文件<sys/types.h>、<sys/stat.h>、<fcntl.h>,函数原型如下:

1. int open(const char *pathname, int flags);
2. int open(const char *pathname, int flags, mode_t mode);
  • pathname:打开文件的路径
  • flags:打开文件的模式

这个模式和fopen的相似,比如控制读、写、追加等,以下选项:

选项 功能
O_RDONLY 以只读方式打开
O_WRONLY 以只写方式打开
O_RDWR 以读写方式打开

O_CREAT

若打开时不存在,则创建文件,需要使用mode选项来指定新文件的权限
O_APPEND 以追加写的方
O_TRUNC 清空文件,然后再开始写

mode:用于控制文件的初始权限,最终权限还要经过umask过滤

open会返回一个int类型的值,这个值就是文件描述符fd,后续通过fd操控对应文件。

第一个接口一般用来打开已经存在的文件,第二个一般用来创建新文件。

示例,在test2.c文件中

close接口

close用于关闭文件,需要头文件<unistd.h>,函数原型如下:

int close(int fd);

直接传入对应文件的文件描述符fd即可。

write接口


write用于向文件写入,需要头文件<unistd.h>,函数原型如下:

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


fd:被写入文件的fd,buf:指向被写入的字符串,count:写入字符的个数。

示例,在test2.c文件中使用write

然后我们再修改buf,改为bbbb,然后再次运行该程序并查看log.txt文件中的内容。

我们发现以O_WRONLY模式对文件进行写入时,不会先把文件内容清空,而是直接从头开始覆盖。

故而我们可以有两种组合:

  • O_WRONLY | O_CREAT | O_TRUNC:以只读形式打开,如果文件不存在就创建,打开时清空文件所有内容
  • O_WRONLY | O_CREAT | O_APPEND:以只读形式打开,如果文件不存在就创建,打开时从文件的结尾开始追加

O_WRONLY | O_CREAT | O_TRUNC模式打开,就是fopen的以"w"形式打开;O_WRONLY | O_CREAT | O_APPEND就是fopen的以"a"形式打开。由于fopen是用户操作接口,而open是系统调用接口,其实fopen就是对open的封装。

read接口

read用于读取文件,需要头文件<unistd.h>,函数原型如下:

ssize_t read(int fd, void* buf, size_t count);
  • fd:目标文件的文件描述符fd
  • buf:将文件内容读取到buf指向的空间中
  • count:最多读取count个字节的内容
  • 返回值:

  • < 0:读取发送错误
    = 0:读取到文件末尾
    > 0:读取成功,返回读取到的字符个数

    示例,在test3.c文件中

  • file.txt的内容为字符串hello Linux\n,一开始通过open以只读形式打开文件后,先通过read(fd, buf, 50)把内容存储到数组buf中,并把返回值交给ret1。随后输出ret1和buf。读取完file.txt后,再读取了一次read(fd, buf, 50),这次读取的结果交给ret2,并输出ret2,第一次read返回值为12,也就是字符串hello Linux\n中有12个字符,随后buf中也正常输出了hello Linux\n。而第二次read,由于上一次读取时已经遇到文件末尾了,所以本次读取返回0。

内存文件管理

1.文件=属性+内容。
2.文件分为打开文件和未打开文件。
3.打开的文件:进程打开。
4.未打开的文件:在磁盘里存放着。
5.文件打开,必须先被加载到内存。
6.一个进程可以打开多个文件,那么操作系统就必须先描述再组织的对文件进行管理。

struct file


想要打开一个文件,文件是要从磁盘中提取的,要访问硬件设备,而访问硬件设备是操作系统才有的权力,因此访问文件毫无疑问是要经过操作系统的。在操作系统中,会使用一个叫做file的结构体来描述一个被打开的文件,并把这个文件的数据从磁盘加载到内存中。struct file 是一个非常重要的数据结构,它表示一个打开的文件,并且提供了访问和操作文件的接口。

loff_t f_pos: 这个成员保存了文件当前的读写位置。许多文件操作函数都会根据这个位置来读写数据。

unsigned int f_flags: 这个成员保存了打开文件时设置的标志,如 O_RDONLY, O_WRONLY, O_APPEND 等。这些标志决定了对文件的访问权限和行为。

struct fown_struct f_owner: 这个成员保存了文件所有者的信息,包括进程 ID 和用户 ID。它用于实现文件的所有权和权限检查。

struct file_operations *f_op: 这个成员指向一个 struct file_operations 类型的结构体,它定义了各种文件操作函数,如 read(), write(), open(), release() 等。这些函数由文件系统或驱动程序实现,用于处理对文件的各种操作。在Linux中,一切皆文件,不论是显示器,键盘,磁盘,内存,一切软硬件都是通过文件来控制的,因此每个文件的写入与读取方式很有可能不同,但是我们又要用read,write这样的接口来对文件统一的读写。于是在struct file中,用一个f_op成员指向一个结构体struct file_operations,在对应结构体内部,存放着指向各种函数指针。


struct files_struct


刚刚的结构体struct file是用于描述具体的被打开的文件的,而操作系统想要对所有被打开的文件进行统一的管理,就需要再把所有的struct file组织起来,这个组织struct file的结构体,就是struct files_struct 。

fd_array就是灰色的数组,每个数组的元素都是一个指向struct file的指针,struct file对应一个打开的文件。操作系统有一个全局的struct files_struct,用于统一管理所有进程打开的文件。每个进程也要打开多个文件,那么每个进程又要如何管理自己打开的文件呢?每个进程也要通过struct files_struct来管理多个struct file.

在进程PCBtask_struct中,有这样一个成员:

/* open file information */
	struct files_struct *files;

也就是说,每个进程也要维护一个struct files_struct,来控制自己打开的文件。

左侧是被操作系统全局管理的struct files_struct,整个系统中所有被打开的文件都要被这个结构体管理。而右侧是每个进程自己打开维护的struct files_struct,分别管理自己打开的文件。当多个进程打开同一个文件,那么它们的fd_array里面的元素,会指向同一个struct file。

文件描述符 fd

文件描述符fd,就是fd_array的数组下标,现在我们创建一大批文件,然后看看它们的fd的值。

执行./test4后,确实多出了五个文件,但是其fd是3 4 5 6 7这样从3开始依次排列的,为什么要这样干?数组下标从0开始,那么编号0 1 2的fd去哪里了?

还记得在C语言中,会为我们默认打开三个流stdin,stdout,stderr吗?这三个流也是向文件输入输出,默认打开三个流stdin,stdout,stderr的时候,0 1 2为编号的fd就已经被占用了!C语言中,文件流是以FILE的形式被管理的,毫无疑问FILE是对Linux文件系统的封装,FILE内部一定存储了fd,否则无法通过fd来访问特定的文件。其中FILE的_fileno成员,就是fd。

相关推荐

  1. 2.Linux文件IO基础

    2024-07-18 09:10:01       41 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-18 09:10:01       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-18 09:10:01       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-18 09:10:01       58 阅读
  4. Python语言-面向对象

    2024-07-18 09:10:01       69 阅读

热门阅读

  1. 游戏中的敏感词算法初探

    2024-07-18 09:10:01       23 阅读
  2. opencv—常用函数学习_“干货“_11

    2024-07-18 09:10:01       24 阅读
  3. 云原生理解

    2024-07-18 09:10:01       24 阅读
  4. 银河麒麟部署 QtMqtt 解决 make 错误问题的教程

    2024-07-18 09:10:01       20 阅读
  5. 伪元素::before :: after的用法?

    2024-07-18 09:10:01       22 阅读
  6. C语言从头学35——struct结构

    2024-07-18 09:10:01       20 阅读
  7. 算法刷题笔记 排列数字(C++实现)

    2024-07-18 09:10:01       19 阅读
  8. Mac更新完系统出现两步报错及解决方法

    2024-07-18 09:10:01       21 阅读
  9. UNIX中sigaction和sigevent有啥区别

    2024-07-18 09:10:01       20 阅读
  10. MySQL第七次作业

    2024-07-18 09:10:01       18 阅读
  11. C语言 二叉树,一个猜动物的小游戏

    2024-07-18 09:10:01       15 阅读