博文說明【前言】:
html
本文將經過我的口吻介紹經過Linux中的文件描述符相關知識,在目前時間點【2017年7月4號】下,所掌握的技術水平有限,可能會存在很多知識理解不夠深刻或全面,望你們指出問題共同交流,在後續工做及學習中如發現本文內容與實際狀況有所誤差,將會完善該博文內容。node
本文參考文獻引用連接:linux
一、http://www.cnblogs.com/reach296/p/3915512.htmlshell
二、http://blog.csdn.net/cywosp/article/details/38965239【這篇文章不錯】 vim
三、http://blog.csdn.net/kumu_linux/article/details/7877770【好文】服務器
正文:網絡
咱們知道,在Linux中,一切皆文件,其實這個文件也是分爲好幾類的。數據結構
主要有(以ls -l命令輸出爲例):socket
- #普通文件ide
d #目錄文件
l #連接文件
c #設備文件中的-字符型設備文件
b #設備文件中的-塊設備文件
n #設備文件中的-網絡設備文件
一、普通文件
該文件是是用戶平常使用最多的文件,包括文本文件、shell腳本、二進制的可執行和各類類型的數據文件等等。
該文件的屬性一般是: -rw-r--r-- 這些文件通常是由一些相關的應用程序建立,好比touch,vim等
二、目錄文件
在Linux中,目錄也是文件,目錄的內容可以是文件名和子目錄名以及指向那些文件和子目錄的指針
目錄文件的屬性一般是:drwxr-xr-x ,目錄在Linux是一個比較特殊的文件。
三、連接文件
連接文件相似於Windows中的「快捷方式」
建立命令格式:
# ln/cp -s/-l /絕對路徑/源文件名 /絕對路徑/新文件名
建立命令有:
ln(建立硬連接,需在同一個文件系統中,inode號相同,刪除源文件後連接文件依然存在)
ln -s(建立軟連接,也就是符號連接,能夠跨越文件系統,inode號不一樣,刪除源文件後連接文件失效)
cp -l(建立硬連接,需在同一個文件系統中,inode號相同,刪除源文件後連接文件依然存在)
cp -s(建立軟連接,也就是符號連接,能夠跨越文件系統,inode號不一樣,刪除源文件後連接文件失效)
四、設備文件
包括三種,塊設備文件,另外一種是字符設備文件,網絡設備文件不怎麼涉及。
字符設備(無緩衝且只能順序存取)、塊設備(有緩衝且能夠隨機存取)。
塊設備文件是指數據的讀寫,它們是以塊爲單位的設備,如硬盤光驅。
字符設備主要是指串行端口的接口設備,如網卡等。
好了,基礎概念介紹完畢,開始正題,上方爲我的編寫,下方爲引用,因引用篇幅超過50%,因此本文視爲轉載。
問題1:什麼是文件描述符?
1. 文件描述概念
文件描述符是一個簡單的整數,用以標明每個被進程所打開的文件和socket。第一個打開的文件是0,第二個是1,第三個文件是2,依此類推。
文件描述符(file descriptor)是內核爲了高效管理已被打開的文件所建立的索引,其是一個非負整數(一般是小整數),用於指代被打開的文件,全部執行I/O操做的系統調用都經過文件描述符。程序剛剛啓動的時候,0是標準輸入,1是標準輸出,2是標準錯誤。若是此時去打開一個新的文件,它的文件描述符會是3。POSIX標準要求每次打開文件時(含socket)必須使用當前進程中最小可用的文件描述符號碼,所以,在網絡通訊過程當中稍不注意就有可能形成串話。標準文件描述符圖以下:
文件描述與打開的文件對應模型以下圖:
2. 文件描述限制
在編寫文件操做的或者網絡通訊的軟件時,初學者通常可能會遇到「Too many open files」的問題。這主要是由於文件描述符是系統的一個重要資源,雖說系統內存有多少就能夠打開多少的文件描述符,可是在實際實現過程當中內核是會作相應的處理的,通常最大打開文件數會是系統內存的10%(以KB來計算)(稱之爲系統級限制)
查看系統級別的最大打開文件數可使用該命令查看:sysctl -a | grep fs.file-max
與此同時,內核爲了避免讓某一個進程消耗掉全部的文件資源,其也會對單個進程最大打開文件數作默認值處理(稱之爲用戶級限制),默認值通常是1024,使用ulimit -n命令能夠查看。在Web服務器中,經過更改系統默認值文件描述符的最大值來優化服務器是最多見的方式之一。
3. 文件描述符合打開文件之間的關係
每個文件描述符會與一個打開文件相對應,同時,不一樣的文件描述符也會指向同一個文件。相同的文件能夠被不一樣的進程打開也能夠在同一個進程中被屢次打開。系統爲每個進程維護了一個文件描述符表,該表的值都是從0開始的,因此在不一樣的進程中你會看到相同的文件描述符,這種狀況下相同文件描述符有可能指向同一個文件,也有可能指向不一樣的文件。具體狀況要具體分析,要理解具體其概況如何,須要查看由內核維護的3個數據結構。
1. 進程級的文件描述符表
2. 系統級的打開文件描述符表
3. 文件系統的i-node表
進程級的描述符表的每一條目記錄了單個文件描述符的相關信息。
1. 控制文件描述符操做的一組標誌。(目前,此類標誌僅定義了一個,即close-on-exec標誌)
2. 對打開文件句柄的引用
內核對全部打開的文件的文件維護有一個系統級的描述符表格(open file description table)。有時,也稱之爲打開文件表(open file table),並將表格中各條目稱爲打開文件句柄(open file handle)。一個打開文件句柄存儲了與一個打開文件相關的所有信息,以下所示:
1. 當前文件偏移量(調用read()和write()時更新,或使用lseek()直接修改)
2. 打開文件時所使用的狀態標識(即,open()的flags參數)
3. 文件訪問模式(如調用open()時所設置的只讀模式、只寫模式或讀寫模式)
4. 與信號驅動相關的設置
5. 對該文件i-node對象的引用
6. 文件類型(例如:常規文件、套接字或FIFO)和訪問權限
7. 一個指針,指向該文件所持有的鎖列表
8. 文件的各類屬性,包括文件大小以及與不一樣類型操做相關的時間戳
下圖展現了文件描述符、打開的文件句柄以及i-node之間的關係,圖中,兩個進程擁有諸多打開的文件描述符。
在進程A中,文件描述符1和30都指向了同一個打開的文件句柄(標號23)。這多是經過調用dup()、dup2()、fcntl()或者對同一個文件屢次調用了open()函數而造成的。
進程A的文件描述符2和進程B的文件描述符2都指向了同一個打開的文件句柄(標號73)。這種情形多是在調用fork()後出現的(即,進程A、B是父子進程關係),或者當某進程經過UNIX域套接字將一個打開的文件描述符傳遞給另外一個進程時,也會發生。再者是不一樣的進程獨自去調用open函數打開了同一個文件,此時進程內部的描述符正好分配到與其餘進程打開該文件的描述符同樣。
此外,進程A的描述符0和進程B的描述符3分別指向不一樣的打開文件句柄,但這些句柄均指向i-node表的相同條目(1976),換言之,指向同一個文件。發生這種狀況是由於每一個進程各自對同一個文件發起了open()調用。同一個進程兩次打開同一個文件,也會發生相似狀況。
4. 總結
1. 因爲進程級文件描述符表的存在,不一樣的進程中會出現相同的文件描述符,它們可能指向同一個文件,也可能指向不一樣的文件
2. 兩個不一樣的文件描述符,若指向同一個打開文件句柄,將共享同一文件偏移量。所以,若是經過其中一個文件描述符來修改文件偏移量(由調用read()、write()或lseek()所致),那麼從另外一個描述符中也會觀察到變化,不管這兩個文件描述符是否屬於不一樣進程,仍是同一個進程,狀況都是如此。
3. 要獲取和修改打開的文件標誌(例如:O_APPEND、O_NONBLOCK和O_ASYNC),可執行fcntl()的F_GETFL和F_SETFL操做,其對做用域的約束與上一條頗爲相似。
4. 文件描述符標誌(即,close-on-exec)爲進程和文件描述符所私有。對這一標誌的修改將不會影響同一進程或不一樣進程中的其餘文件描述符
文件描述符的具體應用
對於squid,由於squid 的工做方式,文件描述符的限制可能會極大的影響性能。當squid 用完全部的文件描述符後,它不能接收用戶新的鏈接。也就是說,用完文件描述符致使拒絕服務。直到一部分當前請求完成,相應的文件和socket 被關閉,squid不能接收新請求。當squid發現文件描述符短缺時,它會發布警告。
對於Apache,當使用了不少虛擬主機,而每一個主機又使用了不一樣的日誌文件時,Apache可能會遭遇耗盡文件描述符(有時也稱爲file handles)的困境。 Apache使用的文件描述符總數以下:每一個不一樣的錯誤日誌文件一個、 每一個其餘日誌文件指令一個、再加10~20個做爲內部使用。Unix操做系統限制了每一個進程可使用的文件描述符數量。典型上限是64個,但能夠進行擴充,直至到達一個很大的硬限制爲止(a large hard-limit)。
linux下最大文件描述符的限制有兩個方面,一個是用戶級的限制,另一個則是系統級限制。
如下是查看Linux文件描述符的三種方式:
系統級限制:
[root@localhost ~]# sysctl -a | grep -i file-max --color
fs.file-max = 392036
[root@localhost ~]# cat /proc/sys/fs/file-max
392036
用戶級限制:
[root@localhost ~]# ulimit -n
1024
[root@localhost ~]#
系統級限制:sysctl命令和proc文件系統中查看到的數值是同樣的,這屬於系統級限制,它是限制全部用戶打開文件描述符的總和
用戶級限制:ulimit命令看到的是用戶級的最大文件描述符限制,也就是說每個用戶登陸後執行的程序佔用文件描述符的總數不能超過這個限制
如何修改文件描述符的值?
一、修改用戶級限制
[root@localhost ~]# ulimit-SHn 10240
[root@localhost ~]# ulimit -n
10240
[root@localhost ~]#
以上的修改只對當前會話起做用,是臨時性的,若是須要永久修改,則要修改以下:
[root@localhost ~]# grep -vE'^$|^#' /etc/security/limits.conf
* hard nofile 4096
[root@localhost ~]#
//默認配置文件中只有hard選項,soft 指的是當前系統生效的設置值,hard 代表系統中所能設定的最大值
[root@localhost ~]# grep -vE'^$|^#' /etc/security/limits.conf
* hard nofile 10240
* soft nofile 10240
[root@localhost ~]#
// soft<=hard soft的限制不能比hard限制高
二、修改系統限制
[root@localhost ~]# sysctl -wfs.file-max=400000
fs.file-max = 400000
[root@localhost ~]# echo350000 > /proc/sys/fs/file-max //重啓後失效
[root@localhost ~]# cat /proc/sys/fs/file-max
350000
[root@localhost ~]#
//以上是臨時修改文件描述符
//永久修改把fs.file-max=400000添加到/etc/sysctl.conf中,使用sysctl -p便可
下面是摘自kernel document中關於file-max和file-nr參數的說明
file-max & file-nr:
The kernel allocates file handles dynamically, but as yet it doesn't free them again.
內核能夠動態的分配文件句柄,但到目前爲止是不會釋放它們的
The value in file-max denotes the maximum number of file handles that the Linux kernel will allocate. When you get lots of error messages about running out of file handles, you might want to increase this limit.
file-max的值是linux內核能夠分配的最大文件句柄數。若是你看到了不少關於打開文件數已經達到了最大值的錯誤信息,你能夠試着增長該值的限制
Historically, the three values in file-nr denoted the number of allocated file handles, the number of allocated but unused file handles, and the maximum number of file handles. Linux 2.6 always reports 0 as the number of free file handles -- this is not an error, it just means that the number of allocated file handles exactly matches the number of used file handles.
在kernel 2.6以前的版本中,file-nr 中的值由三部分組成,分別爲:1.已經分配的文件句柄數,2.已經分配單沒有使用的文件句柄數,3.最大文件句柄數。但在kernel 2.6版本中第二項的值總爲0,這並非一個錯誤,它實際上意味着已經分配的文件句柄無一浪費的都已經被使用了
結尾:
感謝閱讀,祝有收穫的一天,謝謝!