在C语言中打开文件时会发生什么?据我所知,当我们打开一个文件时,文件的内容并没有被加载到内存中。它只是设置了文件描述符。那么这个文件描述符是什么呢?如果文件的内容没有加载到内存中,那么文件是如何被打开的呢?
fopen
或(在POSIX系统上)open
打开文件,并且函数执行成功,它仅仅会给你一个值(FILE *
或int
),以便在将来的读取函数调用中使用。操作系统可能会在底层读取一些或全部文件内容,也可能不会。无论如何,你必须调用某个函数请求数据进行读取,如果在调用fread
/fgets
/read
等函数时还没有进行读取,则此时进行读取。在POSIX系统中,“文件描述符”通常指open
返回的整数。它用于标识已打开的文件。如果你得到一个值为3
的文件描述符,在某个地方,操作系统会跟踪3
是指向/home/user/dir/file.txt
或其他文件的。这是一个简短的值,用于向操作系统指示要从哪个文件进行读取。当你调用open
并打开foo.txt
时,操作系统会说:“好的,文件已打开,从现在开始称之为3
”。这个问题并不完全涉及编程语言。虽然库在打开文件(例如使用open
或fopen
)时会产生影响,但主要的行为来自操作系统。
Linux和其他操作系统在大多数情况下都会执行预读取。这意味着实际上在用户调用read
读取文件之前,文件已经从物理存储中读取出来了。这是一种优化方法,减少了用户读取文件所需的时间。程序员可以部分地控制这种行为,使用特定的标志来调用打开函数。例如,Win32 API的CreateFile
可以指定FILE_FLAG_RANDOM_ACCESS
或FILE_FLAG_SEQUENTIAL_SCAN
来指定随机访问(此时文件不会预读),或者顺序访问(此时操作系统将执行相当激进的预读取),等等。其他操作系统API可能会提供更多或更少的控制。
对于使用文件描述符的基本ANSI C API open
,read
和write
,文件描述符是一个简单的整数,传递给操作系统,表示该文件。在操作系统中,这通常被转换为某些包含有关文件的所有必要信息的结构(名称、路径、寻求偏移量、大小、读取和写入缓冲区等)。操作系统将打开文件,即查找与您在open
方法中给出的路径对应的特定文件系统条目(在Linux下为inode
),创建文件结构,并返回一个ID给用户-文件描述符。从那时起,操作系统可以随意读取任何数据,即使用户没有请求(通常会读取比所请求的更多的数据,至少可以使用文件系统本地大小来工作)。
C语言没有文件I/O的原始数据类型,一切都取决于你使用的操作系统和库。
fopen
是 C 语言中用于文件 I/O 的原始函数,不依赖于操作系统。(尽管您可以经常调用特定于操作系统的函数以获得更多控制权。) - Thanatosfopen
是一个库函数,而不是原始函数。此外,由于文件访问总是由操作系统完成,因此 fopen
的确取决于操作系统。在某个操作系统上,fopen
可能能够预读或缓冲写入,而在另一个操作系统上可能不能。 - Eli Iser文件描述符只是抽象的,所有操作都在操作系统上完成。
fopen()
,则缓冲包将使用特定于实现的系统调用来获取文件描述符,并将其存储在FILE
结构中。如前所述,这是操作系统的功能。
但对于C文件I/O,您可能需要了解fopen
函数的信息。
如果您查看该函数的描述,它会说:
描述:
打开一个流。
fopen打开由文件名指定的文件,并将流与其关联。fopen返回一个指针,用于标识后续操作中的流。
因此,在成功完成时,fopen
只返回指向新打开的流的指针。如果出现任何错误,则返回NULL。
typedef struct {
int level; /* fill/empty level of buffer */
unsigned flags; /* File status flags */
char fd; /* File descriptor */
unsigned char hold; /* Ungetc char if no buffer */
int bsize; /* Buffer size */
unsigned char *buffer; /* Data transfer buffer */
unsigned char *curp; /* Current active pointer */
unsigned istemp; /* Temporary file indicator */
short token; /* Used for validity checking */
} FILE;