对文件进行操作的第一步首先就是要将文件打开,文件未打开之前是保存在磁盘中的,而打开后就被加载到内存中了,之后我们就能对文件进行操作。
打开文件的系统调用接口:open
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int open(const char* pathname, int flags); //函数1
int open(const char* pathname, int flags, mode_t mode); //函数2
先分析函数2,
pathname·:想要打开/创建的文件(包含路径和文件名)
flags:标志位。表示想要它是如何打开的以及一些属性。对于这个参数系统定义了几个宏。
上面的这三个宏,再使用open时必须要指定一个,并且也只能指定一个。
返回值
成功:返回一个新打开的文件描述符(在后面介绍)
失败:返回-1
关闭文件
#include <unistd.h>
int close(int fd); //传入文件描述符
一个进程通常会打开多个文件,那么操作系统就需要对这些文件进行管理。
Linux操作系统将打开的文件相关属性信息放在了结构体struct file
内。而Linux的进程PCB---->task_struct
中有一个结构体指针struct files_struct *fs
,fs指向的表中又存在着一个数组,这个数组里面的内容正是存放着文件相关属性信息的结构体地址。
而文件描述符就是文件打开后存放其相关属性信息的结构体,对应的fd_array数组下标。只要有描述它相关属性信息的结构体信息,那么就能对这个文件进行操作。
//打开文件,查看文件描述符
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6
7 int main(void)
8 {
9 int fd = open("./log.txt", O_WRONLY |O_CREAT, 0644);
10 if(fd < 0)
11 {
12 //open fail
13 perror("open");
14 return 1;
15 }
16
17 printf("%d\n", fd);
18
19 //close file
20 close(fd);
21 return 0;
22 }
~
运行结果
为什么打开文件log.txt,open返回的文件描述符是3?
打开多个文件,它们的文件描述符又会怎样?
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6
7 int main(void)
8 {
9 int fd1 = open("./log1.txt", O_WRONLY |O_CREAT, 0644);
10 int fd2 = open("./log2.txt", O_WRONLY |O_CREAT, 0644);
11 int fd3 = open("./log3.txt", O_WRONLY |O_CREAT, 0644);
12 int fd4 = open("./log4.txt", O_WRONLY |O_CREAT, 0644);
13 int fd5 = open("./log5.txt", O_WRONLY |O_CREAT, 0644);
14
15 printf("%d\n", fd1);
16 printf("%d\n", fd2);
17 printf("%d\n", fd3);
18 printf("%d\n", fd4);
19 printf("%d\n", fd5);
20
21 //close file
22 close(fd1);
23 close(fd2);
24 close(fd3);
25 close(fd4);
26 close(fd5);
27 return 0;
28 }
显然,包含它们相关属性信息的结构体,对应的fd_array[]数组下标是3开头的。
那么fd_array[]数组下标0、1、2里面放了哪些struct file
的地址?
事实上,当一个进程被创建的时候,操作系统会默认打开标准输入、标准输出、标准错误。而这些都是文件,标准输入、标准输出、标准错误对应的文件描述符正是0、1、2
文件描述符的分配规则
文件描述符的分配规则:给一个新文件分配文件描述符,从数组头部开始线性遍历,直到第一次找到一个没有被占用的空间,用存放这个新文件的struct file
,其下标就是对应的文件描述符。(即下标最小的,没有被占用的作为文件描述符)
正如前面所举例,我们新打开一个文件,0、1、2被标准输入、标准输出和标准错误占用,那么此时下标最小的、没有被占用的就是数组下标为3的空间。所以这个文件的文件描述符为3。
//当我们尝试将标准输出关闭
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6
7 int main(void)
8 {
9 close(1); //关闭标准输出
10 int fd = open("./log.txt", O_CREAT | O_WRONLY, 0644);
11 printf("%d\n", fd);
12 printf("hello world\n");
13 printf("hello world\n");
14 printf("hello world\n");
15 printf("hello world\n");
16 printf("hello world\n");
17 printf("hello world\n");
18 //close(fd);
19 return 0;
20}
运行结果:本来应该直接显示到屏幕,但是并没有,发现跑到了文件log.txt中
因为我们把标准输入关闭了,那么文件描述符1就分配给了log.txt,而操作系统一直认为,打印应该是打印到文件描述符为1的位置,所以才会发生这种情况,这也是输出重定向的原理。
这个例子要注意,不要close(fd)
,如果最后关闭了文件描述符1,那么这个位置就是空的,无法打印。