文件描述符
文件描述符(File descriptor)是电脑科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。
概要
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程式设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
每个Unix进程(除了可能的守护进程)应均有三个标准的POSIX文件描述符,对应于三个标准流:
整数值 | 名称 | <unistd.h>符号常量[1] | <stdio.h>文件流[2] |
---|---|---|---|
0 | Standard input | STDIN_FILENO | stdin |
1 | Standard output | STDOUT_FILENO | stdout |
2 | Standard error | STDERR_FILENO | stderr |
优点
文件描述符的优点主要有两个:
例如,下面的代码就示范了如何基于文件描述符来读取当前目录下的一个指定文件,并把文件内容打印至控制台。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main (void){
int fd;
int numbytes;
char path[] = "file";
char buf[256];
/*
* O_CREAT:如果文件不存在则创建
* O_RDONLY:以只读模式打开文件
*/
fd = open(path, O_CREAT | O_RDONLY, 0644);
if(fd < 0){
perror("open()");
exit(EXIT_FAILURE);
}
memset(buf, 0x00, 256);
while((numbytes = read(fd, buf, 255)) > 0){
printf("%d bytes read: %s", numbytes, buf);
memset(buf, 0x00, 256);
}
close (fd);
exit(EXIT_SUCCESS);
}
此外,在Linux系列的操作系统上,由于Linux的设计思想便是把一切设备都视作文件。因此,文件描述符的存在提供了程序操作设备的统一方法。
缺点
文件描述符的概念存在两大缺点:
- 在非UNIX/Linux 操作系统上(如Windows),无法基于这一概念进行编程——事实上,Windows下的文件描述符和信号量、互斥锁等内核对象一样都记作HANDLE。
- 由于文件描述符在形式上不过是个整数,当代码量增大时,会使编程者难以分清哪些整数意味着数据,哪些意味着文件描述符。因此,完成的代码可读性也就会变得很差,这一点一般通过使用名称有文字意义的魔术数字进行替换来解决。
其他
- 对于ANSI C规范中定义的标准库的文件I/O操作。ANSI C规范给出了一个解决方法,就是使用FILE结构体的指针。事实上,UNIX/Linux平台上的FILE结构体的实现中往往都是封装了文件描述符变量在其中。
- 在UNIX/Linux平台上,对于控制台(Console)的标准输入,标准输出,标准错误输出也对应了三个文件描述符。它们分别是0,1,2。在实际编程中,如果要操作这三个文件描述符时,建议使用<unistd.h>头文件中定义的三个宏来表示:STDIN_FILENO, STDOUT_FILENO以及STDERR_FILENO.
- 对于一个进程而言,文件描述符的变化范围为0~OPEN_MAX[注 1].
与文件描述符相关的操作
文件描述符的生成
- open(), creat()
- socket()
- socketpair()
- pipe()
与单一文件描述符相关的操作
- read(), write()
- recv(), send()
- recvmsg(), sendmsg()
- sendfile()
- lseek()
- fstat()
- fchmod()
- fchown()
与复数文件描述符相关的操作
- select(), pselect()
- poll(),epoll()
与文件描述符表相关的操作
- close()
- dup()
- dup2()
- fcntl (F_DUPFD)
- fcntl (F_GETFD and F_SETFD)
改变进程状态的操作
- fchdir()
- mmap()
与文件加锁的操作
- flock()
- fcntl (F_GETLK, F_SETLK and F_SETLKW)
- lockf()
与套接字相关的操作
- connect()
- bind()
- listen()
- accept()
- getsockname()
- getpeername()
- getsockopt(), setsockopt()
- shutdown()
其他
- ioctl()