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
- 一起來寫web server 08 -- 多執行緒+非阻塞IO+epollWebServer執行緒
- IO多路複用與epoll機制淺析
- C IO複用select, epoll 簡單總結
- python網路程式設計——IO多路複用之epollPython程式設計
- Java 併發和多執行緒(一) Java併發性和多執行緒介紹[轉]Java執行緒
- Linux下套接字詳解(十)---epoll模式下的IO多路複用伺服器Linux模式伺服器
- 多執行緒與併發----讀寫鎖執行緒
- epoll+socket實現 socket併發 linux伺服器Linux伺服器
- Linux下Epoll簡介Linux
- 多執行緒與併發-----條件阻塞Condition的應用執行緒
- Web伺服器小專案(Linux / C / epoll)Web伺服器Linux
- 多執行緒與高併發(二)執行緒安全執行緒
- 併發與多執行緒之執行緒安全篇執行緒
- 多執行緒與高併發(一)多執行緒入門執行緒
- IO多路複用——深入淺出理解select、poll、epoll的實現
- IO模式 select、poll、epoll模式
- epoll 非阻塞IO 邊沿觸發模式模式
- 併發與多執行緒基礎執行緒
- 多執行緒與併發----Semaphere同步執行緒
- golang執行緒池在IO多路複用中的應用Golang執行緒
- Epoll 模型簡介模型
- java多執行緒與併發 - 併發工具類Java執行緒
- 執行緒併發執行緒安全介紹及java.util.concurrent包下類介紹執行緒Java
- java多執行緒與併發 - 執行緒池詳解Java執行緒
- MySQL併發複製系列二:多執行緒複製MySql執行緒
- iOS 多執行緒介紹iOS執行緒
- Epoll多路I/O複用技術
- 【多執行緒系列】CAS、AQS簡單介紹執行緒AQS
- 【多執行緒與高併發】- 執行緒基礎與狀態執行緒
- 多執行緒系列(十七) -執行緒組介紹執行緒
- 用多執行緒,實現併發,TCP執行緒TCP
- Java併發 之 執行緒組 ThreadGroup 介紹Java執行緒thread
- Linux IO模式及 select、poll、epoll詳解Linux模式
- MySQL Replication的複製執行緒介紹MySql執行緒
- Java高併發與多執行緒(二)-----執行緒的實現方式Java執行緒
- 怎麼處理WEB應用中的JAVA多執行緒問題(併發問題)WebJava執行緒
- Epoll程式設計-I/O多路複用程式設計