TL;DR
- I/O multiplexing์ ํ ์ค๋ ๋๊ฐ ์ฌ๋ฌ fd๋ฅผ ๋์์ ๊ฐ์ํ๋ค ์ค๋น๋ ๊ฒ๋ง ์ฒ๋ฆฌํ๋ ๊ธฐ๋ฒ โ ์ฐ๊ฒฐ๋น ์ค๋ ๋์ ํ์ฅ ํ๊ณ๋ฅผ ๋๋ ๋ฐฉ๋ฒ
- selectโpollโepoll์ โ๋งค ํธ์ถ๋ง๋ค ์ ์ฒด fd๋ฅผ ์ปค๋์ ๋๊ธฐ๊ณ O(n)๋ก ์ค์บโํ๋ ๋น์ฉ์, epoll์ด โ๊ด์ฌ ๋ชฉ๋ก ํ ๋ฒ ๋ฑ๋ก + ์ค๋น๋ ๊ฒ๋ง ๋ฐํโ์ผ๋ก ์์ค ์งํ
- epoll์ ์ปค๋ ์๋ฃ๊ตฌ์กฐ์ edge-triggered ๋์์ ์ดํดํ๋ฉด event loopยทasyncioยทnginx์ ๋ฐ๋ฐํ์ด ์กํ
AI-assisted
1. ์ ํ์ํ๊ฐ โ ์ฐ๊ฒฐ๋น ์ค๋ ๋์ ํ๊ณ
์๋ฒ๊ฐ ์๋ง ๊ฐ์ ์ฐ๊ฒฐ์ ๋์์ ๋ค๋ค์ผ ํ๋ค๊ณ ํ์. ๊ฐ์ฅ ๋จ์ํ ๋ฐฉ๋ฒ์ ์ฐ๊ฒฐ ํ๋์ ์ค๋ ๋ ํ๋๋ฅผ ๋ฐฐ์ ํ๊ณ ๊ฐ ์ค๋ ๋๊ฐ ์๊ธฐ ์์ผ์ read๋ก ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ด๋ค. ์ฝ๋๋ ์ง๊ด์ ์ด์ง๋ง ๊ท๋ชจ๊ฐ ์ปค์ง๋ฉด ๋ฌด๋์ง๋ค.
- ์ค๋ ๋๋ง๋ค ์คํ(๋๋ต ์ MB)์ด ๋ถ์ด ๋ฉ๋ชจ๋ฆฌ๊ฐ ๊ธ์ธ ๋ฐ๋ฅ๋๋ค
- ์ค๋ ๋ ์์ฒ ๊ฐ๋ฅผ ์ค์ผ์ค๋งํ๋ฉด ์ผํ๋ ์๊ฐ๋ณด๋ค context switch ํ๋ ์๊ฐ์ด ๋์ด๋๋ค
- ๋๋ถ๋ถ์ ์ฐ๊ฒฐ์ ์ฌ์ค ๋๊ณ ์๋ค. ์ฑํ ์๋ฒ๋ผ๋ฉด ๊ฐ ์ฌ์ฉ์๋ ๋๊ฐ ์ ๋ ฅ์ ๊ธฐ๋ค๋ฆฌ๋ ์ค์ด๋ค
์ฌ๊ธฐ์ ๋ญ๋น๊ฐ ๋ณด์ธ๋ค. ๊ฑฐ์ ๋๊ณ ์๋ ์ฐ๊ฒฐ๋ง๋ค ์ค๋ ๋ ํ๋๋ฅผ ํต์งธ๋ก ๋ฌถ์ด๋๋ ๊ฒ์ด๋ค. ์ด ๋ฌธ์ ๊ฐ ์ ๋ช ํ C10K(์ฐ๊ฒฐ 1๋ง ๊ฐ๋ฅผ ํ ์๋ฒ๊ฐ ๊ฐ๋นํ๊ธฐ)๋ค.
ํด๋ฒ์ ๋ฐฉํฅ์ ๋ฐ๋๋ค. ์ค๋ ๋ ํ๋๊ฐ ๋ชจ๋ ์ฐ๊ฒฐ์ ๊ฐ์ํ๋ค๊ฐ, ์ค์ ๋ก ๋ฐ์ดํฐ๊ฐ ์จ ์ฐ๊ฒฐ๋ง ๊ณจ๋ผ ์ฒ๋ฆฌํ๋ค. ์ด๊ฒ์ด I/O multiplexing์ด๊ณ select์์ poll, epoll๋ก ์ด์ด์ง๋ฉฐ ๋ฐ์ ํด์จ ์ธ ์ธ๋์ ๋๊ตฌ๋ค.
2. fd์ blocking โ โ์ค๋น๋๋คโ๊ฐ ๋ฌด์์ธ๊ฐ
๋ฆฌ๋
์ค์์ ์์ผยทํ์ผยทํ์ดํ๋ ๋ชจ๋ ํ์ผ ๋์คํฌ๋ฆฝํฐ(fd)๋ก ๋ค๋ค์ง๋ค.
์์ผ fd๊ฐ ์ฝ์ ์ค๋น๋จ์ด๋ ๊ทธ ์์ ๋ฒํผ์ ๋ฐ์ดํฐ๊ฐ ๋์ฐฉํด ์๋ค๋ ๋ป์ด๊ณ ์ธ ์ค๋น๋จ์ด๋ ์ก์ ๋ฒํผ์ ์ฌ์ ๊ฐ ์๋ค๋ ๋ป์ด๋ค.
๋ฌธ์ ๋ ๊ธฐ๋ณธ read๊ฐ blocking์ด๋ผ๋ ๋ฐ ์๋ค. ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด ๊ทธ ์ค๋ ๋๋ฅผ ์ฌ์(Blocked) ๋์ฐฉํ ๋๊น์ง ์ธ์ด๋ค.
๊ทธ๋์ blocking read ํ๋๋ก๋ ํ ๋ฒ์ fd ํ๋๋ง ๊ธฐ๋ค๋ฆด ์ ์๋ค. ์ฌ๋ฌ ์ฐ๊ฒฐ์ ํ ์ค๋ ๋๋ก ๊ธฐ๋ค๋ฆฌ๋ ค๋ฉด ๋ค๋ฅธ ์๋จ์ด ํ์ํ๋ค.
fd์ O_NONBLOCK ํ๋๊ทธ๋ฅผ ์ผ๋ฉด(์ด ๋ ์ง์ ํ๊ฑฐ๋ ์ด๋ฏธ ์ด๋ฆฐ fd์ fctnl์ด๋ผ๋ ์ด๋ฏธ ์ด๋ฆฐ fd์ ์์ฑ์ ๋ฐ๊พธ๋ syscall ์ ํ ๊ธ) ๊ทธ fd์ I/O๊ฐ blockingํ์ง ์๊ฒ ๋๋ค.
์ด fd์ read๋ฅผ ๋ถ๋ฅด๋ฉด ์ฝ์ ๋ฐ์ดํฐ๊ฐ ์์ ๋ ๋ฐ๋ก ๊ฐ์ ธ์ค์ง๋ง ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด ์ค๋ ๋๋ฅผ ์ฌ์ฐ์ง ์๊ณ ์ฆ์ EAGAIN1์ ๋๋ ค์ฃผ๊ณ ๋์์จ๋ค.
๊ทธ๋ผ ๋ชจ๋ fd๋ฅผ ๋๋ฉฐ non-blocking read๋ฅผ ๋ฐ๋ณตํ๋ฉด ๋์ง ์์๊น ์ถ์ง๋ง ๊ทธ๊ฑด ์ค๋น๋์ง ์์ fd๊น์ง ๊ณ์ ๋๋ฌป๋๋ผ ์ฝ์ด๋ฅผ ํ์ฐ๋ busy polling์ด ๋๋ค.
I/O multiplexing์ ๊ทธ ์ค๊ฐ์ด๋ค. ์ปค๋์๊ฒ โ์ด fd๋ค์ ๊ฐ์ํ๋ค๊ฐ ์ค๋น๋ ๊ฒ ์๊ธฐ๋ฉด ์๋ ค๋ฌ๋ผโ๊ณ ๋งก๊ธฐ๊ณ ์ค๋น๋ ๊ฒ ์์ผ๋ฉด ์ค๋ ๋๋ ์ ๋ ๋ค. ๋๋ฌป๋ ๋ญ๋น๋ ์๊ณ ์ค๋ ๋ ํ๋๋ก ์๋ง์ fd๋ฅผ ๊ธฐ๋ค๋ฆฐ๋ค.
3. I/O multiplexing์ด๋ผ๋ ๋ฐ์
ํต์ฌ์ fd ์ฌ๋ฌ ๊ฐ๋ฅผ ์ธ์๋ก ๋ฐ์, ๊ทธ์ค ํ๋๋ผ๋ ์ค๋น๋ ๋๊น์ง blockingํ๋ค๊ฐ, ์ค๋น๋ ๊ฒ๋ค์ ์๋ ค์ฃผ๋ ์์คํ ์ฝ ํ๋๋ค.
- ์ค๋น๋ fd๊ฐ ์์ผ๋ฉด ํธ์ถ ์ค๋ ๋๋ ์ ๋ ๋ค(Blocked): CPU๋ฅผ ์ฐ์ง ์๋๋ค
- ์ด๋ค fd๊ฐ ์ค๋น๋๋ฉด ์ปค๋์ด ์ค๋ ๋๋ฅผ ๊นจ์ด๋ค
- ์ค๋ ๋๋ ์ค๋น๋ fd๋ง ์ฒ๋ฆฌํ๊ณ ๋ค์ ํธ์ถ๋ก ๋์๊ฐ ์ ๋ ๋ค
์ด ์ํ ํ๋๊ฐ event loop์ ํต์ฌ ๋์์ด๋ค. selectยทpollยทepoll์ด ๋ฐ๋ก ์ด ์ํ์ ๊ตฌํํ๋ฉฐ select์์ epoll๋ก ๊ฐ์๋ก fd๋ฅผ ๋ง์ด ๊ฐ์ํ ๋์ ๋น์ฉ์ด ์ค์ด๋ ๋ค.
4. select() โ ์ต์ด์ ํด๋ฒ๊ณผ ์ธ ๊ฐ์ง ํ๊ณ
๊ฐ์ฅ ์ค๋๋ ํ์ค์ด๋ค. fd ์งํฉ์ ๋นํธ๋ง์คํฌ(fd_set)๋ก ํํํ๊ณ ๋งคํฌ๋ก๋ก ์ผ๊ณ ๋๋ค.
#include <sys/select.h>
fd_set allset, rset; // allset = ๊ฐ์ํ fd ์ ์ฒด ๋ชฉ๋ก, rset = select์ ๋๊ธธ ์์
์ฉ ์ฌ๋ณธ
FD_ZERO(&allset); // ๋ชฉ๋ก์ ๋น์ด๋ค (๋นํธ๋ฅผ ์ ๋ถ 0์ผ๋ก)
FD_SET(listen_fd, &allset); // listen ์์ผ์ ๊ฐ์ ๋ชฉ๋ก์ ์ถ๊ฐ (์ ์ฐ๊ฒฐ์ด ์ค๋์ง ์ง์ผ๋ด)
int maxfd = listen_fd; // ๋ฑ๋ก๋ fd ์ค ๊ฐ์ฅ ํฐ ๋ฒํธ (select์ ๋๊ธธ ๋ฒ์)
for (;;) { // ์๋ฒ ๋ฉ์ธ ๋ฃจํ
rset = allset; // ์ฌ๋ณธ์ ๋ง๋ ๋ค โ select๊ฐ rset์ "์ค๋น๋ fd๋ง ๋จ๊ธด ์งํฉ"์ผ๋ก ๋ฎ์ด์ฐ๋ฏ๋ก ์๋ณธ(allset)์ ๋ณด์กด
select(maxfd + 1, &rset, NULL, NULL, NULL); // ์ปค๋์ ๊ฐ์๋ฅผ ๋งก๊ธด๋ค. ํ๋๋ผ๋ ์ค๋น๋ ๋๊น์ง ์ด ์ค๋ ๋๋ blocking(์ ๋ฆ)
for (int fd = 0; fd <= maxfd; fd++) { // ์ด๋ค fd๊ฐ ์ค๋น๋๋์ง ๋ชจ๋ฅด๋ 0๋ฒ๋ถํฐ maxfd๊น์ง ์ ๋ถ ํ์ธ (O(n) ์ค์บ)
if (!FD_ISSET(fd, &rset)) continue; // ์ด fd๊ฐ ์ค๋น ๋ชฉ๋ก์ ์์ผ๋ฉด ๊ฑด๋๋
if (fd == listen_fd) { // ์ค๋น๋ ๊ฒ listen ์์ผ์ด๋ฉด = ์ ์ฐ๊ฒฐ ์์ฒญ์ด ๋์ฐฉํ๋ค๋ ๋ป
int conn = accept(listen_fd, NULL, NULL); // ์ฐ๊ฒฐ์ ์๋ฝํด ํด๋ผ์ด์ธํธ์ฉ fd(conn) ์์ฑ
FD_SET(conn, &allset); // ์ ์ฐ๊ฒฐ๋ ๊ฐ์ ๋ชฉ๋ก์ ์ถ๊ฐ (๋ค์ ๋ฃจํ๋ถํฐ ์ง์ผ๋ด)
if (conn > maxfd) maxfd = conn; // conn์ด ๋ ํฌ๋ฉด ๋ฒ์(maxfd)๋ฅผ ๋ํ๋ค
} else { // ์ค๋น๋ ๊ฒ ๊ธฐ์กด ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ์ด๋ฉด = ๋ฐ์ดํฐ๊ฐ ๋์ฐฉํ๋ค๋ ๋ป
char buf[1024]; // ์ฝ์ด ๋ค์ผ ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๋ฒํผ
int r = read(fd, buf, sizeof buf); // ์ด๋ฏธ ์ค๋น๋ ์์ผ์ด๋ผ ๋ฐ๋ก ์ฝํ๋ค
if (r <= 0) { close(fd); FD_CLR(fd, &allset); } // 0=์๋๊ฐ ์ฐ๊ฒฐ์ ๋ซ์, ์์=์๋ฌ โ fd ๋ซ๊ณ ๊ฐ์ ๋ชฉ๋ก์์ ์ ๊ฑฐ
else write(fd, buf, r); // ์ฝ์ ๋งํผ ๊ทธ๋๋ก ๋๋ ค๋ณด๋ (์์ฝ ์๋ฒ)
}
}
}๊ฐ์ํ fd๋ฅผ fd_set์ ์ฑ์ฐ๊ณ select๋ฅผ ๋ถ๋ฅธ๋ค. ์ปค๋์ ํ๋๋ผ๋ ์ค๋น๋ ๋๊น์ง ์ฌ์ ๋ค๊ฐ, ์ค๋น๋ fd๋ค์ rset์ ํ์ํด ๋๋ ค์ค๋ค. ๋์์ค๋ฉด FD_ISSET์ผ๋ก ์ ์ฒด๋ฅผ ํ์ด ์ค๋น๋ fd๋ฅผ ์ฐพ์ ์ฒ๋ฆฌํ๋ค.
๋์์ ํ์ง๋ง fd๊ฐ ๋ง์์ง๋ฉด ์ธ ๊ฐ์ง๊ฐ ๋ฐ๋ชฉ์ ์ก๋๋ค.
- fd ๊ฐ์ ์ํ:
fd_set์ ํฌ๊ธฐ๊ฐ ๊ณ ์ ๋ ๋นํธ๋ง์คํฌ๋ผFD_SETSIZE(๋ณดํต 1024)๋ฅผ ๋๋ fd๋ฅผ ๊ฐ์ํ ์ ์๋ค. - ๋งค ํธ์ถ๋ง๋ค ์ ์ฒด ๋ณต์ฌ:
select๊ฐrset์ ์ค๋น๋ ์งํฉ์ผ๋ก ๋ฎ์ด์ฐ๊ธฐ ๋๋ฌธ์ ๋ฃจํ๋ง๋คrset = allset์ผ๋ก ๋ค์ ์ฑ์์ผ ํ๊ณ ์ด ์งํฉ์ด ํธ์ถ๋ง๋ค user ๊ณต๊ฐ๊ณผ kernel ๊ณต๊ฐ ์ฌ์ด๋ฅผ ํต์งธ๋ก ๋ณต์ฌ๋๋ค. - ๋ฐํ ํ ์ ์ฒด ์ค์บ:
select๋ โ๋ช ๊ฐ๊ฐ ์ค๋น๋๋คโ๋ง ์๋ ค์ค ๋ฟ ์ด๋ค fd์ธ์ง๋ ์ ์๋ ค์ค๋ค. ๊ทธ๋์ ์ค๋น๋ fd๋ฅผ ์ฐพ์ผ๋ ค ์ ์ฒด๋ฅผFD_ISSET์ผ๋ก O(n) ํ์ด์ผ ํ๋ค. ์ปค๋๋ ๋ด๋ถ์ ์ผ๋ก ๊ฐ์ fd ์ ์ฒด๋ฅผ ๋งค๋ฒ ๊ฒ์ฌํ๋ค.
ํต์ฌ ๋ฌธ์ ๋ 3๋ฒ์ด๋ค. fd๊ฐ 1๋ง ๊ฐ์ด๊ณ ๊ทธ์ค ๋ฑ ํ๋๋ง ์ค๋น๋ผ๋, ๋งค ํธ์ถ๋ง๋ค 1๋ง ๊ฐ๋ฅผ ๊ฒ์ฌํ๊ณ 1๋ง ๊ฐ๋ฅผ ์ค์บํ๋ค. ๊ฐ์ ๋์์ด ๋์๋ก ์ค๋น๋ ๊ฐ์์ ๋ฌด๊ดํ๊ฒ ๋น์ฉ์ด ์ ํ์ผ๋ก ์ปค์ง๋ค. ์ด๊ฒ ํ์ฅ์ ๋ฒฝ์ด๋ค.
๊ทธ๋ฐ๋ฐ ์ด O(n)์ ๋นํธ๋ง์คํฌ๋ผ๋ ์๋ฃํ ํ์ด ์๋๋ค. ์ ๊ทธ๋ฐ์ง๋ ์ปค๋์ด select๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ๋์ง ๋ด์ผ ๋๋ฌ๋๋๋ฐ, ๊ทธ ๋ด๋ถ๋ฅผ ๋ค์ ์ ์์ ์ฐ๋ค.
์ ์ฒ์๋ถํฐ ๋ฐฐ์ด์ด ์๋ ๋นํธ๋ง์คํฌ๋ก ๊ตฌํ๋์์๊น?
fd_set์ ๋นํธ๋ง์คํฌ๋ก ํ ๊ฑด 1983๋ 4.2BSD ์ ์ฝ์ ๋ง๋ ํฉ๋ฆฌ์ ์ค๊ณ์๋ค. ๋น์์ fd๊ฐ ๋ช ๊ฐ ์ ๋๊ณ (์ฐ๊ฒฐ ์์ฒ์ ์์ ๋ฐ), ์์ ์งํฉ์์ โfd N์ด ์ค๋น๋๋โ๋ฅผ ๋นํธ ํ๋๋ก O(1)์ ๊ฒ์ฌํ๋ ๋นํธ๋ง์คํฌ๊ฐ ๋ฐฐ์ด๋ณด๋ค ๋น ๋ฅด๊ณ ์ฝคํฉํธํ์ผ๋ฉฐ, ๊ณ ์ ํฌ๊ธฐ๋ผ ์คํ์ ์ฌ๋ ค ํ ๋น ์์ด ์ธ ์ ์์๋ค. ์ํ์ ๊ทธ๋ ๋ฟ์ ์ผ์ด ์๋ ๊ฐ์ด์๋ค. ๋ฐฐ์ด๋ก ๋ค์ ํ ๊ฒ poll์ด์ง๋ง O(n) ๋ฌธ์ ๋ ๊ทธ๋๋ก์๊ณ , ์ง์ง ๋์ฝ์ ์๋ฃํ์ด ์๋๋ผ epoll์ ์ปค๋ ์ค๊ณ์๋ค. ๋ค๋ง select๊ฐ ํ์ค ABI๊ฐ ๋ ๋ค์ ์๋ฃํ์ ๋ชป ๋ฐ๊ฟ, pollยทepoll์ ๋ณ๋ ์ธํฐํ์ด์ค๋ก ์น์๋ค.
5. select์ ์ปค๋ ์์์ ์ด๋ป๊ฒ ์๊ณ ๊นจ๋ โ ๋๊ธฐ ํ์ wake_up
์ฐ๋ ๋ฒ๋ง ๋ด์ ์ ๋ณด์ด์ง๋ง, select๋ฅผ ๋ถ๋ฅด๋ฉด ์ปค๋ ์์์ ๋๋ต ์ด๋ ๊ฒ ๋๋ค. ์ด sleep/wake ๋ฉ์ปค๋์ฆ์ pollยทepoll๋ ๊ทธ๋๋ก ๊ณต์ ํ๋ ์ฌ๊ธฐ์ ํ ๋ฒ์ ์ ๋ฆฌํ๋ค.
// ์ปค๋ ์ select ์ฒ๋ฆฌ (๊ฐ๋
์ ์์ฌ์ฝ๋)
copy_in(fd_sets); // โ user โ kernel: fd_set ์ธ ๊ฐ๋ฅผ ๋ณต์ฌ
for (fd = 0; fd < nfds; fd++) // โก ์ ์ฒด ์ํ
if (fd_in_set(fd)) {
ready |= poll_fd(fd); // ์ค๋น ์ํ ํ์ธ (fd ํ์
๋ณ poll ์ฐ์ฐ)
register_wait(fd, current); // ์์ง์ด๋ฉด ๊ทธ fd ๋๊ธฐ ํ์ ์ด ์ค๋ ๋ ๋ฑ๋ก
}
if (!ready) sleep_until_wakeup(); // โข ์๋ฌด๊ฒ๋ ์ค๋น ์ ๋จ โ ์ค๋ ๋ ์ฌ์ (Blocked)
// ... ๋ฑ๋กํ fd๊ฐ ์ค๋น๋ผ ๋๊ธฐ ํ๊ฐ ๊นจ์ฐ๋ฉด ์ฌ๊ธฐ์ ์ฌ๊ฐ ...
for (fd = 0; fd < nfds; fd++) // โฃ ์ฒ์๋ถํฐ ๋ค์ ์ ์ฒด ์ํ
if (fd_ready(fd)) mark_result(fd); // ์ด๋ฒ์ ์ค๋น๋ fd๋ง ๊ฒฐ๊ณผ์ ํ์
copy_out(result_sets); // โค kernel โ user: ๊ฒฐ๊ณผ ๋๋๋ฆผ (์
๋ ฅ์ ๋ฎ์ด์)
return ready_count;๊ฐ fd์ ์ค๋น ์ฌ๋ถ๋ ๊ทธ fd ํ์
(์์ผยทํ์ดํยทํ์ผ)์ด ์ ๋ง๋ค ๊ตฌํํ poll ์ฐ์ฐ(->poll())์ผ๋ก ๋ฌป๋๋ค.
์ ์์ฌ์ฝ๋์ poll_fd๊ฐ ์ด ->poll()์ ๋ถ๋ฅด๋ ์๋ฆฌ์ด๊ณ , ์๋ sock_poll์ด ๊ทธ ์์ผ ๊ตฌํ์ด๋ค. ์ฐธ๊ณ ๋ก 6์ ์ poll() ์์คํ
์ฝ๊ณผ๋ ์ด๋ฆ๋ง ๊ฐ์ ๋ค๋ฅธ ์ธต์๋ค.
ํต์ฌ์ ์ปค๋์ ๊ด์ฌ ๋ชฉ๋ก์ ๋จ๊ฒจ๋๋ ์ํ๊ฐ ์๋ค๋ ์ ์ด๋ค.
๋งค ํธ์ถ๋ง๋ค fd_set์ ํต์งธ๋ก ์ฃผ๊ณ ๋ฐ๊ณ (โ ยทโค), 0๋ถํฐ nfds๊น์ง๋ฅผ ๋ ๋ฒ ํ๋๋ค(โก ๋ฑ๋กํ ๋, โฃ ๊นจ์ด๋ ๋ค). ์ค๋น๋ ๊ฒ ํ๋์ฌ๋ ์ ์ฒด๋ฅผ ์ค์บํ๋ค.
->poll() โ ํ์ธ๊ณผ ๋๊ธฐ ํ ๋ฑ๋ก
->poll()์ select๊ฐ ํ์ ๋ ๋ ๊ฐ์ง๋ฅผ ๋์์ ํ๋ค.
// ์์ผ์ poll ์ฐ์ฐ (๊ฐ๋
์ , ์ค์ tcp_poll ๋จ์ํ)
int sock_poll(fd, poll_table *pt) {
poll_wait(fd, &socket->wait_queue, pt); // โ ์ด ์ค๋ ๋๋ฅผ ์์ผ ๋๊ธฐ ํ์ ๋ฑ๋ก
int mask = 0;
if (์์ ๋ฒํผ์ ์ฝ์ ๋ฐ์ดํฐ ์์) mask |= POLLIN; // โก ์ง๊ธ ์ค๋น ์ํ๋ฅผ ๋ง์คํฌ๋ก ๋ฐํ
if (์ก์ ๋ฒํผ์ ์ฌ์ ์์) mask |= POLLOUT;
return mask;
}- โ ์ ์ด ์ค๋ ๋๋ฅผ ๊ทธ fd์ ๋๊ธฐ ํ์ ๊ฑธ์ด๋๋ค: ๋๊ธฐ ํ๋ ์ปค๋ ์์, fd๋ณ๋ก ์๋ ์๋ฃ๊ตฌ์กฐ๋ค(โ์ค๋น๋๋ฉด ์ฌ๊ธฐ๋ก ๊นจ์๋ฌ๋ผโ๋ ๋ฑ๋ก๋ถ)
- โก๋ ์ง๊ธ ๋น์ฅ ์ค๋น๋๋์ง๋ฅผ ๋นํธ๋ง์คํฌ๋ก ๋๋ ค์ค๋ค
๊นจ์ โ ๋ฐ์ดํฐ ๋์ฐฉ ์ฝ๋๊ฐ wake_up
์ ๋ถ ์ค๋น ์ ๋ผ ์ค๋ ๋๊ฐ ์ ๋ค๋ฉด, ๊นจ์ฐ๋ ๊ฒ์ ๋ฐ์ดํฐ ๋์ฐฉ ์ฝ๋๋ค.
์์ผ์ด๋ผ๋ฉด NIC๊ฐ ํจํท์ ๋ฐ์ ์ธํฐ๋ฝํธ๋ฅผ ๊ฑธ๊ณ , ์ปค๋ ๋คํธ์ํฌ ์คํ์ด ๊ทธ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํด ์์ผ ์์ ๋ฒํผ์ ๋ฃ๋๋ค.
๋ฒํผ๊ฐ ์ฑ์์ง๋ ์๊ฐ sk_data_ready2๋ฅผ ํตํด ์์ผ ์ฝ๋๊ฐ ๊ทธ ์์ผ์ ๋๊ธฐ ํ๋ฅผ ๊นจ์ด๋ค.
// ํจํท์ด ๋์ฐฉํด ์์ผ ์์ ๋ฒํผ๋ฅผ ์ฑ์ด ๋ค
sk_data_ready(socket); // โ wake_up(&socket->wait_queue) โ ๊ฑฐ๊ธฐ ๊ฑธ๋ฆฐ ์ค๋ ๋๋ฅผ ๊นจ์์ฆ ->poll()์ ์ง๊ธ ํ์ธ + ๊นจ์ ๊ฒฝ๋ก ๋ฑ๋ก(pull + ๋ฑ๋ก)์ด๊ณ , ์ค์ ๋ก ์ ์ ๊นจ์ฐ๋ ๊ฑด ๋ฐ์ดํฐ ์ชฝ์ด ๋๊ธฐ ํ๋ฅผ ๋ฏธ๋ wake_up(push)์ด๋ค.
๋จ๊ณ๋ณ Blocked โ Running ์ ์ด
์ ์กฐ๊ฐ๋ค์ ์๋ฃ๊ตฌ์กฐ ์์ค์์ ํ ์์๋ก ๊ฟฐ๋ฉด ์ด๋ ๋ค. ๋ฑ์ฅํ๋ ์ปค๋ ์๋ฃ๊ตฌ์กฐ๋ ๋ท์ด๋ค.
task_struct: ์ค๋ ๋๋ง๋ค ํ๋.stateํ๋๋ฅผ ๊ฐ์ง(TASK_RUNNINGยทTASK_INTERRUPTIBLE๋ฑ)- run queue: CPU๋ง๋ค ํ๋.
TASK_RUNNING์ํ์ ์ค๋ ๋๋ฅผ ๋ด๊ณ , ์ค์ผ์ค๋ฌ๊ฐ ์ฌ๊ธฐ์ ๋ค์ ์คํ ๋์์ ๊ณ ๋ฅธ๋ค wait_queue_head: fd๋ง๋ค ํ๋. ๊ทธ fd๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ค๋ ๋๋ค์ ์ฐ๊ฒฐ ๋ฆฌ์คํธwait_queue_entry: wait queue์ ๋ ธ๋.task_structํฌ์ธํฐ + wake function์ ๋ด๋๋ค
Phase A โ select ํธ์ถ โ ์ ๋ฆ(Running โ Blocked)
- ์ค๋ ๋๊ฐ
select์ง์ (์ง๊ธstate = TASK_RUNNING) - select๊ฐ ๊ฐ์ fd๋ง๋ค
->poll()ํธ์ถ โpoll_wait()์ด ๊ฐ fd์wait_queue_head์ ์ด ์ค๋ ๋์ฉwait_queue_entry๋ฅผ ์ถ๊ฐ - ์ด๋ fd๋ ์ค๋น ์ ๋จ โ select๊ฐ
state๋ฅผTASK_INTERRUPTIBLE๋ก ๋ฐ๊พธ๊ณschedule()ํธ์ถ schedule()์ด ์ด ์ค๋ ๋๋ฅผ run queue์์ ์ ๊ฑฐํ๊ณ ๋ค๋ฅธ ์ค๋ ๋์ CPU๋ฅผ ๋๊น โ ์ด ์ค๋ ๋๋ Blocked. ๋จ ๊ฐ์ํ๋ ๋ชจ๋ fd์wait_queue_head์ ์ํธ๋ฆฌ๋ก ๋ฑ๋ก๋ผ ์์
Phase B โ ๋ฐ์ดํฐ ๋์ฐฉ โ wake_up(Blocked โ Runnable)
- ์ด๋ค fd์ ๋ฐ์ดํฐ ๋์ฐฉ โ ๋ฐ์ดํฐ ๋์ฐฉ ์ฝ๋(
sk_data_ready๋ฑ)๊ฐ ๊ทธ fd์wait_queue_head์wake_up()ํธ์ถ wake_up()์ด ๊ทธ wait queue์ ์ํธ๋ฆฌ ๋ฆฌ์คํธ๋ฅผ ์ํํ๋ฉฐ ๊ฐ ์ํธ๋ฆฌ์ wake function ํธ์ถ- wake function(
try_to_wake_up)์ด ๊ทธ ์ํธ๋ฆฌ๊ฐ ๊ฐ๋ฆฌํค๋task_struct์ ๋ํด:state๋ฅผTASK_RUNNING์ผ๋ก ๋ณ๊ฒฝ- ๊ทธ ์ค๋ ๋๋ฅผ run queue์ ์ถ๊ฐ(enqueue)
- ๊นจ์ด ์ค๋ ๋๊ฐ ๋ ๋์ ์ฐ์ ์์๋ฉด ๋ฆฌ์ค์ผ์ค(์ ์ ) ํ๋๊ทธ ์ค์
- ์ด์ ์ค๋ ๋๋ Runnable
- โ์คํ ์ค๋น๋จโ์ ์ค์ฒด =
task_struct.state == TASK_RUNNING+ run queue ๋ฉค๋ฒ์ญ์ด๊ณ , ์ด ๋์ 7๋ฒ์ด wake_up ๊ฒฝ๋ก์์ ๊ธฐ๋ก
- โ์คํ ์ค๋น๋จโ์ ์ค์ฒด =
Phase C โ ์ค์ผ์ค โ ์ฌ๊ฐ โ ๊ฒฐ๊ณผ ํ์ธ(Runnable โ Running โ ๋ฐํ)
- ์ค์ผ์ค๋ฌ๊ฐ ์ด ์ค๋ ๋๋ฅผ ๊ณจ๋ผ CPU์ ์ฌ๋ฆผ โ ์ค์ Running
- ์ค๋ ๋๋ 4๋ฒ์
schedule()๋ค์ ์ง์ (select ๋ด๋ถ)์์ ์ฌ๊ฐ - select๊ฐ ๊ฐ์ fd ์ ๋ถ์ ๋ํด ๋ค์
->poll()โ ์ค๋น๋ fd์ ๋นํธ๋ฅผ ๊ฒฐ๊ณผfd_set์ set - select๊ฐ Phase A์์ ๋ฑ๋กํ
wait_queue_entry๋ค์ ๊ฐ wait queue์์ ์ ๊ฑฐ - select ๋ฐํ (๋ฐํ๊ฐ = set๋ ๋นํธ ์)
์ ๋ฆฌํ์๋ฉด wake_up์ด ํ๋ ์ผ์ 7๋ฒ๋ฟ์ด๋ค โ state = RUNNING + run queue enqueue.
๋นํธ๋ง์คํฌ๋ fd ๊ฒฐ๊ณผ๋ ๊ฑด๋๋ฆฌ์ง ์๊ณ , โ์ด๋ fd๊ฐ ๊นจ์ ๋์งโ๋ ๋จ๊ธฐ์ง ์๋๋ค.
๊ทธ๋์ 11๋ฒ์ ์ ์ฒด ์ฌ์ค์บ์ด ํ์ํ๋ค.
epoll์ 7๋ฒ์ wake function์ โ๊ทธ fd๋ฅผ ready list์ ์ถ๊ฐโํ๋ ์ฝ๋ฐฑ์ผ๋ก ๋ฐ๊ฟ ์ด ์ฌ์ค์บ์ ์์ค๋ค.
6. poll() โ ์ํ์ ํ๋, O(n)์ ๋จ๋๋ค
poll์ ๋นํธ๋ง์คํฌ ๋์ ๊ตฌ์กฐ์ฒด ๋ฐฐ์ด์ ์ด๋ค.
#include <poll.h>
struct pollfd fds[MAX]; // ๊ฐ์ํ fd๋ค์ ๋ฐฐ์ด. ๊ฐ ์์ = { fd, events(๊ด์ฌ), revents(๋ฐ์) }
fds[0].fd = listen_fd; // 0๋ฒ ์ฌ๋กฏ์ listen ์์ผ ๋ฑ๋ก
fds[0].events = POLLIN; // ๊ด์ฌ ์ด๋ฒคํธ = "์ฝ์ ๊ฒ ์๊ธฐ๋ฉด"(POLLIN)
int nfds = 1; // ํ์ฌ ๊ฐ์ ์ค์ธ fd ๊ฐ์
for (;;) { // ์๋ฒ ๋ฉ์ธ ๋ฃจํ
poll(fds, nfds, -1); // ๋ฐฐ์ด ์ ์ฒด๋ฅผ ์ปค๋์ ๋๊ฒจ ๊ฐ์ ์์. -1 = ์ค๋น๋ ๋๊น์ง ๋ฌดํ ๋๊ธฐ(blocking)
for (int i = 0; i < nfds; i++) { // ์ด๋ค ๊ฒ ์ค๋น๋๋์ง ๋ชจ๋ฅด๋ ๋ฐฐ์ด ์ ์ฒด๋ฅผ ํ๋๋ค (์ฌ์ ํ O(n) ์ค์บ)
if (!(fds[i].revents & POLLIN)) continue; // ์ปค๋์ด ์ฑ์ด ๋ฐ์ ์ด๋ฒคํธ(revents)์ POLLIN์ด ์์ผ๋ฉด ๊ฑด๋๋
// ... ์ฌ๊ธฐ์ listen ์์ผ์ด๋ฉด accept, ์๋๋ฉด read/echo (select ์์์ ๋์ผ) ...
}
}๋ ๊ฐ์ง๊ฐ ๋์์ก๋ค.
- ํ๋๋
fd_set๊ฐ์ ํฌ๊ธฐ ์ ํ์ด ์์ด(ํ์ผ ๋์คํฌ๋ฆฝํฐ ํ๊ณ๊น์ง) 1024๋ฅผ ๋๋ ๊ฒ - ๋ค๋ฅธ ํ๋๊ฐ
events์revents์ ๋ถ๋ฆฌ โ ๊ด์ฌ(์ ๋ ฅ)๊ณผ ๋ฐ์(์ถ๋ ฅ)์ ๋ณ๋ ํ๋๋ก ๋๋ ๊ฒ์ด๋ค.select์fd_set์ ์ ๋ ฅ์ด์ ์ถ๋ ฅ์ด๋ผ ์ปค๋์ด ๊ฒฐ๊ณผ(์ค๋น๋ fd)๋ก ๊ทธ ์๋ฅผ ๋ฎ์ด์ด๋ค. ๊ทธ๋์ ์๋ ๊ฐ์ ๋ชฉ๋ก์ด ์ฌ๋ผ์ง๊ณ , ๋งค ๋ฃจํrset = allset์ผ๋ก ๋ค์ ์ฑ์์ผ ํ๋ค.poll์ ๊ฐpollfd๊ฐ ๋ด๊ฐ ์ฑ์ฐ๋ ์ ๋ ฅ (events)์ ์ปค๋์ด ์ฑ์ฐ๋ ์ถ๋ ฅ(revents)๋ก ๋๋๋ค. ์ปค๋์events๋ ๊ฑด๋๋ฆฌ์ง ์๊ณrevents์๋ง ๊ฒฐ๊ณผ๋ฅผ ์ฐ๋ฏ๋ก, ๊ฐ์ ๋ชฉ๋ก(events)์ด ๊ฒฐ๊ณผ์ ์ ๋ฎ์ฌ ๊ทธ๋๋ก ์ฌ์ฌ์ฉ๋๋ค. select์ ๋งค ํธ์ถ ์ฌ์ด๊ธฐํ๊ฐ ์ฌ๋ผ์ง๋ ์ด์ ๋ค.
๋ค๋ง ๋ ๋ฒ์งธ ๊ฐ์ ๋ด์ฉ(events, revents์ ๋ถ๋ฆฌ)์ ์ฑ๋ฅ์ด ์๋๋ผ ํธ์ ๊ฐ์ ์ ๊ฐ๊น๋ค.
๋งค ๋ฃจํ ๊ฐ์ ๋ชฉ๋ก์ ๋ค์ ๋ง๋๋ ๋ฒ๊ฑฐ๋ก์ ํ๋๋ฅผ ๋ ๊ฒ๋ฟ์ด๋ผ, poll์ ๋ ์๋ฏธ ์๋ ๊ฐ์ ์ ์คํ๋ ค 1024 ์ํ ์ ๊ฑฐ ์ชฝ์ด๋ค.
ํ์ง๋ง ๊ทผ๋ณธ์ ๊ทธ๋๋ก๋ค. ์ฌ์ ํ ์ ์ฒด ๋ฐฐ์ด์ ๋งค ํธ์ถ๋ง๋ค ์ปค๋์ ๋๊ธฐ๊ณ (O(n) ๋ณต์ฌ), ๋์์ revents๋ฅผ ์ ๋ถ ํ๋๋ค(O(n) ์ค์บ). poll์ 1024 ์ ํ๊ณผ ์ฌ์ด๊ธฐํ๋ฅผ ์์ด์ ๋ฟ, ์ค๋น ๊ฐ์์ ๋ฌด๊ดํ ์ ํ ๋น์ฉ์ ๊ทธ๋๋ก๋ค.
7. epoll โ ๊ด์ฌ ๋ชฉ๋ก์ ์ปค๋์ด ๋ค๊ณ ์๋๋ค
๋ฆฌ๋ ์ค๋ง์ ๋์ฝ์ด๋ค. selectยทpoll์ด ๋งค ํธ์ถ๋ง๋ค ๊ฐ์ ๋ชฉ๋ก ์ ์ฒด๋ฅผ ์ปค๋์ ์๋ก ์๋ ค์ฃผ๋ ๊ฒ์, epoll์ ํ ๋ฒ ๋ฑ๋กํด๋๊ณ ์ค๋น๋ ๊ฒ๋ง ๋ฐ๋๋ค. ์์คํ ์ฝ์ด ์ ์ผ๋ก ๋๋๋ค.
epoll_create1: epoll ์ธ์คํด์ค๋ฅผ ๋ง๋ ๋ค. ์ด ์ธ์คํด์ค ์์ฒด๋ ํ๋์ fd๋คepoll_ctl: ๊ฐ์ํ fd๋ฅผ ๊ด์ฌ ๋ชฉ๋ก์ ๋ฑ๋ก(ADD)ยท์์ (MOD)ยท์ ๊ฑฐ(DEL). fd๋น ํ ๋ฒ๋ง ๋ถ๋ฅธ๋คepoll_wait: ์ค๋น๋ fd๋ง ๋ฐฐ์ด๋ก ๋๋ ค๋ฐ๋๋ค
#include <sys/epoll.h>
int epfd = epoll_create1(0); // epoll ์ธ์คํด์ค ์์ฑ. ๋ฐํ๊ฐ epfd๋ ํ๋์ fd๋ค
struct epoll_event ev = { .events = EPOLLIN, .data.fd = listen_fd }; // listen ์์ผ์ "์ฝ๊ธฐ ๊ฐ์"๋ก ๋ฑ๋กํ ์ค์
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev); // ๊ด์ฌ ๋ชฉ๋ก์ ํ ๋ฒ๋ง ๋ฑ๋ก (์ดํ ๋งค ๋ฃจํ ๋ค์ ์ ๋๊น)
struct epoll_event events[MAX]; // epoll_wait์ด ์ค๋น๋ fd๋ค์ ๋ด์์ค ๋ฐฐ์ด
for (;;) { // ์๋ฒ ๋ฉ์ธ ๋ฃจํ
int n = epoll_wait(epfd, events, MAX, -1); // ์ค๋น๋ fd๋ง events์ n๊ฐ ๋ด์ ๋ฐํ. -1 = ๋ฌดํ ๋๊ธฐ(blocking)
for (int i = 0; i < n; i++) { // ์ค๋น๋ n๊ฐ๋ง ์ํ โ ์ ์ฒด ์ค์บ์ด ์๋ค (O(์ค๋น๋ ์))
int fd = events[i].data.fd; // ๋ฑ๋ก ๋ ๋ฃ์ด๋ fd๋ฅผ ๊ทธ๋๋ก ๊บผ๋
if (fd == listen_fd) { // ์ค๋น๋ ๊ฒ listen ์์ผ์ด๋ฉด = ์ ์ฐ๊ฒฐ ์์ฒญ
int conn = accept(listen_fd, NULL, NULL); // ์ฐ๊ฒฐ ์๋ฝ โ ํด๋ผ์ด์ธํธ์ฉ fd ์์ฑ
struct epoll_event cev = { .events = EPOLLIN, .data.fd = conn }; // ์ ์ฐ๊ฒฐ๋ "์ฝ๊ธฐ ๊ฐ์"๋ก ์ค์
epoll_ctl(epfd, EPOLL_CTL_ADD, conn, &cev); // ๊ด์ฌ ๋ชฉ๋ก์ ์ถ๊ฐ (์ญ์ ํ ๋ฒ๋ง)
} else { // ์ค๋น๋ ๊ฒ ๊ธฐ์กด ์ฐ๊ฒฐ์ด๋ฉด = ๋ฐ์ดํฐ ๋์ฐฉ
char buf[1024]; // ์ฝ์ด ๋ค์ผ ๋ฐ์ดํฐ ๋ฒํผ
int r = read(fd, buf, sizeof buf); // ์ด๋ฏธ ์ค๋น๋ ์์ผ์ด๋ผ ๋ฐ๋ก ์ฝํ๋ค
if (r <= 0) close(fd); // 0=์๋๊ฐ ๋ซ์, ์์=์๋ฌ โ close (๋ซ์ผ๋ฉด epoll์์ ์๋ ์ ๊ฑฐ)
else write(fd, buf, r); // ์ฝ์ ๋งํผ ๊ทธ๋๋ก ๋๋ ค๋ณด๋ (์์ฝ)
}
}
}์ฐจ์ด๊ฐ ์ฝ๋์ ๋๋ฌ๋๋ค. epoll_wait์๋ fd ์งํฉ์ ๋๊ธฐ์ง ์๋๋ค. ๊ด์ฌ ๋ชฉ๋ก์ ์ด๋ฏธ ์ปค๋์ด ๋ค๊ณ ์๊ณ ์ค๋น๋ fd๋ง events ๋ฐฐ์ด๋ก ๋์์จ๋ค. ๊ทธ๋์ ๋ฐํ ํ ์ค์บ๋ โ์ค๋น๋ n๊ฐโ๋ง ๋๋ค. ์ ์ฒด๋ฅผ ํ๋ selectยทpoll์ O(n)์ด ์ฌ๋ผ์ก๋ค.
8. epoll์ ๋์ ์๋ฆฌ โ interest list์ ready list
epoll์ด ์ด๋ป๊ฒ O(n)์ ์์ด๋์ง๊ฐ ํต์ฌ์ด๋ค. epoll ์ธ์คํด์ค๋ ์ปค๋ ์์ ๋ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ ์งํ๋ค.
epoll ์ธ์คํด์ค (์ปค๋)
โโ interest list โ epoll_ctl(ADD)๋ก ๋ฑ๋ก๋ fd๋ค (red-black tree)
โ fd3, fd7, fd10, ... (์์ญ๋ง ๊ฐ์ฌ๋ OK)
โโ ready list โ ์ค๋น๋ fd๋ง (doubly linked list)
fd7 โ ๋ฐ์ดํฐ ๋์ฐฉ โ ์ฝ๋ฐฑ์ด ์ฌ๊ธฐ์ ์ถ๊ฐ
epoll_wait โ ready list๋ง ํ์ธ โ fd7 ๋ฐํ (interest list ์ ์ฒด๋ ์ ๋ด)
- interest list: ๋ฑ๋ก๋ fd ์ ์ฒด. red-black tree๋ผ fd ์ถ๊ฐยท์ญ์ ยท์กฐํ๊ฐ ๋น ๋ฅด๋ค.
- ready list: ์ค๋น๋ fd๋ง ๋ชจ์ธ ๋ชฉ๋ก.
๋์์ ์ฝ๋ฐฑ ๊ธฐ๋ฐ์ด๋ค. epoll_ctl๋ก fd๋ฅผ ๋ฑ๋กํ ๋, ์ปค๋์ ๊ทธ fd์ ๋๊ธฐ ํ์ ์ฝ๋ฐฑ์ ์ฌ๋๋ค. ๋์ค์ ๊ทธ fd๊ฐ ์ค๋น ์ํ๊ฐ ๋๋ฉด(์: ํจํท์ด ๋์ฐฉํด ์์ผ ์์ ๋ฒํผ๊ฐ ์ฑ์์ง๋ฉด), ์ปค๋์ ๋คํธ์ํฌ ์ฝ๋๊ฐ ๊ทธ ๋๊ธฐ ํ๋ฅผ ๊นจ์ฐ๊ณ ์ฌ์ด๋ ์ฝ๋ฐฑ์ด ์คํ๋ผ ํด๋น fd๋ฅผ ready list์ ์ถ๊ฐํ๋ค.
๊ทธ๋์ epoll_wait๊ฐ ํ๋ ์ผ์ ๋จ์ํ๋ค. ready list๋ฅผ ํ์ธํด ๋น์ด ์์ง ์์ผ๋ฉด ๊ทธ ํญ๋ชฉ๋ค๋ง ๋ณต์ฌํด ๋ฐํํ๊ณ ๋น์ด ์์ผ๋ฉด ํธ์ถ ์ค๋ ๋๋ฅผ ์ฌ์ด๋ค(Blocked). ์ค๋น ์ด๋ฒคํธ๊ฐ ์ ready list๊ฐ ์ฑ์์ง๋ฉด ๊นจ์ด๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก epoll_wait์ ๋น์ฉ์ ๋ฑ๋ก๋ fd ์๊ฐ ์๋๋ผ ์ค๋น๋ fd ์์ ๋น๋กํ๋ค. fd๋ฅผ 100๋ง ๊ฐ ๋ฑ๋กํด๋ ๊ทธ ์๊ฐ ์ค๋น๋ ๊ฒ 3๊ฐ๋ฉด 3๊ฐ๋งํผ๋ง ์ผํ๋ค. selectยทpoll์ด ๋งค๋ฒ 100๋ง ๊ฐ๋ฅผ ๋ค ํ๋ ๊ฒ๊ณผ ์ ๋ฐ๋๋ค. ๊ฐ์ ๋ชฉ๋ก์ ์ปค๋์ด ๊ณ์ ๋ค๊ณ ์๊ณ (๋ฑ๋ก์ ํ ๋ฒ), ์ค๋น ์ฌ๋ถ๋ฅผ ์ค์บ์ด ์๋๋ผ ์ฝ๋ฐฑ์ผ๋ก ์๊ธฐ ๋๋ฌธ์ ์ฌ๊ฒ์ฌ ์์ฒด๊ฐ ์๋ค.
| select | poll | epoll | |
|---|---|---|---|
| fd ๊ฐ์ ์ํ | FD_SETSIZE 1024 | ์์ | ์์ |
| ๊ด์ฌ ๋ชฉ๋ก ์ ๋ฌ | ๋งค ํธ์ถ ์ ์ฒด ๋ณต์ฌ | ๋งค ํธ์ถ ์ ์ฒด ๋ณต์ฌ | epoll_ctl๋ก ํ ๋ฒ ๋ฑ๋ก |
| ์ค๋น fd ์ฐพ๊ธฐ | ๋ฐํ ํ O(n) ์ค์บ | ๋ฐํ ํ O(n) ์ค์บ | ์ค๋น๋ ๊ฒ๋ง ๋ฐํ, O(ready) |
| ์ปค๋ ๋ด๋ถ | ๋งค๋ฒ ์ ์ฒด O(n) ๊ฒ์ฌ | ๋งค๋ฒ ์ ์ฒด O(n) ๊ฒ์ฌ | ์ฝ๋ฐฑ์ผ๋ก ready list ์ ์ง |
| ์ปค๋ ์ํ | ์์(๋งค๋ฒ ์ฌ์ ๋ฌ) | ์์(๋งค๋ฒ ์ฌ์ ๋ฌ) | interest list ์ ์ง |
9. level-triggered์ edge-triggered
epoll์ด โfd๊ฐ ์ค๋น๋๋คโ๊ณ ์๋ฆฌ๋ ๋ฐฉ์์ ๋๋ก ๊ฐ๋ฆฐ๋ค: level-triggered์ edge-triggered.
์ด๊ฑด epoll์ด ๋ง๋ ๊ฒ ์๋๋ผ ํ๋์จ์ด ์ธํฐ๋ฝํธ์์ ์จ ์ค๋๋ ๊ฐ๋
์ด๋ค. ํ๋์จ์ด ์ธํฐ๋ฝํธ๋ ์ฅ์น(ํค๋ณด๋ยทNIC ๋ฑ)๊ฐ CPU์ โ์ฒ๋ฆฌํ ๊ฒ ์๊ฒผ๋คโ๊ณ ๋ณด๋ด๋ ์ ๊ธฐ ์ ํธ์ธ๋ฐ, ๊ทธ ์ ํธ๊ฐ ๋์ ์ํ๋ก ์๋ ๋์ ๊ณ์ ๋ฐ์ํ๋ฉด ๋ ๋ฒจ ํธ๋ฆฌ๊ฑฐ, ๋ฎ์์์ ๋์์ผ๋ก ๋ฐ๋๋ ์๊ฐ์๋ง ๋ฐ์ํ๋ฉด ์ฃ์ง ํธ๋ฆฌ๊ฑฐ๋ค.
์ด โ์ํ๋ ์ ์ด๋โ๊ฐ ๊ทธ๋๋ก fd ํต์ง์ ์ ์ฉ๋๋ค.
์ค๋น ์ํ๋ฅผ ๋ ์๋นํ๋ฉด?
์ด โ์ํ๋ ์ ์ด๋โ๊ฐ fd์์ ์ค์ ๋ก ๊ฐ๋ฆฌ๋ ์ง์ ์ ์ค๋น๋ ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ ๋ค ์ ์ฝ์์ ๋๋ค.
๊ตฌ์ฒด์ ์ผ๋ก, ์์ผ์ 2KB๊ฐ ๋์ฐฉํด โ์ฝ๊ธฐ ๊ฐ๋ฅโ ํต์ง๋ฅผ ๋ฐ์๋๋ฐ ๋น์ ์ด 1KB๋ง ์ฝ๊ณ ๋ฉ์ท๋ค๋ฉด(1KB ๋จ์) ๋ค์ ํต์ง๊ฐ ๊ฐ๋ฆฐ๋ค.
- LT: ์์ง 1KB๊ฐ ๋จ์ ์กฐ๊ฑด์ด ์ฐธ์ด๋ ๋ ํต์งํ๋ค. ๋ค ์ ์ฝ์ด๋ ๋ค์์ ๋ ์๋ ค์ฃผ๋ ์์ ํ๊ณ ๋ค๋ฃจ๊ธฐ ์ฝ๋ค.
- ET: ์ ๋ฐ์ดํฐ๊ฐ ์ค๋ ์ edge๊ฐ ์์ผ๋ ๋ค์ ํต์ง ์ ํ๋ค. ๋จ์ 1KB๋ ๋ฐฉ์น๋ผ ๊ทธ ์ฐ๊ฒฐ์ด ๋ฉ์ถ ๊ฒ์ฒ๋ผ ๋ณด์ธ๋ค.
epoll์์ โ ๋ ๋ค ์ ๊ณต, ํ๋๊ทธ๋ก ์ ํ
I/O ๋ฉํฐํ๋ ์ฑ์์ ์ด ๋์ ๊ณ ๋ฅผ ์ ์๋ ๊ฑด epoll๋ฟ์ด๋ค(selectยทpoll์ LT ๊ณ ์ ). ๊ธฐ๋ณธ์ LT์ด๊ณ , ๋ฑ๋กํ ๋ EPOLLET์ ๋ํ๋ฉด ET๋ค.
struct epoll_event ev = { .events = EPOLLIN | EPOLLET, .data.fd = conn }; // ET๋ก ๋ฑ๋ก
epoll_ctl(epfd, EPOLL_CTL_ADD, conn, &ev);ET๋ ํต์งยทwakeup ํ์๋ฅผ ์ค์ฌ ๊ณ ๋ถํ์์ ์ ๋ฆฌํด nginx ๊ฐ์ ์๋ฒ๊ฐ ์ด๋ค. ๋์ ์ โ๋ ์ฝ์ผ๋ฉด ๋ฐฉ์นโ ๋๋ฌธ์ ๊ณ์ฝ์ด ๋ถ๋๋ค.
edge-triggered์ ํจ์
ET์์ ํต์ง๋ฅผ ๋ฐ์ผ๋ฉด
EAGAIN์ด ๋ ๋๊น์ง ๋ฒํผ๋ฅผ ๋ค ์ฝ์ด์ผ ํ๋ค. ํ ๋ฒ๋งreadํ๊ณ ๋จ๊ธฐ๋ฉด, ๋จ์ ๋ฐ์ดํฐ์ ๋ํ ๋ค์ ํต์ง๊ฐ ์ค์ง ์์ ๊ทธ ์ฐ๊ฒฐ์ด ์์ ๋ฉ์ถ๋ค. ๊ทธ๋์ ET๋ non-blocking fd + ๋ค ์ฝ๋ ๋ฃจํ๊ฐ ํ์๋ค.
// edge-triggered: EAGAIN๊น์ง ๋ชจ๋ ์ฝ์ด์ผ ํ๋ค
while (1) {
ssize_t r = read(fd, buf, sizeof buf);
if (r > 0) {
// ... ์ฝ์ ๋งํผ ์ฒ๋ฆฌ ...
} else if (r == 0) {
close(fd); // ์๋๊ฐ ์ฐ๊ฒฐ์ ๋ซ์
break;
} else { // r < 0
if (errno == EAGAIN) break; // ๋ฒํผ๋ฅผ ๋ค ๋น์ ๋ค โ ์ ์ ์ข
๋ฃ
close(fd); // ์ง์ง ์๋ฌ
break;
}
}ET๊ฐ ์
๋ช
๋์ ๊ฑด, ์ด drain์ ๋น ๋จ๋ ค๋ ์๋ฌ๋ ๋ก๊ทธ๋ ์์ด ๊ทธ ์ฐ๊ฒฐ๋ง ์กฐ์ฉํ ๋ฉ์ถฐ ์์ธ ์ฐพ๊ธฐ๊ฐ ์ด๋ ต๊ณ , ์์ ๋ฉ์์ง๋ก ํ
์คํธํ๋ฉด ํ ๋ฒ์ ๋ค ์ฝํ ์ ๊ฑธ๋ฆฌ๋ค ๋ถํ๊ฐ ์ปค์ง๋ฉด ํฐ์ง๊ธฐ ๋๋ฌธ์ด๋ค.
๋ค๋ง ์ด๊ฑด raw epoll์ ์ง์ ์งค ๋์ ํจ์ ์ด๋ค.
๋๊ฐ ์ ํ๋
๊ฐ๋ฐ์๋ ์ด ๋ฐฉ์์ ๊ตฌํํ๋ ๊ฒ ์๋๋ผ EPOLLET ํ๋๊ทธ๋ก ๊ณ ๋ฅด๊ธฐ๋ง ํ๋ค(์ ์ฃผ๋ฉด LT). ๊ทธ๋ง์ ๋ epoll API๋ฅผ ์ง์ ๋ถ๋ฅด๋ ์ ์์ค ์ฝ๋์์๋ง ๋ง์ง๊ณ , ๊ณ ์์ค์ผ๋ก ๊ฐ์๋ก ๋ฐํ์ยท๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋์ ์ ํ๋ค.
| ์ธต์ | LT/ET๋ฅผ ๋๊ฐ ์ ํ๋ |
|---|---|
| C raw epoll | ๊ฐ๋ฐ์๊ฐ EPOLLET๋ก ์ง์ |
| nginxยทredis (C ์ด๋ฒคํธ ๋ฃจํ) | ๊ทธ ์๋ฒ ์ ์๊ฐ ์ง์ (nginx๋ ET) |
Go net ํจํค์ง | Go ๋ฐํ์์ด ๋ด๋ถ์์ (ET). ์ฑ ์ฝ๋๋ ์ ๋ง์ง |
| Python asyncio | ์ ํ๊ธฐ(selectors)๊ฐ (LT). ์ ๋ง์ง |
| Node.js | libuv๊ฐ ์ ํจ. ์ ๋ง์ง |
๊ทธ๋์ ๋๋ถ๋ถ์ ์ฑ ๊ฐ๋ฐ์๋ LT/ET๋ฅผ ๊ฑด๋๋ฆด ์ผ์ด ์๊ณ , ์ด๋ฒคํธ ๋ฃจํ๋ ์ปค๋ ๋ฐ์ฐฉ ๋คํธ์ํน์ ์ง์ ์ง๋ ์ฌ๋๋ง ๋ง์ฃผ์น๋ ์ค์์น๋ค.
๋๋ถ๋ถ์ LT๋ก ์ถฉ๋ถํ๊ณ , ๊ทนํ ์ฑ๋ฅ์ด ํ์ํ ๋๋ง ET์ ๋ณต์กํจ์ ๊ฐ์ํ๋ค.
10. epoll ์์ ์นํ ์์ ๊ฐ๋ ๋ค
์ค๋ฌด์์ epoll์ ์ง์ ๋ถ๋ฅผ ์ผ์ ๋๋ฌผ๊ณ , ๋ณดํต ๊ทธ ์์ ์นํ ์์ ๊ฐ๋ ์ ํตํด ๋ง๋๋ค. ๋ํ์ ์ธ ๊ฒ๋ค์ด๋ค.
- event loop:
epoll_wait๋ฃจํ์ โ์ค๋น๋ fd โ ๋์ํ๋ ์ฝ๋ฐฑ ์คํ ๋๋ coroutine ์ฌ๊ฐโ๋ฅผ ์น์ ๊ฒ์ด๋ค. Pythonasyncio, Node์ libuv, Go์ netpoller๊ฐ ๋ชจ๋ ์ด ๊ตฌ์กฐ ์์ ์๋ค. - nginxยทredisยทHAProxy: epoll ๊ธฐ๋ฐ ์ด๋ฒคํธ ์๋ฒ๋ผ ๋จ์ผ(๋๋ ์์) ์ค๋ ๋๋ก ์๋ง ์ฐ๊ฒฐ์ ๊ฐ๋นํ๋ค.
- Concurrency and Parallelism 11์ ์ด โOS์ ์ค๋น์๋ฃ ํต์งโ๋ผ ๋ถ๋ฅธ ๊ฒ์ด ์ ํํ epoll์ ready list์ด๊ณ , ๊ทธ ์ event loop์ด FastAPI๊ฐ ์์ฒญ์ coroutine์ผ๋ก ๋ค์คํํ๋ ๋ฐํ์ด๋ค.
- busy polling๊ณผ ๋๋นํ๋ฉด ์ ๋ช ํ๋ค. busy polling์ ์ค๋ ๋๊ฐ ์ง์ ๋๋ฌผ์ผ๋ฉฐ ์ฝ์ด๋ฅผ ํ์ฐ๊ณ , epoll์ ์ค๋น๋ ๋๊น์ง ์ ๋ค์๋ค ์ปค๋ ํต์ง๋ก ๊นจ์ด๋๋ค.
์ ๋ฆฌํ๋ฉด asyncioยทNodeยท๊ณ ์ฑ๋ฅ ์๋ฒ์ โ๋จ์ผ ์ค๋ ๋๋ก ์์ฒ ์ฐ๊ฒฐโ์ ์ ๋ถ epoll(๋ฆฌ๋ ์ค) ๋๋ kqueue(BSDยทmacOS) ์์ ์ธ์ด ์ถ์์ด๋ค.
Footnotes
-
โResource temporarily unavailableโ, ๊ณง โ์ง๊ธ์ ์ค๋น ์ ๋์ผ๋ ๋์ค์ ๋ค์ ์๋ํ๋ผโ๋ ์ ํธ โฉ
-
์์ผ ์์ ๋ฒํผ์ ์๋ก์ด ๋ฐ์ดํฐ๊ฐ ๋์ฐฉํ์ ๋ ํธ์ถ๋๋ ํต์ฌ ์ฝ๋ฐฑ ํจ์. ๊ธฐ๋ณธ์ ์ผ๋ก
sock_def_readable()ํจ์๋ก ๋งคํ๋๋ฉฐ, ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ธฐ ์ํด ๋๊ธฐ ์ค์ธ ํ๋ก์ธ์ค๋ฅผ ๊นจ์(Wake-up) I/O ์ฒ๋ฆฌ๋ฅผ ์งํํ๋๋ก ์๋ฆฌ๋ ์ญํ โฉ