计算机中两个重要概念,时间和空间。时间概念可以抽象为进程,而空间的概念可以抽象为文件。
文件描述符的概念:
Linux环境下每一个磁盘文件在打开时都会在内核建立一个文件表项。文件表项包括文件的状态信息、存储文件内容的缓存区以及当前文件的读写位置等。同一个磁盘文件打开两次时会创建两个这样的文件表项,读写该文件的时候只影响到该文件表项中的文件读写位置。这些文件表项共同保存在内核的一个数组里,这个数组就是文件表。
每个进程在内核中有保存一个整型数组,数组中每个元素是文件表的下标。因此使用该数组下标可以引用打开的文件表项。该数组下标就是文件描述符,每个进程中的文件表下标组成的数组就是文件描述数组。
综上来说就是,文件的状态信息、存储文件内容的缓存区以及当前文件的读写位置等都存在文件表项中,多个文件表项保存在内核的一个数组中,这个数组叫做文件表。多个文件表的下标组成一个数组存储在内核中。进程需要引用文件的时候只需要通过内核中的数组来操作文件描述符就可以了,文件描述符的意义就是为进程与打开文件搭建一个桥梁。
文件的打开:
基本的I/O操作包括打开文件、关闭文件、读写定位等。Linux系统中打开文件的函数是open(),关闭文件的函数是close(),在文件中定位是lseek()函数,读文件使用read()函数,写文件使用write()函数。
1、打开文件,在Linux系统下通过调用open()函数来打开一个文件,如果该文件不存在可以创建该文件。
打开文件函数 open(const char * pathname, int oflag, … /*mode_t mode*/)
头文件 #include <fcntl.h>
参数说明:
第一个参数pathname是要打开的文件,有两种方式指定该文件。
(1)可以是文件的绝对路径(或者是相对路径),系统会根据路径寻找文件
(2)该文件的文件名,这种情况下系统只在当前工作目录下寻找要操作的文件。
第二个参数oflag是指定打开文件的选项,其值是一个位向量,通常使用下列一个或多个常量进行或操作构成oflag,这些常量值由宏定义,存在fcntl.h头文件中。第二个参数中分为两类一类是必选,一类是可选。必选的有三个,分别是O_RDONLY、O_WRONLY和O_RDWR。有一点需要注意的是,三种必选参数是互斥的,不能同时使用,每次只能选择三个必选参数中的一个,但是必选参数可以同下边的可选参数通过“|”运算符组合使用。
必选参数意义如下:
O_RDONLY(Read only):只读打开,值为0,用该方式打开文件,只能对文件进行读操作。
O_WRONLY(Write only):只写打开,值为1,用该方式打开文件,只能对文件进行写操作。
O_RDWR(Read Write):读写打开,值为2,用该方式打开文件,既可以对文件进行读操作也可以对文件进行写操作。
可选参数有多种,一些比较常用的下面列举出来
可选参数意义如下:
O_CREAT:若欲打开的文件不存在则自动建立该文件,此时需要open函数的第三个参数,第三个参数代表新创建文件的访问权限。
O_EXCL:如果O_CREAT也被设置,此指令会去检查文件是否存在。文件若不存在则建立该文件,如果该文件存在,则将导致打开文件错误。这种做法可以避免多进程的并发错误,该操作是原子操作。
O_TRUNC:若文件存在并且以可写的方式成功打开时,此标志位会将该文件截短为0,而原来存于该文件的资料也会消失。
O_APPEND:当读写文件时会从文件尾开始移动,也就是所写入的数据会以附加的方式加入到文件后面。
O_NONBLOCK:以不可阻断的方式打开文件,也就是无论有无数据读取或等待,都会立即返回进程之中。如果指定文件是命名管道、块设备或者字符设备,则将此设备设置为非组态。
O_SYNC:以同步的方式打开文件。
O_NOCTTY:如果欲打开的文件为终端机设备时,则不会将该终端机当成进程控制终端机。
打开文件选项的操作比较复杂,需要通过实际的操作之后,更好理解,这些常量的意义理解之后,再通过实际的程序来加深理解效果更好一点。
第三个参数/*mode_t mode*/只有在要打开的文件不存在的时候才会使用,该参数是设置新创建文件的权限。
返回值:返回值是打开文件的文件描述符(前边讲过文件描述符)。错误返回-1,并设置errno变量值。
函数说明:打开要操作的文件,具体的打开方式视需求而定。
应用实例:下面一个简单的例子是用open打开一个文件,如果该文件不存在的话创建一个该文件。
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> int main(void) { int fd; /* 文件描述符 */ /* 以“读写”模式打开当前目录下的test.txt文件,如果不存在则创建该文件, * 并且将其文件权限字设置为八进制的0700 */ fd = open("test.txt", O_RDWR | O_CREAT, 0700); if(fd == -1){ perror("fail to open"); /* 打开文件出错,打印错误号 */ exit(1); }else printf("open OK "); close(fd); /* 关闭文件,有关内容见本书后面小节 */ return 0; }
在实际编程中,为了避免打开文件出错我们一般会用检查errno的方法来及时 发现是否出错,能够及时处理,下面一个程序演示一下常见的打开错误以及检查errno的方法。
#include <stdio.h> #include <fcntl.h> #include <unistd.h> int main(void) { int fd; fd = open("no_such_file", O_RDONLY); /* 尝试打开一个不存在的文件 */ if(fd == -1) perror("fail to open"); else close(fd); fd = open("denied", O_WRONLY); /* 以权限不允许的方式打开文件 */ if(fd == -1) perror("fail to open"); else close(fd); /* 关闭打开的文件 */ return 0; }
2、关闭文件
Linux系统中使用close()函数关闭一个打开的文件,close(int fd)。
参数fd是要关闭文件的文件描述符,成功关闭返回1,失败返回-1。