1. 程式人生 > >深入理解Linux/Unix檔案描述符和epoll

深入理解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的fd
  • op表示操作的型別
    • 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);
  • epfdepoll的檔案描述符
  • 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.

參考資料