基础IO(一)
- 1.文件基础概念
- 2.C语言接口回顾
- 3.系统接口
- 4.文件系统调用
- 5.三个标准
- 6.输出缓冲区
🌟🌟hello,各位读者大大们你们好呀🌟🌟
🚀🚀系列专栏:【Linux的学习】
📝📝本篇内容:文件基础概念;C语言接口回顾;系统接口;文件系统调用;三个标准;输出缓冲区
⬆⬆⬆⬆上一篇:C++中的继承
💖💖作者简介:轩情吖,请多多指教(> •̀֊•́ ) ̖́-
1.文件基础概念
①文件=属性+内容->针对文件的操作,对内容的操作,对属性的操作
②当文件没有被操作时,一般在磁盘
③当我们对文件进行操作时,文件需要在内存(冯诺依曼体系)
④先描述,构建在内存中的文件结构体struct file(文件一些属性可以从磁盘中来)
⑤每一个被打开的文件,都要在OS内对应文件对象的struct结构体,可以将所有的struct file结构体用某种数据结构连接起来,在OS内部,对被打开的文件进行管理,转换成了对链表的增删查改,总结来说就是文件被打开,OS要为被打开的文件,创建对应的内核数据结构
struct file
{
//各种属性、连接关系
}
⑥文件分为磁盘文件和被打开的文件(内存文件)
⑦文件是OS打开的,是用户(进程)让OS打开的
⑧都是进程和被打开文件的关系:struct task_struct和struct file
2.C语言接口回顾
①w:默认写方式打开文件,如果文件不存在,就创建它,默认如果只是打开,文件内容会被自动清空,每次写入都会从最开始进行写入
②a:不会清空文件,而是每一次写入都是从文件结尾写入的(追加)
3.系统接口
①int open(const char* pathname,int flags,mode_t mode);
其中mode是权限的设置,flags是位图的使用,int是32个比特位,我们可以用一个比特位表示一个标志位,可以看下面的代码来理解一下位图的使用
1 #include <stdio.h>
2 #define ONE 0x1//00000000000000000000000000000001
3 #define TWO 0x2//00000000000000000000000000000010
4 #define FOUR 0x4//00000000000000000000000000000100
5 #define EIGHT 0x8//00000000000000000000000000001000
6 void Print(int x)
7 {
8 //充当不同行为
9 if(x&ONE)
10 {
11 printf("NOE\n");
12 }
13
14 if(x&TWO)
15 {
16 printf("TWO\n");
17 }
18 if(x&FOUR)
19 {
20 printf("FOUR\n");
21 }
22 if(x&EIGHT)
23 {
24 printf("EIGHT\n");
25 }
26 }
27 int main()
28 {
29 Print(ONE|TWO|EIGHT);
30
31
32 return 0;
33 }
O_WRONLY:只写,默认不会对原始文件内容做清空
O_CREAT:没有文件则创建文件
O_TRUNC:清空文件
O_APPEND:追加
O_RDONLY:只读
②ssize_t write(int fd,const void* buf,size_t count)
③ssize_t read(int fd, void *buf, size_t count);
代码测试:
//write的使用
#include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <stdio.h>
7 int main()
8 {
9 mode_t mode=0000;//在本次进程中,权限掩码为0000
10 int fd=open("log.txt",O_WRONLY|O_WRONLY|O_APPEND,mode);
11 if(fd==-1)
12 {
13 return -1;
14 }
15 char arr[]="我是一个文件";
16 write(fd,arr,strlen(arr));//']'\0'是C语言规定的,不是文件的规定
17 close(fd);//由于关闭文件的函数过于简单,我就不拿出来讲了
18
19
20
21
22 return 0;
23 }
//read的使用
#include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <stdio.h>
7 int main()
8 {
9 int fd=open("log.txt",O_RDONLY);
10 if(fd==-1)
11 {
12 return -1;
13 }
14 char arr[1024];
15 int ret=read(fd,arr,1023);//']'\0'是C语言规定的,不是文件的规定
16 if(ret>0)//read函数的返回值表示的是读取了多少字节
17 arr[ret]='\0';
18 close(fd);//由于关闭文件的函数过于简单,我就不拿出来讲了
19 printf("%s",arr);
20
21
22
23 return 0;
24 }
使用系统接口进行IO的时候一定要注意‘\0’的问题,这里我们无法做到按行读取,我们是整体读取的,其实本质上库函数底层也会调用系统调用
4.文件系统调用
任何一个进程,在启动的时候,默认会打开当前进程的三个文件:
现在来解释一下open的返回值:文件描述符,本质上其实是数组下标
我们使用OS的本质:都是通过进程的方式进行OS的访问的
在对于文件访问当中也会包含对于显示器,键盘等硬件的访问,而硬件访问就会比较复杂,因此在此类文件的struct file中会有对应的函数指针
FILE是一个结构体,C语言提供的
操作系统层面,我们必须要访问fd,我们才能找到文件
任何语言层访问外设或者文件,必须经历OS
在FILE里必定封装了fd
5.三个标准
进程中,文件描述符的分配规则:在文件描述符表中,最小的,没有被使用的数组元素,分配给新文件
其实本质上重定向的原理就是在上层无法感知的情况下,在OS内部,更改进程对应的文件描述符表中特定下标的指向!!!
其中stdout,cout本质他们内部的fd是1号文件描述符,他们都是向1号文件描述符对应的文件打印
stderr,cerr本质他们内部的fd是2号文件描述符,他们都是向2号文件描述符对应的文件打印
因此,输出重定向,只改的是1号对应的指向
代码验证:
新的使用方法:
其中那行命令意思是把1号文件描述符所对应的指针变为log.txt的文件结构体指针,而2号文件描述符对应的指针同理
这是另一种用法,把一号文件描述符所对应的指针变为log.txt的文件结构体指针,2号文件描述符所对应的指针变为err.txt文件结构体指针
当我们真正要改变进程对应描述符表中特定下标的指向时,我们也可以使用函数
它的用法需要特别注意,不要搞反了,前半句的英语大致意思是,使新的fd变成旧的fd的拷贝,也就是说会有一个fd被覆盖,那我们可以明白newfd应该是你想要被修改的fd,实际上这个函数的本质就是数组对应下标的内容进行拷贝
6.输出缓冲区
C库会结合一定的刷新策略,将我们缓冲区中的数据写入给OS( write(FILE->fd,xxxxx) )
①无缓冲区 ②行缓冲区 ③全缓冲区
a.显示器采用的刷新策略:行缓冲。普通文件采用的刷新策略:全缓冲
b.缓冲区节省调用者的时间
c.在你进行fopen打开文件的时候,你会得到FILE结构体指针,缓冲区就在这个结构体中
d.当我们关闭文件的时候,C要帮我们进行冲刷缓冲区
e.在struct file里也有一个文件缓冲区,OS有自己的刷新策略,是不可见的且极其复杂
f.一共其实至少会有三次数据拷贝,从用户到语言,再从语言到struct file,再从struct file到磁盘
其中强制刷新struct file的缓冲区可以使用fsync函数
🌸🌸基础IO(一)的知识大概就讲到这里啦,博主后续会继续更新更多Linux的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪