目录
2.所以对文件操作就衍生为 对文件内容操作 和 对文件属性操作。
4.是不是所有文件都处于打开的状态? 不是,没有打开的文件都在磁盘上静静地存储着,等着被打开。
5.所以从系统的角度,我们可以将文件分为 打开的文件(内存文件) 和 磁盘文件。
6.通常我们打开文件,访问文件,关闭文件等相关操作是谁来做的?
8.打开文件fopen 有几个选项“r, w,r+,w+,a,a+”,我们来了解下。
初知:
1.文件是什么? 文件是 文件内容 加 文件属性
我创建一个新的文件,是不是不用占磁盘空间因为我没写内容啊,并不是的,文件属性(大小、文件名、创建时间等)也要占用磁盘空间。
2.所以对文件操作就衍生为 对文件内容操作 和 对文件属性操作。
3.打开文件这个操作,系统都做了什么?
将磁盘上的文件内容或属性加载到内存中,由CPU来进行访问。
4.是不是所有文件都处于打开的状态?
不是,没有打开的文件都在磁盘上静静地存储着,等着被打开。
5.所以从系统的角度,我们可以将文件分为 打开的文件(内存文件) 和 磁盘文件。
6.通常我们打开文件,访问文件,关闭文件等相关操作是谁来做的?
我们在C语言中调用 fread 、fwrite、fclose函数->变成一串串代码->编译为可执行程序->运行程序,只有程序跑起来了,成为进程,才会真正对文件操作。所以真正对文件操作的就是进程。
我们切实去使用C语言操作一下文件。
这就是C语言中对文件的写入操作。
7.这个文件所在的路径在哪里?
文件就在当前路径下,当前路径就是进程所在的工作路径。
我们试验一下:
在写入文件之前,获取此进程的pid,通过ls /proc/pid -l去查看进程信息。proc是内存级文件系统可以查看进程信息。
这里的cwd是,current working directory 即是当前工作目录。我们获得了当前进程的工作路径。这时我们的文件也在当前路径下。
我们用chdir修改下,当前进程所在的工作路径,看看文件最后在哪?
用ls /proc 查看 ,这时的进程的工作路径在/home/Yewei
而文件如我们所料,在/home/Yewei路径下。
8.打开文件fopen 有几个选项“r, w,r+,w+,a,a+”,我们来了解下。
r+和w+的功能都是可以读和写
w:只写,如果这个文件没有,就创建一个文件。如果这个文件有内容,就清空,从文件开头开始写入。
r:只读,不能写。
a:写入在文件的结尾,如果文件不存在,则创建该文件。
a+:读 加 写在文件结尾,如果文件不存在,则创建该文件。
系统文件接口
我们都知道c语言对文件的操作都是封装自操作系统的提供的对文件操作的接口,我们下面了解下这些接口。
1.open
这是最重要的一个接口。
第一个参数是接受文件的名字,第二个参数flags是用来接受标记位。什么是标记位,我的理解是这个函数有多个选项执行不同的功能,传参数来选择执行哪个功能。不像其他的函数,传1或0等这里要传,
只读 只写 读加写 追加 建造
这些都是宏,为什么要传他们,我传1、2、3、4等不好吗?那我们要传30个参数怎么办,太麻烦了。而这里使用宏就可以传一个整形就行,为什么?
系统传递标记位,是用位图结构来进行实现的。也就是说,这些宏对应的是位图中的一个数。例如:
传给系统就是一个整数,而且能清楚表达我们想做的,简单易懂。
写段代码体验一下:
1 #include<stdio.h>
2
3 #define PRIT_I 0x1
4 #define PRIT_L 0x2
5 #define PRIT_V 0x4
6 #define PRIT_U 0x8
7
8 void fuc(int flags)
9 {
10 if(flags & PRIT_I)
11 printf("I ");
12 if(flags & PRIT_L)
13 printf("L ");
14 if(flags & PRIT_V)
15 printf("V ");
16 if(flags & PRIT_U)
17 printf("U");
18
19 }
20
21 int main()
22 {
23 fuc(PRIT_I);
24 fuc(PRIT_L);
25 fuc(PRIT_V);
26 fuc(PRIT_U);
27 printf("\n");
28 fuc(PRIT_I);
29 printf("\n");
30 fuc(PRIT_I | PRIT_L);
31 printf("\n");
32 fuc(PRIT_I | PRIT_L | PRIT_V);
33 printf("\n");
34 fuc(PRIT_I | PRIT_L | PRIT_V | PRIT_U);
35 printf("\n");
36
37 return 0;
38 }
运行:
很是方便。知道了标记位,我们来小用下open。
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5
6 int main()
7 {
8 int fd = open("fight.txt", O_WRONLY | O_CREAT);
9 if(fd < 0)
10 {
11 perror("open");//打印错误信息
12 return 1;
13 }
14
15 printf("fd : %d \n", fd);
16
17
18 return 0;
19 }
这里的返回值为什么是3呢?我们下文来讲 。
为什么创建的这个文件,权限是乱的,-wSr...是什么?还记得open也有函数重载么,我们用第二个open,因为创建一个从没有过的文件,要设置它初始的权限。
int fd = open("fight.txt", O_WRONLY | O_CREAT, 0666);
运行结果:
我们明明想要0666,也就是权限为-rw-rw-rw-,现在为什么是-rw-rw-r--,那是跟我们系统的权限掩码有关,此时我们系统的权限掩码是0002,以致如此。
①细节:
c语言中fopen函数底层调用的是系统的open,fopen函数传入w会清空文件再写入或者创建新文件然后写入,底层给open第二个参数传入的是:
fopen函数传入a,底层给open第二个参数传入的是:
这样看来系统接口好麻烦,还是封装过后的函数好用。
②open的返回值
为什么是3呢?0 1 2 呢?其他呢?
int fda = open("fight1.txt", O_WRONLY | O_CREAT, 0666);
int fdb = open("fight2.txt", O_WRONLY | O_CREAT, 0666);
int fdc = open("fight3.txt", O_WRONLY | O_CREAT, 0666);
int fdd = open("fight4.txt", O_WRONLY | O_CREAT, 0666);
int fde = open("fight5.txt", O_WRONLY | O_CREAT, 0666);
printf("fda : %d \n", fda);
printf("fdb : %d \n", fdb);
printf("fdc : %d \n", fdc);
printf("fdd : %d \n", fdd);
printf("fde : %d \n", fde);
结果:
很怪啊,0 1 2 都没有,3以后递增,为什么?且听我细细道来。
文件描述符中的 C语言中的默认文件流
0: 标准输入 extern FILE * stdin
1: 标准输出 extern FILE * stdout
2: 标准错误 extern FILE * stdin
这俩如出一辙,我们都知道,C语言中的库函数都是封装自系统的接口。
这三个std和012有什么关系呢?一步一步来。我们知道:
那FILE是什么?
它是C库中的结构体,它其中封装了很多个成员。回到系统接口,对文件操作而言,系统接口只认文件描述符fd(file descriptor)。这些stdin、stdout、stderr都是文件,要去打开这些文件必定要使用fd,所以FILE这个结构体必须要封装fd(文件描述符),这样这三个文件才可以去使用封装过后的fd,来调用系统接口。
例如:
struct FILE
{
//封装fd的细节
}
int main()
{
FILE * stdin;
FILE * stdout;
FIEL * stderr;
}
试验一下,在系统接口read中传入0 1 2 ,是否是对应的stdin stdout stderr
③进程与文件
这里的0 1 2 3 ... 好像跟数组下标有关,这里为什么要这么设计呢?看下去.
在系统运行中,有可能有大量的文件被打开,所以系统要建立数据结构来进行对这些文件的管理,从数据结构的角度理解,就如同这样:
然后每个file就可以连接起来,以数据结构的角度来看,当然系统内核有自己的实现方式.
1个进程打开n个文件,在内核中进程如何映射到对应的文件的?
struct task_struct 中有struct files_struct *fs指针,这个指针再指向struct files_struct,这个结构中有一个指针数组struct file*fd_arr[],里面存在着struct file*的指针。
图示:
这些都是内核中实现的。我们来实际模拟一下:
④文件描述符的分配规则
从头遍历指针数组struct file* fd_arr[],将没有使用的下标且是没有使用中最小的,分配给新文件。
我们关闭0,分配给新文件的文件描述符就是0:
close(0);
int fd = open("Go.txt",O_WRONLY | O_CREAT,0666);
printf("fd: %d\n",fd);
close(fd);
结果:
2.close
有开必有关,只需将open时的返回值传入即可。
3.write
代码:
const char *str = "You gonna break my heart\n";
write(fd, str, strlen(str));
结果:
4.read
从文件中读取内容。这里的返回值ssize_t 是有符号整数。count是你所要读取的内容所占字节大小。
感谢观看。