FileDescriptor(文件描述符 )

简介

      内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。

     文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。在Windows操作系统上,文件描述符被称作文件句柄。
    习惯上,标准输入(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或createFile函数获取或者通过从父进程继承。后一种方法允许子进程同样能够访问由父进程使用的文件。文件描述符对于每个进程一般是唯一的。当用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),无法基于这一概念进行编程。

由于文件描述符在形式上不过是个整数,当代码量增大时,会使编程者难以分清哪些整数意味着数据,哪些意味着文件描述符。因此,完成的代码可读性也就会变得很差。

文件描述符与文件指针的区别

文件描述符:在linux系统中打开文件就会获得文件描述符,它是个很小的正整数。每个进程在PCB(Process Control Block)中保存着一份文件描述符表,文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针。文件指针:C语言中使用文件指针做为I/O的句柄。文件指针指向进程用户区中的一个被称为FILE结构的数据结构。FILE结构包括一个缓冲区和一个文件描述符。而文件描述符是文件描述符表的一个索引,因此从某种意义上说文件指针就是句柄的句柄(在Windows系统上,文件描述符被称作文件句柄)。

转载自:文件描述符 百科

=========================

FileDescriptor

一个打开的文件通过唯一的描述符进行引用,该描述符是打开文件的元数据到文件本身的映射。在Linux内核中,这个描述符称为文件描述符(FileDescriptor),文件描述符用一个整数表示(C语言中的类型为int),简写为fd。

文件描述符在用户空间(相对于内核空间而言,也就是我们应用程序的那层)中共享,允许用户程序用文件描述符直接访问文件。

同一个文件能被不同或者相同的进程多次打开,每一个打开文件的实例(也就是java中的File类对象吧)都产生一个唯一的文件描述符。同一个描述符可以被多个进程使用。不同的进程能同时对一个文件进行读写,所以存在并发修改问题。


fd如何访问文件

内核为每一个进程维护一个打开文件的列表,称为文件表(File Table),索引是fd,数据为打开文件的信息(包括一个指向文件的Inode对象的指针,和相关元数据,如当前文件文职、读取模式)。(Inode包含文件的物理地址)

简单的讲,也就是有了Map结构,key是fd,value是文件的信息(包括物理地址、读取模式。。。。。)

子进程默认获得一份父进程FIle Table的拷贝,而 更改一个进程的FileTable不会影响另一个进程(如果子进程关闭了文件 不会影响父进程的File Table),所以fd可以用来共享文件。

fd用C语言的int表示非负整数,从0开始递增 直到默认上限1024。按照惯例每个进程至少有三个打开的文件描述符,除非进程显式的关闭他们: 0 标准输入(stdin),1标准输出(stdout),2标准错误(stderr)。


Linux中fd打开文件的API

#include <sys/types.h>  
      #include <sys/stat.h>  
      #include <fcntl.h>  
  
      int open(const char *pathname, int flags);  
      int open(const char *pathname, int flags, mode_t mode);  
  
      int creat(const char *pathname, mode_t mode);  
  
      int openat(int dirfd, const char *pathname, int flags);  
      int openat(int dirfd, const char *pathname, int flags, mode_t mode);  

转载自:FileDescriptor(文件描述符 )与Linux文件系统

==================================

通过 Linux文件描述符/Windows句柄 来看 C语言里的"FILE"

1.“文件”的由来

一个程序的I/O指代了程序与外界的交互,包括文件、管道、网络、命令行、信号等。更广义地讲,I/O指代任何操作系统理解为“文件”的事务。许多操作系统,包括Linux和Windows,都将各种具有输入和输出概念的实体——包括设备、磁盘文件、命令行等——统称为文件,因此这里所说的文件是一个广义的概念。

对于一个任意类型文件,操作系统会提供一组操作函数,这包括打开文件、读文件、写文件、移动文件指针等,相信有编程经验的读者对此都不陌生。有过C语言编程经验的读者应该知道,C语言文件操作是通过了一个FILE结构的指针来进行的。fopen函数返回一个FILE结构的指针,而其他的函数如fwrite/fread使用这个这个指针操作文件。

2.“文件的实质”

在操作系统层面上,文件操作也有类似于FILE的一个概念,在Linux里,这叫做文件描述符(File Descriptor),而在Windows,叫做句柄(Handle)。实质上,文件描述符与句柄是一个相似的概念,都是用一个整形表示的value(以下在没有歧义的时候统称为句柄)。用户通过某个函数打开文件以获得句柄,此后用户操纵文件皆通过该句柄进行。

设计这么一个句柄的原因在于句柄可以防止用户随意读写系统内核的文件对象。无论是Linux还是Windows,文件句柄总是和内核的文件对象相关联的。内核可以通过句柄来计算出内核里文件对象的地址,但并不会把地址给用户。因为地址给你了,你可以利用指针对文件做任何事情,这个很危险。

下面举一个实际的例子,在Linux中,值为0,1,2的fd分别代表标准输入、标准输出和标准错误输出。在程序中打开文件得到的fd从3开始增长。fd具体是什么呢?在内核中,每一个进程都有一个私有的“打开文件表”,这个表是一个指针数组,每一个元素都指向一个内核的打开文件对象。而fd,就是这个表的下标。当用户打开一个文件时,内核会在内部生成一个打开文件对象,并在这个表里找到一个空项,让这一项指向生成的打开文件对象,并返回这一项的下标作为fd。由于这个表处于内核,用户无法访问到,因此用户即使拥有fd,也无法得到打开文件对象的地址,只能够通过系统提供的函数来操作。

在C语言里,操作文件的渠道则是FILE结构,不难想象,C语言中的FILE结构必定和fd有一对一的关系,每个FILE结构都会记录自己唯一对应的fd。

内核指针p指向该进程的打开文件表,所以只要有fd,就可以用p+fd来得到打开文件表的某一项地址。stdin、stdout、stderr均是FILE结构的指针。

对于Windows中的句柄,与Linux中的fd大同小异,不过Windows的句柄并不是打开文件表的下标,而是其下标经过某种线性变换之后的结果


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值