什么是文件
a.文件=文件内容+文件属性
对文件的操作
1.对文件内容
2.对文件属性
b.访问文件之前,必须先打开文件,打开文件需要文件得地址,


看下面这个案例,为什么这里我们没有文件的路径,他直接在当前路径下新建了一个文件呢???


其实是当这个可执行程序运行起来的时候已然变成了一个进程,而一个进程是会动态维护自己的cwd,当你没有给他路径时,他就会默认在当前的工作路径中创建文件了
c.打开文件实际上是在把文件加载到内存中,进程要访问什么,就把什么加载到内存,让cpu访问内存的文件
d.linux存在大量的文件,文件分为,打开和未打开的文件.打开的在内存,未打开在磁盘
e.linux中,可以存在很多被打开的文件,操作系统要对那么多的文件进行管理,要先描述在组织
一些基础文件操作
fopen


fopen里面的mode有众多选项
基础 6 种模式
w模式
没有文件,就在当前目录下面新增文件


存在文件,就将文件内容清空


和echo的输出重定向很像

根据写的这个特征我们可以写一个小demo实现清空文件内容的文件


a模式
追加只写;不存在则创建;


不存在则创建;

和echo的追加很相似

如何理解
fputs

- 参数 1:要输出的字符串
- 参数 2:必须是文件指针 FILE*
我们向log.txt文件里面写入


fgets

fgets


因为读到换行符就停止所以之会打印一行

linux进行文件操作
open

1. 参数说明
- O_RDONLY:只读
- O_WRONLY:只写
- O_RDWR:读写
常用附加标志:
- O_CREAT:文件不存在则创建;使用该标志时,必须带上第三个参数 mode
- O_TRUNC:若文件存在且以写方式打开,清空文件内容
- O_APPEND:写操作追加到文件末尾
- O_NONBLOCK:非阻塞打开
在调用之前我们来看这个例子理解一下

这段代码是位标记(Bit Flag / 位掩码)经典示例,open 函数 flags 参数原理
(1)| 按位或:组合多个标记
(2)& 按位与:检测某个标记是否存在
例子
我们这里打开文件只给系统的写权限,但是发现,原来文件里面没有,他并不会为我们新创建一个,反而是打开失败,系统本身的文件操作和c语言是不同的


这里我们加上了创建的操作,事实上却是也为我们创建了,但是我们发现权限是乱的


此时需要我们给出新建文件的权限
对于第二个open->mode,是解决如果文件不存在要怎么创建问题的解决也就是文件权限
我们给的是666,为什么文件却是664


这是因为umask,权限掩码
我们在c代码里面设置umask变为0即可完成刚刚的要求





文件描述符是3,为什么是三呢???
稍后回答
2. 返回值
- 成功:返回非负整数文件描述符 fd
- 失败:返回 -1,同时设置全局变量 errno
close关闭文件
write

我们发现系统的write是覆盖式的写不会对原内容进行清空再写



想要写入时,将原来的内容清空就加上 O_TRYNC


追加式写入O_APPEND


c对系统的封装理解

我们再来看下面一个现象


为什么没有0,1,2???
因为系统已经占用了前三个文件描述符
如果说0,1,2被占用,那么我们用write写应该是可以实现的

| 0 | 标准输入 | stdin | 读取键盘输入 |
| 1 | 标准输出 | stdout | 输出到终端屏幕 |
| 2 | 标准错误 | stderr | 错误信息输出到终端 |
向屏幕进行输出


当然c封装的函数也可以实现这一点


也就是stdin,stdout,stderr结构体内存在文件描述符


语言层面的文件操作vs系统的文件操作
文件描述符的本质???


读写文件时,内核配合页缓冲区:

每一个进程(struct task_struct)内部包含一个 struct files_struct, 里面维护一张指针数组:struct file *fd_array[],这就是文件描述符表。
- 数组下标 = 文件描述符 fd(0、1、2、3、4……)
- 数组元素是指针,指向内存中对应的struct file
所以呢,文件描述符就是数组下标,操作系统也只认文件标识符
为什么c要对系统进行封装,理解IO
系统调用(open/read/write/close)是操作系统提供的原生接口,运行陷入内核态;C 标准库在此之上封装出 FILE* 系列 IO 函数,本质就是用户态缓冲层封装。
1. 跨平台可移植
- open/read/write 是 Linux/Unix 特有系统调用,Windows 没有这套 API(Windows 用CreateFile)。
- C 标准库 fopen/fwrite 语言标准规定,编译器在不同系统底层自动适配对应系统接口。一份代码不用修改,就能在 Linux、Windows、macOS 编译运行。
- 可以让更多人使用
2. 引入用户态缓冲区,减少系统调用次数
系统调用存在内核态 / 用户态切换开销,频繁调用read/write性能很差。
- 标准库FILE内部自带一块用户缓冲区;
- fprintf/fputs先把数据写到缓冲区,缓冲区填满后,才一次性调用write系统调用刷入内核;
- 大幅减少系统调用次数,提升 IO 效率。



