Linux下的文件IO操作

举报
绝活蛋炒饭 发表于 2024/12/15 19:20:32 2024/12/15
【摘要】 本文主要讲解了,在Linux环境下一些C语言提供的文件接口,以及C语言文件接口底层封装的由操作系统Linux提供的系统调用接口

 1.前导

1.1文件知识

  1. 文件 = 文件内容 + 文件属性
  2. 访问文件的时候,都得先打开文件,修改文件都是通过代码去修改的,所以首先就得先把文件先加载到内存当中。
  3. 谁在打开文件?------ 进程在打开文件
  4. 一段时间内,OS当中肯定有很多进程,也肯定有很多被打开的文件,那么OS该怎么去取管理这些文件呢?----- 先描述,后组织
  5. 进程和文件的关系:struct task_struct 和 struct XXX
  6. 系统是不是把所有的文件都打开了?-------- 那一定是没有的,没有打开的文件都存储在磁盘当中。


 1.2对比一下文件操作和重定向


1.2.1输入重定向

编辑

 fopen以"w"方式打开:如果文件不存在,先会创建一个文件 / 如果文件存在,先会清空文件内容,然后再从头进行写入操作。

 编辑

 编辑

这上面的操作是不是和输入重定向很相似。 

编辑



1.2.2追加重定向 

编辑

fopen以"a"方式打开:本质也是写入,如果文件不存在,先会创建一个文件,然后进行写入 / 如果文件存在,会在文件原有内容的末尾处追加写入。 

编辑

上面的操作是不是和追加重定向很像。 

 编辑



1.3当前路径

当文件不存在的时候,在当前路径下创建一个,那么进程又是如何知道当前文件的呢?--- 当然是记录在进程PCB当中的。

可以 ls /proc/pid 查看

补充函数:

int chdir(const char* path);

编辑


1.4stdin stdout stderr

编辑

三大标准输入,OS都默认帮你打开了。 

一、标准输入流stdin

  • 定义:标准输入是程序可以从中读取输入数据的位置,它默认指向键盘,但也可以被重定向为文件或者其他输入设备。
  • 作用:允许用户通过键盘或者其他输入设备向用户提供数据,也可以从文件中读取数据。
  • 文件描述符:在linux系统中,stdin文件描述符为0。

二、标准输出流stdout

  • 定义:标准输出是程序用于发送其输出数据的位置,它默认指向终端屏幕,但也可以被重定向为文件或者其他输出设备。
  • 作用:stdout用于显示程序的正常输出,包括结果、状态信息、其他非错误信息。
  • 文件描述符:在linux系统中,stdout文件描述符为1。
  • 缓冲:stdout通常是行缓冲的,意味着输出会先存储在缓冲区中,直到遇到换行符或者缓冲区满才会刷新到目的地。

三、标准错误输出流stderr

  • 定义:标准错误是程序用于发送错误、异常信息的位置,它默认指向终端屏幕,但也可以被重定向为文件或者其他输出设备。
  • 作用:用于输出错误信息,以便用户能够识别并解决问题。
  • 文件描述符:在linux系统中,stderr文件描述符为2。
  • 缓冲:stderr是非缓冲的,意味着错误信息会被立即发送到目的地,以便用户能够尽快的看到它。


2.文件操作的系统调用接口

访问文件不仅有C语言上的文件接口,OS必须提供对应的访问文件的系统调用接口。即:C标准库中的文件IO接口,底层一定封装了系统调用接口。 


2.1.open()打开文件 

编辑

  1. 功能:打开一个文件,如果文件不存在则创建文件。
  2. 返回值:打开成功,返回非负整数,即:文件描述符(用于后续文件操作);如果失败,返回-1,并设置errno以指示错误的原因。
fopen:"r"-> open:O_RDONLY
fopen:"w"-> open:O_WRONLY|O_CREAT|O_TRUNC
fopen:"a"-> open:O_WRONLY|O_CREAT|O_APPEND
  • 底层调用Open,传递不同的参数,在上层表现为fopen以r、w、a方式打开文件。即:不同的fopen风格,代表着open传递了不同的选项。


2.1.2.flags参数

O_WRONLY  O_CREAT  O_APPEND

这三个大写的字符串,还是这种格式的,我们很容易就联想到宏。

  1. 参数flag:标记位,用于指定文件的打开模式(只读、只写、追加等)和其他选项(创建文件、截断文件等)。
//宏
#define O_RDONLY    0x00000000  // 只读模式  16进制
#define O_WRONLY    0x00000001  // 只写模式
#define O_RDWR      0x00000002  // 读写模式
//以上三个常量,必须且只能指定一个
#define O_CREAT     0x00000100  // 如果文件不存在,则创建文件
#define O_TRUNC     0x00000200  // 如果文件存在,则截断为零长度
#define O_APPEND    0x00000400  // 每次写入时追加到文件末尾
#define O_EXCL      0x00000800  // 如果文件已存在,则打开失败
#define O_NONBLOCK  0x00001000  // 非阻塞模式 
  1. flag参数是一个整数,每个比特位代表一个标记位。通过位操作(|、&),可以一次性向函数传递多个标记位。
  2. 在编程中,涉及需要向函数传递多个布尔选项(标记位)时,使用单个整数(int,32位),并通过位操作来设置和检查这些选项,这种方法被称为"位图",是一种非常高效和节省资源的方法。
  3. 使用宏定义,来表示各种标记位,每个宏定义只有一位为1(每个宏中为1的位是错开的),其余位全为0。在这个整数中为1的位,用来表示某个特定的选项是否被设置。多个宏通过位操作(|按位或)组合,一次性地向函数传递多个标记位。即:通过位图的方式,传递多个标记位。

编辑

编辑


2.1.3.mode参数

文件不存在的时候,要创建文件时,要用mode参数设置权限,

如果没有设置权限就会出现下面的情况(权限乱码) 

编辑

编辑

现在让我们来看看mode参数如何传参。

编辑

编辑

为什么我们用mode设置的文件权限是666,但是文件权限最终确实664呢?

(这是因为权限掩码的存在)


原因:默认(最终)权限计算公式 = 起始权限 & (~umask值) , 本质是从起始权限中去掉在umask权限中出现的权限,如果在起始权限中某权限位不存在,但umask中该权限位存在,该权限位的结果为0,"去掉"不是删除。

2.1.3 umask()函数

编辑

  • 功能:设置文件的权限掩码。

umask函数只会影响调用它的进程所创建的权限掩码,而不会对父进程或其他进程的权限掩码产生影响。

编辑

编辑


 2.2.write()向文件写入

编辑

  1. 功能:向打开的文件中写入数据。
  2. 参数:fd,表示写入数据的文件或设备; buf,指向要写入数据的缓冲区的指针; count,要写入的字节数。
  3. 如果buf为const char*类型,strlen(buff),不需要在后面+1,即:不需要把字符串结束标志\0写入进去。因为c语言字符串以\0结尾,\0不是字符串内容,而是作为字符串结束标记,与文件无关,若把\0写入,则会造成乱码。
  4. 返回值:如果成功,返回实际写入的字节数。如果出错,则返回-1,并设置errno以指示错误。


2.3.read()从文件读取

 编辑

  1. 功能:从打开的文件中读取数据。

  2. 参数:fd,表示要读取数据的文件或设备; buf,指向读取数据的缓冲区的指针; count,要读取的最大字节数。

  3. 返回值:如果成功,返回实际读取的字节数。如果出错,则返回-1,并设置errno以指示错误。如果到达文件末尾(EOF),则返回0


2.4.close()

编辑

  1. 功能:关闭一个打开的文件描述符。

  2. 返回值:如果成功,返回0。如果失败,返回-1,并设置errno以指示错误。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。