核心(kernel)利用档案描述符(file descriptor)来访问档案。档案描述符是非负整数。打开现存档案或新建档案时,核心会返回一个档案描述符。读写档案也需要使用档案描述符来指定待读写的档案。
基本介绍
- 中文名:档案描述符
- 外文名:file descriptor
- 作用:核心利用档案描述符来访问档案
- 形式:非负整数
提出前提
每一个档案描述符会与一个打开档案相对应,同时,不同的档案描述符也会指向同一个档案。相同的档案可以被不同的进程打开也可以在同一个进程中被多次打开。系统为每一个进程维护了一个档案描述符表,该表的值都是从0开始的,所以在不同的进程中你会看到相同的档案描述符,这种情况下相同档案描述符有可能指向同一个档案,也有可能指向不同的档案。具体情况要具体分析,要理解具体其概况如何,需要查看由核心维护的3个数据结构。
1.进程级的档案描述符表;
2.系统级的打开档案描述符表;
3.档案系统的i-node表。
简介
档案描述符在形式上是一个非负整数。实际上,它是一个索引值,指向核心为每一个进程所维护的该进程打开档案的记录表。当程式打开一个现有档案或者创建一个新档案时,核心向进程返回一个档案描述符。在程式设计中,一些涉及底层的程式编写往往会围绕着档案描述符展开。但是档案描述符这一概念往往只适用于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结构中维护档案描述符表,所有的档案描述符都在该表中建立索引。
特点
优点
档案描述符的好处主要有两个:
基于档案描述符的I/O操作兼容POSIX标準。
在UNIX、Linux的系统调用中,大量的系统调用都是依赖于档案描述符。
例如,下面的代码就示範了如何基于档案描述符来读取当前目录下的一个指定档案,并把档案内容列印至Console中。
此外,在Linux系列的作业系统上,由于Linux的设计思想便是把一切设备都视作档案。因此,档案描述符为在该系列平台上进行设备相关的编程实际上提供了一个统一的方法。
#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);}
缺点
档案描述符的概念存在两大缺点:
在非UNIX/Linux作业系统上(如Windows NT),无法基于这一概念进行编程。
由于档案描述符在形式上不过是个整数,当代码量增大时,会使编程者难以分清哪些整数意味着数据,哪些意味着档案描述符。因此,完成的代码可读性也就会变得很差。
定义数量
如何在不同平台上定义档案描述符的数量
档案描述符极限以及可分配给进程的最大大小由资源限制来定义。这些值应当按照在WebLogicServer文档中建议的、特定于作业系统的档案描述符值来设定:
对于WLS8.1:调整硬体、作业系统和网路性能
对于WLS7.0:调整硬体、作业系统和网路性能
对于WLS6.1:调整硬体、作业系统和网路性能
Unix和Linux都有档案描述符。不过,二者的主要区别在于如何设定档案描述符的硬极限值、预设值和配置过程。
Solaris
/usr/bin/ulimit实用程式定义允许单个进程使用的档案描述符的数量。它的最大值在rlim_fd_max中定义,在预设情况下,它设定为65,536。只有root用户才能修改这些核心值。
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
Windows
在Windows作业系统上,档案描述符被称作档案句柄。在Windows2000伺服器上,打开档案的句柄极限设定为16,384。此数量可以在任务管理器的性能摘要中监视。
HP-UX
nfile定义打开档案的最大数量。此值通常由以下公式来确定:((NPROC*2)+1000),其中NPROC通常为:((MAXUSERS*5)+64)。如果MAXUSERS等于400,则经过计算得到此值为5128。通常可以将此值设高一些。maxfiles是每个进程的软档案极限,maxfiles_lim是每个进程的硬档案极限。
AIX
档案描述符极限在/etc/security/limits档案中设定,它的预设值是2000。此极限可以通过ulimit命令或setrlimit子例程来更改。最大大小由OPEN_MAX常数来定义。
解决方法
对于ANSI C规範中定义的标準库的档案I/O操作。ANSI C规範给出了一个解决方法,就是使用FILE结构体的指针。事实上,UNIX/Linux平台上的FILE结构体的实现中往往都是封装了档案描述符变数在其中。
在UNIX/Linux平台上,对于控制台(Console)的标準输入,标準输出,标準错误输出也对应了三个档案描述符。它们分别是0,1,2。在实际编程中,如果要操作这三个档案描述符时,建议使用<unistd.h>头档案中定义的三个宏来表示: STDIN_FILENO, STDOUT_FILENO以及STDERR_FILENO。 与档案描述符相关的操作
档案描述符的生成
open(), open64(), creat(), creat64()
socket()
socketpair()
pipe()
与单一档案描述符相关的操作
read(), write()
recv(), send()
recvmsg(),sendmsg()
sendfile()
lseek(), lseek64()
fstat(), fstat64()
fchmod()
fchown()
与複数档案描述符相关的操作
select(), pselect()
poll()
与档案描述符表相关的操作
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()
档案描述符与档案指针的区别
档案描述符:在linux系统中打开档案就会获得档案描述符,它是个很小的非负整数。每个进程在PCB(Process Control Block)中保存着一份档案描述符表,档案描述符就是这个表的索引,每个表项都有一个指向已打开档案的指针。
档案指针:C语言中使用档案指针做为I/O的句柄。档案指针指向进程用户区中的一个被称为FILE结构的数据结构。FILE结构包括一个缓冲区和一个档案描述符。而档案描述符是档案描述符表的一个索引,因此从某种意义上说档案指针就是句柄的句柄(在Windows系统上,档案描述符被称作档案句柄)。