I/O multiplexing
: synchronous, Blocking I/O 형태의 멀티 프로세스 서버, 멀티 스레드 서버 모두 고성능 서버로는 부적합하다.
: 클라이언트마다 프로세스나 스레드를 할당하면 Context Switching 문제로 메모리적인 문제가 발생한다.
-> 멀티플렉싱 개념 필요- poll
■ 1_poll
: mkfifo fifo 파이프 만들기
: IPC 전용 메커니즘
: cat > myfifo로 리다이렉션#include <unistd.h> #include <stdio.h> #include <fcntl.h> int main() { char buff[100]; int ret; int fd = open("myfifo", O_RDWR); while (1) { ret = read(0, buff, sizeof buff); buff[ret] = '\0'; printf("Keyboard : %s\n", buff); ret = read(fd, buff, sizeof buff); buff[ret] = '\0'; printf("myfifo : %s\n", buff); } }
■ 2_poll#include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <poll.h> int main() { char buff[100]; int ret; int fd = open("myfifo", O_RDWR); // 비동기적으로 이벤트를 처리할 디스크립터 배열 struct pollfd fds[2]; while (1) { fds[0].fd = 0; fds[0].events = POLLIN; // 관심 있는 이벤트의 종류 // read : POLLIN // write : POLLOUT fds[1].fd = fd; fds[1].events = POLLIN; poll(fds, 2, -1); // 주의할 점 : 이벤트가 동시에 발생할 수 있으므로 // 절대 else 로 묶으면 안된다. if (fds[0].revents & POLLIN) { ret = read(0, buff, sizeof buff); buff[ret] = '\0'; printf("Keyboard : %s\n", buff); } if (fds[1].revents & POLLIN) { ret = read(fd, buff, sizeof buff); buff[ret] = '\0'; printf("myfifo : %s\n", buff); } } }
- select (참고: http://ozt88.tistory.com/21)
: select는 싱글스레드로 다중 I/O를 처리하는 멀티플렉싱 통지모델의 가장 대표적인 방법이다.
: 해당 파일 디스크립터가 I/O를 할 준비가 되었는지 알 수 있다면, 그 파일 디스크립터가 할당받은 커널 Buffer에 데이터를 복사해주기만 하면 된다. 이런 목적하에 통지모델은 파일디스크립터의 상황을 파악할 수 있게 하는 기능을 할 수 있어야한다. select는 많은 파일 디스크립터들을 한꺼번에 관찰하는 FD_SET 구조체를 사용하여 빠르고 간편하게 유저에게 파일 디스크립터의 상황을 알려준다.
: select를 사용해서 I/O의 상황을 알기 위해서는 프로세스가 커널에게 직접 상황 체크를 요청해야한다. 프로세스가 커널의 상황을 지속적으로 확인하고 그에 맞는 대응을 하는 형태로 구성되기 때문에 프로세스와 커널이 서로 동기화된 상태에서 정보를 주고 받는 형태로 볼 수 있다. 따라서 select의 통지형태를 동기형 통지방식이라 부를 수 있다. 그리고 select 그 자체는 I/O를 담당하지 않지만, 통지하는 함수의 호출방식이 timeout에 따라 non-blocking 또는 blocking 형태가 된다. timeout을 설정하지 않으면, 관찰 대상이 변경되지 않는 이상 반환되지 않으므로 blocking 함수가 되고, timeout이 설정되면 주어진 시간이 지나면 시간이 다되었다는 정보를 반환하므로 non-blocking 함수가 된다.#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/select.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> inline int max(int a, int b) { return a > b ? a : b; } int main() { int sock = socket(PF_INET, SOCK_STREAM, 0); struct sockaddr_in saddr = {0, }; saddr.sin_family = AF_INET; saddr.sin_port = htons(4000); saddr.sin_addr.s_addr = INADDR_ANY; // 0.0.0.0 int option = true; socklen_t optlen = sizeof(option); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &option, optlen); bind(sock, (struct sockaddr*)&saddr, sizeof saddr); listen(sock, 5); // fd_set : bit array fd_set socks; // source data fd_set readsocks; FD_ZERO(&socks); FD_SET(sock, &socks); int maxfds = sock; while (1) { readsocks = socks; int ret = select(maxfds + 1, &readsocks, 0, 0, 0); printf("ret : %d\n", ret); for (int i = 0 ; i < maxfds + 1 ; ++i) { if (FD_ISSET(i, &readsocks)) { if (i == sock) { struct sockaddr_in caddr = {0, }; socklen_t clen = sizeof(caddr); int csock = accept(sock, (struct sockaddr*)&caddr, &clen); char* cip = inet_ntoa(caddr.sin_addr); printf("Connected from %s\n", cip); // 새로운 연결을 등록해주어야 한다. FD_SET(csock, &socks); maxfds = max(maxfds, csock); } else { int csock = i; char buf[1024]; int n = read(csock, buf, sizeof buf); if (n <= 0) { printf("연결 종료!!\n"); close(csock); // 종료된 디스크립터를 등록 해지해야 한다. FD_CLR(csock, &socks); } else write(csock, buf, n); } } } #if 0 while (1) { char buf[1024]; int n = read(csock, buf, sizeof buf); if (n == 0) close(csock); else if (n == -1) close(csock); write(csock, buf, n); } #endif } close(sock); }
'Programing > Server Model' 카테고리의 다른 글
서버 모델 - 윈도우 소켓 프로그래밍 (0) | 2016.02.26 |
---|---|
서버 모델 - Event Driven I/O (0) | 2016.02.22 |
서버 모델 - Synchronous Blocking I/O (0) | 2016.02.22 |
서버 모델 - I/O 모델 개요 (0) | 2016.02.22 |
서버 모델 - 리눅스 소켓 프로그래밍(3) (0) | 2016.02.22 |