深入理解Linux/Unix檔案描述符和epoll
Linux/Unix 檔案描述符(File Describer)的本質
Linux/Unix(以下簡稱Linux)系統中,每個程序都有一個專用的陣列,陣列的元素是一個結構體,稱為檔案描述符File Descriptor(以下簡稱fd),但是至少包含一個檔案指標,指向Linux核心的Open File Table(以下簡稱Open表),Open表也可以理解一個數組,使用偏移量來指示每個元素的位置,上述fd的檔案指標指向的就是這裡說的偏移位置。Open表的元素稱為File Description(以下簡稱FD,注意描述的差異),每個FD都有一個INODE指標,指向檔案系統的INODE Table。檔案系統的INODE Table(以下簡稱INODE表),每個元素也是個結構型別,包括了檔案在磁碟中的具體位置、所有者、寫入時間等的資訊,檔案驅動器通過INODE表來實際操作檔案。具體如下圖:
建立fd的方式:
- 系統呼叫,比如使用
socket()
的函式進行操作 - 從父程序中繼承,執行緒A使用
fork()
函式生成執行緒B,那麼B就有了自己的fd,不過指向的是相同的FD。
注意:如果在複製的時候,對某些fd使用了CLOSE_ONEXEC
標記,那麼子程序的這些fd就失效了,但是不影響父程序的fd使用
銷燬fd的方式:
close()
系統呼叫- 程序結束
關於INODE,前面提到INODE也是一個結構型別,但是它仍然不會儲存資料的磁碟資料,它儲存的是檔案的資訊。檔案系統是軟硬體的結合處,該系統通過INODE的資訊查詢檔案。Linux中的每一個檔案(Linux一切皆檔案)都對應一個INODE實體,每個系統有一個INODE上限。
理解epoll底層原理(非具體實現)
建立epoll()
:
#include <sys/epoll.h>
int epoll_create(int size);
size
指定大小epoll
將要建立事件佇列的容量,不過該引數在核心2.6.8之後就廢棄了,由系統自動化分配。
函式返回epoll在程序中的fd。
#include <sys/epoll.h>
int epoll_create1(int flags);
flags=0功能同上,另一個選項是EPOLL_CLOEXEC
。這個選項的作用是當父程序fork
出一個子程序的時候,子程序不會包含epoll
fd
在epoll
上註冊事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
epfd
是建立的epoll
的fdop
表示操作的型別EPOLL_CTL_ADD
:註冊事件EPOLL_CTL_MOD
:更改事件EPOLL_CTL_DEL
:刪除事件
fd
是相應的檔案描述符event
是事件佇列
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events;
epoll_data_t data;
};
事件是一些巨集定義,可以查表,data是共用體,ptr
表示核心中OPEN表的指標,fd
表示
epoll_wait
等待事件發生
int epoll_wait(int epfd, struct epoll_event* evlist, int maxevents, int timeout);
epfd
是epoll
的檔案描述符evlist
是發生的事件佇列maxevents
是佇列最長的長度timeout
是事件限制
錯誤返回-1,超時返回0,成功返回事件的個數。
基本流程
以下是基本的流程,但不是真正的記憶體模型。一個epoll
有一個註冊事件的fd
的列表,列表中發生事件的fd會被儲存在epoll_wait
函式的佇列中。
epoll
的陷阱與內部的原理
給出一個典型的陷阱,借用之前的圖片:
A執行緒fd0指向一個系統資源,A執行緒的fd3是複製的fd0的。A執行緒fork出B執行緒,但是fd3複製的時候標記為close-on-exec
,那麼複製後的fd3就不能再表示之前的資源了。同時還可以看出,FD是程序間共享的,如果任意一個程序更改了FD,那麼其它程序的fd對應的FD也會發生更改,這在多程序模型中是需要注意的。
epoll
的內部基本機制(不含實現)
fd0和fd1是兩個已經開啟的檔案描述符,而且指向不同的INODE。之後系統呼叫epoll_create
建立新的INODE實體(等效在核心中建立一個FD實體),之後呼叫該函式的執行緒會獲取一個fd,假設是fd9,那麼此時fd9和程序A任然共享同一個Interest List,此時A也會響應fd9的事件。假設B程序又添加了fd8,那麼A也會響應fd8.