오늘 my httpd 의 연결 처리 모드 를 select 에서 epoll 로 바 꿉 니 다.

Linux 2.6 커 널 은 epoll 을 완전히 지원 합 니 다. epoll 의 IO 효율 은 FD 수량 이 증가 하지 않 고 선형 으로 떨 어 집 니 다. 전통 적 인 select / poll 은 호출 할 때마다 모든 집합 을 선형 으로 스 캔 하여 효율 이 선형 으로 떨 어 집 니 다.커 널 구현 중 epoll 은 모든 fd 위의 callback 함수 에 따라 이 루어 집 니 다."활성 화 된" socket 만 콜 백 함 수 를 주동 적 으로 호출 할 수 있 으 며, 다른 idle 상태 socket 은 그렇지 않 습 니 다.모든 socket 이 기본적으로 활발 하 다 면 - 예 를 들 어 고속 LAN 환경 은 epoll 을 너무 많이 사용 하면 효율 이 약간 떨어진다.그러나 idle connections 를 사용 하여 WAN 환경 을 모 의 하면 epoll 의 효율 은 select / poll 보다 훨씬 높다.
 
poll 의 실행 은 세 부분 으로 나 뉜 다. 1. 사용자 가 들 어 온 pollfd 배열 을 커 널 공간 으로 복사 합 니 다. 복사 작업 은 배열 의 길이 와 관련 되 기 때문에 시간 적 으로 O (n) 작업 입 니 다. 각 파일 설명자 가 대응 하 는 장치 의 상 태 를 조회 합 니 다. 이 장치 가 준비 되 지 않 으 면 이 장치 의 대기 열 에 하 나 를 추가 하고 다음 장치 의 상 태 를 계속 조회 합 니 다.모든 장 치 를 조회 한 후 장치 가 준비 되 지 않 으 면 현재 프로 세 스 를 걸 고 기 다 려 야 합 니 다. 장치 가 준비 되 거나 시간 이 초 과 될 때 까지 기 다 려 야 합 니 다.장치 가 준 비 된 후에 프로 세 스 가 계속 실행 되 라 는 통 지 를 받 았 습 니 다. 이 때 모든 장 치 를 다시 옮 겨 다 니 며 준 비 된 장 치 를 찾 습 니 다.이 단 계 는 모든 장 치 를 두 번 이나 옮 겨 다 니 기 때문에 시간 복잡 도 역시 O (n) 이 고 이 안 에는 대기 시간 이 포함 되 지 않 습 니 다.  3. 획득 한 데 이 터 를 사용자 공간 으로 전송 하고 메모리 방출 과 대기 열 박리 등 사후 작업 을 수행 하 며 사용자 공간 에 데 이 터 를 복사 하 는 것 과 대기 열 박리 등 작업 을 하 는 시간 복잡 도 는 O (n) 입 니 다.epoll 에 사용 되 는 모든 함 수 는 헤더 파일 sys / epoll. h 에서 설명 합 니 다. 아래 에 사용 되 는 데이터 구조 와 함 수 를 간략하게 설명 합 니 다. 사용 되 는 데이터 구조 typedef union epolldata {                 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 */         }; 구조 체 epoll event 는 관심 있 는 이벤트 와 리 턴 을 등록 하 는 데 사 용 됩 니 다. 이 중 epoll data 연합 체 는 트리거 이벤트 의 한 파일 설명자 와 관련 된 데 이 터 를 저장 하 는 데 사 용 됩 니 다. 예 를 들 어 하나의 client 가 서버 에 연결 되 어 있 으 며, 서버 는 accept 함 수 를 호출 하여 이 client 에 대응 하 는 socket 파일 설명 자 를 얻 을 수 있 습 니 다. 이 파일 설명 자 를 사용 할 수 있 습 니 다.이 파일 설명자 에서 다음 읽 기와 쓰기 동작 을 할 수 있 도록 epoll data 의 fd 필드 에 부 여 됩 니 다. epoll event 구조 체 의 events 필드 는 관심 을 표시 합 니 다.
 
출처 장 심 붕 의 자바 아이 블 로그
학습 노트
http://zsp.iteye.com/blog/146850
 
- - 주: 위의 내용 은 모두 발췌 문 입 니 다. -
 
이전 select 모드 의 코드
/* server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>

#include "wrap.h"
#include "httpprocesser.h"

#define MAXLINE 800
#define SERV_PORT 7080
#define BACKLOG 200

int main(int argc, char **argv)
{
	int i, maxi, maxfd, listenfd, connfd, sockfd;
	int nready, client[FD_SETSIZE];
	ssize_t n;
	fd_set rset, allset;
	char buf[MAXLINE];
	char str[INET_ADDRSTRLEN];
	socklen_t cliaddr_len;
	struct sockaddr_in	cliaddr, servaddr;

	listenfd = my_socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family      = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port        = htons(SERV_PORT);

	my_bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	my_listen(listenfd, BACKLOG);

	maxfd = listenfd;		/* initialize */
	maxi = -1;			/* index into client[] array */
	for (i = 0; i < FD_SETSIZE; i++)
		client[i] = -1;	/* -1 indicates available entry */
	FD_ZERO(&allset);
	FD_SET(listenfd, &allset);

	for ( ; ; ) {
		rset = allset;	/* structure assignment */
		nready = select(maxfd+1, &rset, NULL, NULL, NULL);
		if (nready < 0)
			perr_exit("select error");

		if (FD_ISSET(listenfd, &rset)) { /* new client connection */
			cliaddr_len = sizeof(cliaddr);
			connfd = my_accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);

			printf("received from %s at PORT %d
", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port)); for (i = 0; i < FD_SETSIZE; i++) if (client[i] < 0) { client[i] = connfd; /* save descriptor */ break; } if (i == FD_SETSIZE) { fputs("too many clients
", stderr); exit(1); } FD_SET(connfd, &allset); /* add new descriptor to set */ if (connfd > maxfd) maxfd = connfd; /* for select */ if (i > maxi) maxi = i; /* max index in client[] array */ if (--nready == 0) continue; /* no more readable descriptors */ } for (i = 0; i <= maxi; i++) { /* check all clients for data */ if ( (sockfd = client[i]) < 0) continue; if (FD_ISSET(sockfd, &rset)) { if ( (n = my_read(sockfd, buf, MAXLINE)) > 0) { struct http_msg msg; parse_msg(buf, &msg); printf("%s
", msg.response); my_write(sockfd, buf, n); } /* connection closed by client */ my_close(sockfd); FD_CLR(sockfd, &allset); client[i] = -1; if (--nready == 0) break; /* no more readable descriptors */ } } } }

 현재 사용 하고 있 는 epoll 모드 의 코드
이 부분의 코드 는 http://blog.csdn.net/mote_li/archive/2004/12/08/209450.aspx pthread 라 이브 러 리 를 기반 으로 한 작업 대기 열 방식 의 스 레 드 풀 도 추가 되 었 습 니 다.
 
#ifndef __myhttpd_h
#define __myhttpd_h

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <strings.h>

#include <pthread.h>

#include "wrap.h"
#include "task.h"


#define OPEN_MAX 100
#define LISTENQ 20
#define INFTIM 1000

#define LOCAL_IP "127.0.0.1" /*                 */
#define SERV_PORT 5555

extern pthread_mutex_t mutex; /*        */
extern pthread_cond_t cond1; /*          */

extern struct task *readhead ,
                                *readtail ,
                                *writehead ;
extern struct epoll_event ev, events[20];

extern int epfd;


void setnonblocking(int sock)
{
  int opts;

  opts = fcntl(sock, F_GETFL);
  if(opts<0)
   {
     perror("fcntl(sock,GETFL)");
     exit(1); /*          ,              ,             */
   }

  if(fcntl(sock, F_SETFL, opts | O_NONBLOCK)<0)
   {
    perror("fcntl(sock,SETFL,opts)");
    exit(1);
   }
}

int main()
{
  int i, maxi, listenfd, connfd, sockfd, nfds;
  socklen_t clilen;
  pthread_t tid1,tid2;
  struct task *new_task=NULL;
  struct user_data *rdata=NULL;

  readhead = readtail = writehead = NULL;

  /* initialize the thread pool */
  pthread_mutex_init(&mutex, NULL);
  pthread_cond_init(&cond1, NULL);

   /*     ,           ,       .            ! */
  pthread_create(&tid1, NULL, readtask, NULL);
  pthread_create(&tid2, NULL, readtask, NULL);

   /*       accept epoll        
    *       
    *

Create a new epoll file descriptor by requesting the kernel allocate an event backing store dimensioned
[n.   ,   ,  ( ),  ( ),  ] for size descriptors.
 The size is not the maximum size of the backing store but just a hint to the kernel about
how to dimension internal structures.
The returned file descriptor will be used for all the subsequent calls to the epoll interface.
 The file descriptor returned by epoll_create must be closed by using POSIX::close.

When successful, epoll_create returns a positive integer identifying the descriptor. When an error occurs,
 epoll_create returns -1 and errno is set appropriately.

    *
    */
  epfd = epoll_create(256);

  struct sockaddr_in clientaddr;
  struct sockaddr_in serveraddr;

  listenfd = my_socket(AF_INET, SOCK_STREAM, 0);

  // socket        

  setnonblocking(listenfd);

   //                 

  ev.data.fd = listenfd;

   //          

  ev.events = EPOLLIN | EPOLLET;

  /*  epoll  

Control an epoll descriptor, $epfd, by requesting the operation op be performed on the target file descriptor, fd.

$epfd is an epoll descriptor returned from epoll_create.
$op is one of EPOLL_CTL_ADD, EPOLL_CTL_MOD or EPOLL_CTL_DEL.
$fd is the file desciptor to be watched.
$eventmask is a bitmask of events defined by EPOLLIN, EPOLLOUT, etc.

When successful, epoll_ctl returns 0. When an error occurs, epoll_ctl returns -1 and errno is set appropriately.
   */
  epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

  bzero(&serveraddr, sizeof(serveraddr));
  serveraddr.sin_family = AF_INET;

  char *local_addr = LOCAL_IP;
  inet_aton(local_addr, &(serveraddr.sin_addr));

  serveraddr.sin_port = htons(SERV_PORT);
  my_bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
  my_listen(listenfd, LISTENQ);

  maxi = 0;
  for ( ; ; )
   {
     /*  epoll     

Wait for events on the epoll file descriptor $epfd.

$epfd is an epoll descriptor returned from epoll_create.
$maxevents is an integer specifying the maximum number of events to be returned.
$timeout is a timeout, in milliseconds

When successful, epoll_wait returns a reference to an array of events. Each event is a two element array,
the first element being the file descriptor which triggered the event,
and the second is the mask of event types triggered.
For example, if epoll_wait returned the following data structure:

     */
     nfds = epoll_wait(epfd, events, 20, 500);

      //          

     for(i = 0; i < nfds; ++i)
      {
       if(events[i].data.fd == listenfd)
         {
         connfd = my_accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);
         if(connfd < 0)
           {
          perror("connfd<0");
          exit(1);
           }

         setnonblocking(connfd);

         char *str = inet_ntoa(clientaddr.sin_addr);

         printf("connect_from >> %s 
", str); ev.data.fd = connfd; // ev.events = EPOLLIN | EPOLLET; // // ev epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev); } else if (events[i].events & EPOLLIN) { printf("reading!
"); if ((sockfd = events[i].data.fd) < 0) continue; new_task = (struct task *)malloc(sizeof(struct task)); new_task->fd = sockfd; new_task->next = NULL; pthread_mutex_lock(&mutex); // if(readhead == NULL) { readhead = new_task; readtail = new_task; } else { readtail->next = new_task; readtail = new_task; } // cond1 pthread_cond_broadcast(&cond1); pthread_mutex_unlock(&mutex); } else if (events[i].events & EPOLLOUT) { rdata = (struct user_data *)events[i].data.ptr; sockfd = rdata->fd; printf("thread.%u Write data fd.%d len.%d data.%s
" , (uint32_t)pthread_self(), sockfd, rdata->n_size, rdata->line); my_write(sockfd, rdata->line, rdata->n_size); my_close(sockfd); free(rdata); ev.data.fd = sockfd; // ev.events = EPOLLIN | EPOLLET; // // sockfd EPOLIN epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev); } } } } #endif

좋은 웹페이지 즐겨찾기