🤲文件
00 min
2024-11-18
2024-11-23
type
status
summary
date
slug
tags
category
icon
password
💫
本篇文章主要讲讲
1.c文件io相关操作
2.文件相关系统调用接口
3.文件描述符,什么是重定向
4.对比fd和file,理解系统调用和库函数的关系
5.文件系统中inode的概念
6.认识软硬链接,对比区别
7.认识动静态库,结合gcc选项制作动静态库
先说一说文件的一些共识原理:
1.文件=内容+属性
2.文件分为打开的文件和没打开的文件
3.本质上打开文件的是进程—-是研究进程和文件的关系
4.没打开的文件在磁盘上放着
文件要被打开要先被加载到内存中(冯诺依曼体系)
一个进程能打开多个文件——操作系统内部一定存在大量被打开的文件——所以需要操作系统进行管理:
所以每一个被打开的文件都必须要有自己的文件打开对象里面包含了很多文件属性——struct XXX{文件属性:}
 
 

1.c语言文件操作接口

1.fopen,打开文件函数
包含在stdio头文件中
notion image
这是fopen函数的一些基本操作:
notion image
打开文件默认是在当前路径下创建一个文件,当然也可以更改cwd来使得创建的文件是在其他路径下创建
使用chdir函数
chdir(”/home/PytC”);
这样再使用fopen就能再这个路径下创建文件了
fopen的w选项其实就是如果文件不存在就创建一个文件并且写入,如果文件存在那就先清空处理再进行写入
与bash中的echo “ “ > log.txt
一样的
a(appending)选项:就是在文件结尾后追加内容
 
在C语言中默认会打开三个输入输出流:
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
对应c++中的
cin,cout,cer
其实就是显示器,键盘对应的文件流
文件其实是在此磁盘上的,磁盘是外部设备,访问文件就是访问硬件
用户
程序 < - std lib
系统调用
操作系统
硬件驱动
硬件
几乎所有的库只要是访问硬件设备,必定要封装系统调用
 
 

2.open函数

notion image
这是man手册上的open函数, 一般在linux下都用带三个参数的open函数,其中flags是下面定义的宏,使用O_APPEND|O_ASYNC这样的方式
第三个参数是设置文件的权限(如果文件不存在)但是这个权限是经过umask过滤过的权限如果想要直接改名变的话,需要在代码中使用函数umask改变umask值然后再改变
在C语言中,open函数用于打开一个文件,并返回一个文件描述符(file descriptor),这个描述符用于后续的文件操作(如读、写等)。open函数定义在头文件<fcntl.h>中。虽然open函数是POSIX标准的一部分,并且在类Unix系统(如Linux和macOS)上广泛使用,但它并不是C语言标准库的一部分,因此在Windows上不可用。
以下是open函数的基本用法和参数说明:

参数

  1. pathname:要打开文件的路径,是一个以null结尾的字符串。
  1. flags:用于指定打开文件的模式,可以是以下一个或多个标志的组合(使用按位或运算符|):
      • O_RDONLY:只读打开。
      • O_WRONLY:只写打开。
      • O_RDWR:读写打开。
      • O_CREAT:如果文件不存在,则创建文件。
      • O_TRUNC:如果文件已存在且为写打开,则将其长度截断为0。
      • O_APPEND:以追加方式打开文件(写操作将数据追加到文件末尾)。
      • O_EXCL:与O_CREAT一起使用时,如果文件已存在,则open调用失败。
      • O_NONBLOCK:对于设备文件,以非阻塞方式打开。
      • O_SYNC:写操作同步进行。
      • O_DSYNC:类似O_SYNC,但只同步数据,不同步元数据。
  1. mode:指定文件的权限(当flags中包含O_CREAT时)。这个参数是一个位掩码,由以下常量组合而成(使用按位或运算符|):
      • S_IRUSR:用户读权限。
      • S_IWUSR:用户写权限。
      • S_IXUSR:用户执行权限。
      • S_IRGRP:组读权限。
      • S_IWGRP:组写权限。
      • S_IXGRP:组执行权限。
      • S_IROTH:其他用户读权限。
      • S_IWOTH:其他用户写权限。
      • S_IXOTH:其他用户执行权限。

返回值

  • 成功时,open返回一个非负的文件描述符。
  • 失败时,返回-1,并设置errno以指示错误类型。

示例

以下是一个简单的示例,展示如何使用open函数打开一个文件:
在这个示例中,我们尝试以只读模式打开名为example.txt的文件。如果文件打开失败,perror函数将打印出错误信息。最后,我们使用close函数关闭文件描述符。

注意事项

  • 使用open函数时,请确保在文件操作完成后关闭文件描述符,以避免资源泄露。
  • 在处理文件权限时,应谨慎选择适当的权限标志,以确保文件的安全性。
  • 在处理错误时,应检查errno以获取更详细的错误信息。
这里mode这里也定义了宏所以其实如果使用宏也不需要更改umask了
这里可有发现fopen的返回值是一个FILE*类型,但是open函数的返回值是一个int类型的值
 

3.write函数

在C语言中,write 函数通常用于向文件描述符(file descriptor)指向的文件写入数据。这个函数是POSIX标准的一部分,在类Unix系统(如Linux、macOS等)中广泛使用,但在Windows系统中并不直接可用(Windows有类似的函数,如WriteFile,但其用法和参数有所不同)。

函数原型

参数

  • fd:文件描述符,是一个整数,通常由openpipesocket等系统调用返回。
  • buf:指向要写入数据的缓冲区的指针。
  • count:要写入的字节数。

返回值

  • 成功时,返回写入的字节数。这个值可能小于count,如果发生这种情况,通常是因为磁盘已满或达到了文件大小限制。
  • 失败时,返回1,并设置errno以指示错误类型。

示例代码

以下是一个使用write函数向文件写入数据的示例:

注意事项

  1. 文件描述符write函数使用文件描述符而不是FILE *类型的指针。文件描述符是底层系统调用的接口,而FILE *是C标准库提供的更高层次的接口。
  1. 错误处理:始终检查write函数的返回值,以确保数据正确写入。如果返回值小于要写入的字节数,可能需要采取额外的措施(如重试写入)。
  1. 关闭文件描述符:使用close函数关闭文件描述符,以释放系统资源。
  1. 缓冲区write函数可能不保证写入完整的数据块。如果需要确保数据完整写入,可能需要循环调用write直到所有数据都写入。
  1. 权限:在open函数中设置正确的权限标志,以确保文件以期望的模式打开。例如,O_WRONLY表示只写模式,O_CREAT表示如果文件不存在则创建它,O_TRUNC表示如果文件已存在则清空其内容。
notion image
这是一个file struct上面基本的一些属性(file struct在上文讲文件共识原理的时候讲到过)
其实还有一个文件内核缓冲区信息
在操作系统层面上,一个进程能打开多个文件就是创建多个file struct然后操作系统将所有的file struct通过双链表链接起来就能通过简单的链表增删查改来管理文件操作
🚨
所以打开文件的本质是什么呢?
在linux的task struct中里面有一个属性叫做struct struct_file files这个指针指向了一个结构体,这个结构体呢里面又有一个数组,里面存放的就是存放文件信息的file
struct,然后file struct又通过双链表链接在一起
所以这里就大揭秘了为什么说open函数的返回值是一个int类型的一个值,其实就是struct_file struct里面的数组的下标
在这里我做了一个实验:
将我open的那个文件的fd打印出来了,发现是3,那前面还有三个文件是什么呢?(我自己打开的只有一个文件)其实就是我们之前讲到过的stdin,stdout,stderr这三个
 
 
💥
作者反思和留言:为什么说linux中一切皆文件,他到底是怎么做到将冯诺依曼中的外设结构也通过文件管理起来的呢?
虚拟文件系统:
其实所有的外设文件都能通过open来打开,这是为什么呢,其实就是open能调用每一个外设自己的structfile,而每一个structfile里面都封装了一个struct operator_open
*oo而里面所对应的就是每一个外设文件自己封装的打开读写方式,(当然所有外设的打开读写方式都不同)这样通过虚拟文件系统将所有的操作都通过文件来管理,文件又通过进程来打开,这样就能做到一切皆文件的效果
上一篇
基础IO
下一篇
数据结构-搜索二叉树