為什么Nginx的性能要比Apache高很多? 這得益于Nginx使用了最新的epoll(Linux 2.6內核)和kqueue(freebsd)網絡I/O模型,而Apache則使用的是傳統的select模型。
目前Linux下能夠承受高并發訪問的Squid、Memcached都采用的是epoll網絡I/O模型。
處理大量的連接的讀寫,Apache所采用的select網絡I/O模型非常低效。
下面用一個比喻來解析Apache采用的select模型和Nginx采用的epoll模型進行之間的區別:
假設你在大學讀書,住的宿舍樓有很多間房間,你的朋友要來找你。
select版宿管大媽就會帶著你的朋友挨個房間去找,直到找到你為止。
而epoll版宿管大媽會先記下每位同學的房間號,
你的朋友來時,只需告訴你的朋友你住在哪個房間即可,不用親自帶著你的朋友滿大樓找人。
如果來了10000個人,都要找自己住這棟樓的同學時,select版和epoll版宿管大媽,誰的效率更高,不言自明。
同理,在高并發服務器中,輪詢I/O是最耗時間的操作之一,select和epoll的性能誰的性能更高,同樣十分明了。
復制代碼 代碼如下:
epoll - I/O event notification facility
在linux的網絡編程中,很長的時間都在使用select來做事件觸發。
在linux新的內核中,有了一種替換它的機制,就是epoll。
相比于select,epoll最大的好處在于它不會隨著監聽fd數目的增長而降低效率。
因為在內核中的select實現中,它是采用輪詢來處理的,輪詢的fd數目越多,自然耗時越多。
并且,在linux/posix_types.h頭文件有這樣的聲明:
#define __FD_SETSIZE 1024
表示select最多同時監聽1024個fd,當然,可以通過修改頭文件再重編譯內核來擴大這個數目,但這似乎并不治本。
epoll的接口非常簡單,一共就三個函數:
1. int epoll_create(int size);
創建一個epoll的句柄,size用來告訴內核這個監聽的數目一共有多大。
這個參數不同于select()中的第一個參數,給出最大監聽的fd+1的值。
需要注意的是,當創建好epoll句柄后,它就是會占用一個fd值,在linux下如果查看/proc/進程id/fd/,
是能夠看到這個fd的,所以在使用完epoll后,必須調用close()關閉,否則可能導致fd被耗盡。
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注冊函數,它不同與select()是在監聽事件時告訴內核要監聽什么類型的事件,
而是在這里先注冊要監聽的事件類型。第一個參數是epoll_create()的返回值,
第二個參數表示動作,用三個宏來表示:
EPOLL_CTL_ADD:注冊新的fd到epfd中;
EPOLL_CTL_MOD:修改已經注冊的fd的監聽事件;
EPOLL_CTL_DEL:從epfd中刪除一個fd;
第三個參數是需要監聽的fd,第四個參數是告訴內核需要監聽什么事,struct epoll_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 events */
epoll_data_t data; /* User data variable */
};
events可以是以下幾個宏的集合:
EPOLLIN :表示對應的文件描述符可以讀(包括對端SOCKET正常關閉);
EPOLLOUT:表示對應的文件描述符可以寫;
EPOLLPRI:表示對應的文件描述符有緊急的數據可讀(這里應該表示有帶外數據到來);
EPOLLERR:表示對應的文件描述符發生錯誤;
EPOLLHUP:表示對應的文件描述符被掛斷;
新聞熱點
疑難解答