本帖最后由 ICHLIEBEDICH13 于 2016-9-9 18:24 编辑
文件描述符
内核(
kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
习惯上,标准输入(standard input)的文件描述符是 0,标准输出(standard output)是 1,标准错误(standard error)是 2。尽管这种习惯并非Unix内核的特性,但是因为一些 shell 和很多应用程序都使用这种习惯,因此,如果内核不遵循这种习惯的话,很多应用程序将不能使用。 POSIX 定义了 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 来代替 0、1、2。这三个符号常量的定义位于头文件 unistd.h。
文件描述符的有效范围是 0 到 OPEN_MAX。一般来说,每个进程最多可以打开 64 个文件(0 — 63)。对于 FreeBSD 5.2.1、Mac OS X 10.3 和 Solaris 9 来说,每个进程最多可以打开文件的多少取决于系统内存的大小,int 的大小,以及系统管理员设定的限制。Linux 2.4.22 强制规定最多不能超过 1,048,576 。
文件描述符是由无符号整数表示的句柄,进程使用它来标识打开的文件。文件描述符与包括相关信息(如文件的打开模式、文件的位置类型、文件的初始类型等)的文件对象相关联,这些信息被称作文件的上下文。
如何创建文件描述符
进程获取文件描述符最常见的方法是通过本机子例程open或create获取或者通过从父进程继承。后一种方法允许子进程同样能够访问由父进程使用的文件。文件描述符对于每个进程一般是唯一的。当用fork子例程创建某个子进程时,该子进程会获得其父进程所有文件描述符的副本,这些文件描述符在执行fork时打开。在由fcntl、dup和dup2子例程复制或拷贝某个进程时,会发生同样的复制过程。 对于每个进程,操作系统内核在u_block结构中维护文件描述符表,所有的文件描述符都在该表中建立索引。
优点
文件描述符的好处主要有两个:
例如,下面的代码就示范了如何基于文件描述符来读取当前目录下的一个指定文件,并把文件内容打印至Console中。
此外,在Linux系列的操作系统上,由于Linux的设计思想便是把一切设备都视作文件。因此,文件描述符为在该系列平台上进行设备相关的编程实际上提供了一个统一的方法。
#include
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);
缺点
文件描述符的概念存在两大缺点:
由于文件描述符在形式上不过是个整数,当代码量增大时,会使编程者难以分清哪些整数意味着数据,哪些意味着文件描述符。因此,完成的代码可读性也就会变得很差。
如定义文件描述符的数量
Linux
管理用户可以在etc/security/limits.conf配置文件中设置他们的文件描述符极限,如下例所示。
softnofile1024
hardnofile4096
系统级文件描述符极限还可以通过将以下三行添加到/etc/rc.d/rc.local启动脚本中来设置:
#Increasesystem-widefiledescriptorlimit.
echo4096>/proc/sys/fs/file-max
echo16384>/proc/sys/fs/inode-max
附:------------------------------------------
文件描述符的生成
open(),open64(), creat(), creat64()
socket()
pipe()
与单一文件描述符相关的操作
read(), write()
recv(), send()
sendfile()
lseek(),lseek64()
与复数文件描述符相关的操作
select(),pselect()
poll()
与文件描述符表相关的操作
close()
dup()
fcntl (F_DUPFD)
fcntl (F_GETFDand F_SETFD)
改变进程状态的操作
mmap()
与文件加锁的操作
flock()
fcntl (F_GETLK
F_SETLK and F_SETLKW)
与套接字相关的操作
connect()
bind()
listen()
accept()
getsockname()
getpeername()
getsockopt(),setsockopt()
shutdown()