Linux学习笔记:基础IO篇
3.当执行下面的代码创建子进程后,正常执行./属于向显示器输出(刷新方式为行刷新),也就是说当遇到“\n”时,打印内容就已经刷新,属于正常打印;但执行重定向命令后,刷新方式的改变,导致库函数的内容不能及时刷新,程序遇到fork()时,会发生。引入缓冲区的目的:减少磁盘的读写次数,再加上计算机对缓冲区的操作远远快于对磁盘的操作。在我们的电脑中,有显卡、网卡、键盘、鼠标、麦克风等外设,它们都是。Lin
目录
1.初步理解"文件"
文件在磁盘当中存储;
文件 = 内容 + 属性;(文件当中所写的内容以及在磁盘当中存储的各种属性)

正确理解:
1.文件大小为0,当然也要在磁盘中占据空间;
2.磁盘属于外设(既属于输出设备,也属于输入设备);
3.对文件的操作,本质是:进程对文件的操作;
2.从C语言库函数中理解文件操作
进程一旦被执行,编译器默认打开的文件:
#include <stdio.h>
extern FILE* stdin;//标准输入
extern FILE* stdout;//标准输出
extern FILE* stderr;//标准错误
打开文件的方式:
r - 打开文件只读,并将输入流设置在文件开头;
r+ - 打开文件进行读写, 并将输入流设置在文件开头;
w/w+ - 将文件内容清空(若未创建,则创建),并进行文件写入;
a/a+ - (若文件为创建,则创建), 并对文件内容进行追加写入;
3.从系统调用角度理解文件操作
了解系统层面的文件打开操作:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open (const char* pathname,int flags);//通常进行只读操作;
int open (const char* pathname,int flags,mode_t mode);
//权限位 涉及位图的思想
//O_RDONLY 进行只读
//O_WRONLY 进行只写入
//O_CREAT 进行文件的创建
//O_TRUNC 进行文件内容的清空
总结:基本和C语言库函数的文件操作函数参数差不多;但有权限位和标志位的;
C语言中的众多文件打开方式,其实是标志位的组合;
引出下文:open的返回值其实是文件标识符;
4.理解文件标识符
默认我们通过open打开的第一个文件的文件标识符为:3
其中系统中默认打开的stdin、stdout、stderr分别为0、1、2;
这里可做个实验理解;
我们这里将stdout的文件标识符1通过close关掉,创建文件后将msg字符串向1中写入;
int main()
9 {
10 close(1);
11 int fd=open("log.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);
12 if(fd<0)
13 {
14 perror("open");
15 exit(1);
16 }
17
18 const char* msg="abcd";
19 write(1,msg,strlen(msg));
20 return 0;
}
实验结果:
所以我们也可以理解文件标识符的分配原则:从低到高分配;
重定向:通过改变文件标识符来完成文件操作;
> 输出重定向
>> 追加输出重定向
< 输入重定向
使用dup2系统调用完成重定向;
int main()
9 {
10 //close(1);
11 int fd=open("log.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);
12 dup2(fd,1);
13 if(fd<0)
14 {
15 perror("open");
16 exit(1);
17 }
18
19 const char* msg="abcd";
20 write(fd,msg,strlen(msg));
21 return 0;
}
![]()
5.理解“一切皆文件”
Linux操作系统在管理文件的过程中,其实是文件的描述为结构体struct_file被管理,
而在struct_file中有很多的成员;
f_count //文件的引用计数,有进程打开它就++,一旦为0则释放空间,提高系统效率;
const struct file_operations * f_op;//文件操作
在我们的电脑中,有显卡、网卡、键盘、鼠标、麦克风等外设,它们都是通过驱动程序调用系统调用实现对应文件结构体的修改;
*f_op中有很多的系统调用函数指针;
实现外设对应系统中的文件,被系统管理;

6.缓冲区
引入缓冲区的目的:减少磁盘的读写次数,再加上计算机对缓冲区的操作远远快于对磁盘的操作。所以用缓冲区可大大提高计算机的运行速度;
缓冲类型:
全缓冲:要求填满整个缓冲区后才刷新,对于磁盘文件的操作通常使用全缓冲的方式访问;
行缓冲:在输入和输出中遇到换行符\n时,才会刷新到内核级缓冲区采用系统调用进行读写操作(例如操作流为stdout或stdin时,采用行缓冲方式);
无缓冲:立即刷新(例如标准出错流stderr通常不带缓冲区,使得错误信息立即刷新出来);
缓冲区刷新的条件:
1.缓冲区满时;
2.执行flush语句;
3.进程结束;
实践例子:
1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 #include <unistd.h>
5
6 int main()
7 {
8 //库函数
9 printf("hello printf!\n");
10 fprintf(stdout,"hello fprintf\n");
11 const char* s="hello fwrite\n";
12 fwrite(s,strlen(s),1,stdout);
13
14 //系统调用
15 const char* ss="hello write!\n";
16 write(1,ss,strlen(ss));
17
18 return 0;
19 }
1.当程序执行时,打印信息进过行刷新,刷新到内核级缓冲区,进程结束后文件级缓冲区上的数据刷新到内核级缓冲区,同write所打印内容一同被系统调用打印在显示器上;

2.当程序被执行时,因为执行的是重定向命令,所以程序缓冲区刷新方式从行刷新变成了满刷新,进程结束后才被刷新到内核级缓冲区,所以库函数的打印在系统调用函数的后面;
![]()

3.当执行下面的代码创建子进程后,正常执行./属于向显示器输出(刷新方式为行刷新),也就是说当遇到“\n”时,打印内容就已经刷新,属于正常打印;
1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 #include <unistd.h>
5
6 int main()
7 {
8 //库函数
9 printf("hello printf!\n");
10 fprintf(stdout,"hello fprintf\n");
11 const char* s="hello fwrite\n";
12 fwrite(s,strlen(s),1,stdout);
13
14 //系统调用
15 const char* ss="hello write!\n";
16 write(1,ss,strlen(ss));
17
fork();
18 return 0;
19 }

但执行重定向命令后,刷新方式的改变,导致库函数的内容不能及时刷新,程序遇到fork()时,会发生进程间数据的写时拷贝,同时也将struct_task中的struct_file中的文件缓冲区中的内容拷贝,进程都结束后刷新,所以log.txt中的内容,库函数打印出现两次,系统调用函数只出现一次;
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)