Linux IO 複用之 epoll 介紹與 epoll 應用(編寫單執行緒多併發的 Web 伺服器)
一、Linux epoll 介紹
epoll是Linux核心為處理大批量檔案描述符而作了改進的poll,是Linux下多路複用IO介面select/poll的增強版本,它能顯著提高程式在大量併發連線中只有少量活躍的情況下的系統CPU利用率(百度百科)。
-
epoll
- The epoll API performs a similar task to poll: monitoring multiple file descriptors to see if I/O is possible on any of them. The epoll API can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of watched file descriptors.
epoll 執行與 poll 相似的任務:通過監控多個檔案描述符來檢視任何一個(被監控的)檔案描述符是否可能有IO操作。epoll API 要麼通過edge-triggered要麼通過level-triggered介面來使用並且可以很好地擴充套件到大量被監控的檔案描述符(可用於實現Web伺服器的原因)。
- The epoll API performs a similar task to poll: monitoring multiple file descriptors to see if I/O is possible on any of them. The epoll API can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of watched file descriptors.
-
System calls
-
epoll_create(): creates an epoll instance and returns a file descriptor referring to that instance(建立一個epoll例項並且返回一個指代該例項的檔案描述符).
- int epoll_create(int size)
- int epoll_create(int size)
-
epoll_ctl(): Interest in particular file descriptors is then registered via epoll_ctl. The set of file descriptors currently registered on an epoll instance is sometimes called an epoll set.
感興趣的特定檔案描述符通過epoll_ctl註冊到 epoll 例項。
當前註冊到一個 epoll 例項的檔案描述符集合有時被稱作 epoll 集合。- int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
- epfd: epoll 檔案描述符
- op: 通過指定op來新增(EPOLL_CTL_ADD)/修改(EPOLL_CTL_MOD)/刪除(EPOLL_CTL_DEL)需要偵聽的檔案描述符及其事件
- fd: 檔案描述符
- event: 連結到檔案描述符的事件,struct epoll_event 定義如下
- int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
-
epoll_wait: waits for I/O events(等待IO事件)
- int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
- epfd: epoll 檔案描述符
- events: The memory area pointed to by events will contain the events that will be available for the caller.
- maxevents: epoll_wait 返回的最大事件數量
- timeout: timeout為0的時候表示馬上返回,為-1的時候表示一直等下去,直到有事件發生,為正整數(t)的時候表示等待t毫秒。
- int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
-
二、單執行緒多併發的 Web 伺服器編寫
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/epoll.h>
#define MAX_EVENTS 10
// 處理get請求
void handle_get(int fd, char path[]){
char buf[1024];
int n;
int filefd;
if((filefd=open(path+1, O_RDONLY))==-1){
write(fd, "HTTP/1.0 404 Not Found\r\n\r\n", 26);
return;
}
write(fd, "HTTP/1.0 200 OK\r\n\r\n", 19);
while((n=read(filefd, buf, sizeof(buf)))>0){
write(fd, buf, n);
}
close(filefd);
}
// 非阻塞設定
void setnonblocking(int fd) {
int opts;
opts=fcntl(fd, F_GETFL);
if(opts<0) {
perror("fcntl(sock,GETFL)");
exit(1);
}
opts = opts|O_NONBLOCK;
if(fcntl(fd, F_SETFL, opts)<0) {
perror("fcntl(sock,SETFL,opts)");
exit(1);
}
}
main(int ac, char *av[]){
struct sockaddr_in addr;
char buf[1024];
int i;
int n;
char cmd[512];
char path[512];
int sockfd;
if(ac<2){
printf("Usage cmd port_num\n");
exit(1);
}
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;
signal(SIGPIPE, SIG_IGN);
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(av[1]));
addr.sin_addr.s_addr = INADDR_ANY;
if(bind(listen_sock, (const struct sockaddr *)&addr, sizeof(struct sockaddr_in))==-1){
perror("cannot bind");
exit(1);
}
listen(listen_sock, 1);
// 建立 epoll 例項
epollfd = epoll_create(10);
if (epollfd == -1) {
perror("epoll_create");
exit(EXIT_FAILURE);
}
// 註冊 socket 檔案描述符
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
while(1){
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n) {
// 主socket通過accept獲取客戶端與伺服器之間的socket檔案描述符並註冊到epoll例項
if (events[n].data.fd == listen_sock) {
// 註冊客戶端與伺服器之間的socket檔案描述符
conn_sock = accept(listen_sock, NULL, NULL);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET; // 處理的事件型別為EPOLLIN
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else { // 客戶端與伺服器之間的socket檔案描述符用於讀取客戶端GET請求(EPOLLIN)
sockfd=events[n].data.fd;
if ((i = read(sockfd, buf, sizeof(buf))) <= 0) {
close(sockfd);
} else {
sscanf(buf, "%s%s", cmd, path);
if(strcmp(cmd, "GET")==0){
handle_get(sockfd, path);
}
close(sockfd); //close the sockfd
}
}
}
}
}
相關文章
- epoll程式設計,單epoll+執行緒池?執行緒池+epoll?nginx實現高併發的原理?程式設計執行緒Nginx
- C IO複用select, epoll 簡單總結
- IO多路複用與epoll機制淺析
- IO多路複用(一)– Select、Poll、Epoll
- 細談 Linux 中的多路複用epollLinux
- epoll+socket實現 socket併發 linux伺服器Linux伺服器
- 多執行緒與併發----讀寫鎖執行緒
- Web伺服器小專案(Linux / C / epoll)Web伺服器Linux
- 多執行緒與併發-----條件阻塞Condition的應用執行緒
- 一文說透IO多路複用select/poll/epoll
- IO多路複用——深入淺出理解select、poll、epoll的實現
- IO模式 select、poll、epoll模式
- 多執行緒與高併發(二)執行緒安全執行緒
- 併發與多執行緒之執行緒安全篇執行緒
- 多執行緒與高併發(一)多執行緒入門執行緒
- golang執行緒池在IO多路複用中的應用Golang執行緒
- iOS 多執行緒介紹iOS執行緒
- epoll 非阻塞IO 邊沿觸發模式模式
- 【多執行緒系列】CAS、AQS簡單介紹執行緒AQS
- Java併發 之 執行緒組 ThreadGroup 介紹Java執行緒thread
- 多執行緒與併發----Semaphere同步執行緒
- 併發與多執行緒基礎執行緒
- java多執行緒與併發 - 執行緒池詳解Java執行緒
- java多執行緒與併發 - 併發工具類Java執行緒
- 多執行緒系列(十七) -執行緒組介紹執行緒
- 【多執行緒與高併發】- 執行緒基礎與狀態執行緒
- 編寫執行緒安全的JSP應用程式執行緒JS
- 網路程式設計學習——Linux epoll多路複用模型程式設計Linux模型
- 多路I/O複用:select、poll、epoll(二)
- Java多執行緒與併發之ThreadLocalJava執行緒thread
- Java高併發與多執行緒(二)-----執行緒的實現方式Java執行緒
- 用多執行緒,實現併發,TCP執行緒TCP
- 多執行緒高併發程式設計(9) -- CopyOnWrite寫入時複製執行緒程式設計
- [作業系統]阻塞io 非阻塞io Epoll作業系統
- JAVA多執行緒併發Java執行緒
- OS開發基礎——多執行緒的簡單應用執行緒
- epoll使用與原理
- 多執行緒併發篇——如何停止執行緒執行緒