文件描述符

什麼是文件描述符

文件描述符在形式上是一個非負整數。實際上,它是一個索引值,node

指向內核爲每個進程所維護的該進程打開文件的記錄表。linux

當程序打開一個現有文件或者建立一個新文件時,內核向進程返回一個文件描述符。shell

在程序設計中,一些涉及底層的程序編寫每每會圍繞着文件描述符展開。編程

可是文件描述符這一律念每每只適用於UNIX、Linux這樣的操做系統。windows

 

  習慣上,緩存

     標準輸入(standard input)文件描述符是  0服務器

     標準輸出(standard output)文件描述符是 1網絡

     標準錯誤(standard error)文件描述符是  2數據結構

 

儘管這種習慣並不是Unix內核的特性,可是由於一些 shell 和不少應用程序都使用這種習慣,所以,若是內核不遵循這種習慣的話,不少應用程序將不能使用。socket


POSIX 定義了 STDIN_FILENO  STDOUT_FILENO  STDERR_FILENO 來代替 0、一、2。

這三個符號常量的定義位於頭文件 <unistd.h>

 

文件描述符的範圍

文件描述符的有效範圍是 [0,OPEN_MAX]

通常來講,每一個進程最多能夠打開 64 個文件(0 — 63)。

對於 FreeBSD 5.2.一、Mac OS X 10.3 和 Solaris 9 來講,每一個進程最多能夠打開文件的多少取決於系統內存的大小,int 的大小

以及系統管理員設定的限制。Linux 2.4.22 強制規定最多不能超過 1,048,576 。


文件描述符是由無符號整數(正整數)表示的句柄,進程使用它來標識打開的文件。

文件描述符與包括相關信息(如文件的打開模式、文件的位置類型、文件的初始類型等)的文件對象相關聯,這些信息被稱做文件的上下文。

 

 

如何建立文件描述符

進程獲取文件描述符最多見的方法是經過本機子例程opencreate獲取或者經過從父進程繼承。

後一種方法容許子進程一樣可以訪問由父進程使用的文件。

文件描述符對於每一個進程通常是惟一的。當用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);
}

// 讀取文件描述符,將結果保存到Buf變量
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),沒法基於這一律念進行編程。(windows是垃圾系統,能夠無視)
因爲文件描述符在形式上不過是個整數,當代碼量增大時,會使編程者難以分清哪些整數意味着數據,哪些意味着文件描述符。(可讀性差)

 

定義數量
如何在不一樣平臺上定義文件描述符的數量.

文件描述符極限以及可分配給進程的最大大小由資源限制來定義。

這些值應當按照在WebLogicServer文檔中建議的、特定於操做系統的文件描述符值來設置:

對於WLS8.1:調整硬件、操做系統和網絡性能 對於WLS7.0:調整硬件、操做系統和網絡性能 對於WLS6.1:調整硬件、操做系統和網絡性能

 


Unix和Linux都有文件描述符。不過,兩者的主要區別在於如何設置文件描述符的硬極限值、缺省值和配置過程。


Solaris  (SUN微系統公司開發的一種網絡操做系統)

/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   (惠普9000系列服務器的操做系統)

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系統上,文件描述符被稱做文件句柄)。

 

 

fileno()函數

原型:int _fileno(FILE * stream);

功能:獲取stream指定的文件流所使用的文件描述符

返回:文件描述符

頭件:<stdio.h>

 

例子:

 

#include <stdio.h>
int main( void ) { printf( "The file descriptor for stdin is %d\n", _fileno( stdin ) );  //輸出0 printf( "The file descriptor for stdout is %d\n", _fileno( stdout ) );         //輸出1 printf( "The file descriptor for stderr is %d\n", _fileno( stderr ) );         //輸出2 }

 

 

#include <stdio.h>
int main(void) { FILE *fp; int fd; fp = fopen("/etc/passwd", "r"); fd = fileno(fp); printf("fd = %d\n", fd); fclose(fp); return 0; }

fileno函數的實現

#define fileno(_p)((_p)->_file)  //_file是文件描述符,由於FILE結構體以下:

 

typedef struct {
int _cnt;
int __stdioid;

void *_lock;
char *__newbase;

unsigned
char *_ptr;
unsigned char *_base;
unsigned
char *_bufendp;
short _flag; short _file;
} FILE;

 

 

isatty()函數

原型:int isatty(int desc);

返回:判斷結果

功能:判斷desc所表示的文件描述符是否爲終端

例子:

 

#include <stdio.h> #include <io.h>
int main(void) { int handle; handle = fileno(stdout); if (isatty(handle)) printf("Handle %d is a device type\n", handle); else printf("Handle %d isn't a device type\n", handle); return 0; }

 

 

自行實例

/* * *緩存機制 *由於讀取了13個字符,而hello world\0 只有12個字符 *所以從緩衝區在取多一個讀取,因此打印g *open用文件描述符來對文件或設備進行操做 *fopen用文件流來對文件進行操做 *fopen用 fileno將文件流轉成文件描述符 * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h>

#define SIZE 13

int main(){ //char *p = "hello world"; /*char p[] = "hello world" */ //char *p = (char*)malloc(20*sizeof(char)); //p="hello world"; //char *p = (char*)malloc(20*sizeof(char)); //*p='c'; //wrong: *p = "hello world";

  char *p = (char*)malloc(SIZE * sizeof(char)); //read
  int fd =   open("test.cc",O_RDWR|O_CREAT); int fd =   open("test.cc",O_RDWR|O_CREAT); printf("fd = %d",fd); int read_num = read(fd,p,SIZE); printf("read %d num string :%s\n",read_num,p); //write
  p = "good morning"; int write_num = write(fd,p,SIZE); printf("write %d num string:%s\n",write_num,p); //fopen is file flow??
  FILE *pf = fopen("test.cc","r"); fd = fileno(pf);   //file flow change to file descriptor;
  read_num = read(fd,p,SIZE); printf("fd = %d and read_string = %s\n",fd,p); free(pf); return 0; }

 [root@demon-pc ~]# ./a.out
fd = 3read 12 num string :hello world

write 12 num string:good morning
fd = 4 and read_string = good morning

相關文章
相關標籤/搜索