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๊ฐ€ ๋งŽ์•„์ง€๋ฉด ์„ธ ๊ฐ€์ง€๊ฐ€ ๋ฐœ๋ชฉ์„ ์žก๋Š”๋‹ค.

  1. fd ๊ฐœ์ˆ˜ ์ƒํ•œ: fd_set์€ ํฌ๊ธฐ๊ฐ€ ๊ณ ์ •๋œ ๋น„ํŠธ๋งˆ์Šคํฌ๋ผ FD_SETSIZE(๋ณดํ†ต 1024)๋ฅผ ๋„˜๋Š” fd๋ฅผ ๊ฐ์‹œํ•  ์ˆ˜ ์—†๋‹ค.
  2. ๋งค ํ˜ธ์ถœ๋งˆ๋‹ค ์ „์ฒด ๋ณต์‚ฌ: select๊ฐ€ rset์„ ์ค€๋น„๋œ ์ง‘ํ•ฉ์œผ๋กœ ๋ฎ์–ด์“ฐ๊ธฐ ๋•Œ๋ฌธ์— ๋ฃจํ”„๋งˆ๋‹ค rset = allset์œผ๋กœ ๋‹ค์‹œ ์ฑ„์›Œ์•ผ ํ•˜๊ณ  ์ด ์ง‘ํ•ฉ์ด ํ˜ธ์ถœ๋งˆ๋‹ค user ๊ณต๊ฐ„๊ณผ kernel ๊ณต๊ฐ„ ์‚ฌ์ด๋ฅผ ํ†ต์งธ๋กœ ๋ณต์‚ฌ๋œ๋‹ค.
  3. ๋ฐ˜ํ™˜ ํ›„ ์ „์ฒด ์Šค์บ”: 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)

  1. ์Šค๋ ˆ๋“œ๊ฐ€ select ์ง„์ž… (์ง€๊ธˆ state = TASK_RUNNING)
  2. select๊ฐ€ ๊ฐ์‹œ fd๋งˆ๋‹ค ->poll() ํ˜ธ์ถœ โ†’ poll_wait()์ด ๊ฐ fd์˜ wait_queue_head์— ์ด ์Šค๋ ˆ๋“œ์šฉ wait_queue_entry๋ฅผ ์ถ”๊ฐ€
  3. ์–ด๋А fd๋„ ์ค€๋น„ ์•ˆ ๋จ โ†’ select๊ฐ€ state๋ฅผ TASK_INTERRUPTIBLE๋กœ ๋ฐ”๊พธ๊ณ  schedule() ํ˜ธ์ถœ
  4. schedule()์ด ์ด ์Šค๋ ˆ๋“œ๋ฅผ run queue์—์„œ ์ œ๊ฑฐํ•˜๊ณ  ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์— CPU๋ฅผ ๋„˜๊น€ โ†’ ์ด ์Šค๋ ˆ๋“œ๋Š” Blocked. ๋‹จ ๊ฐ์‹œํ•˜๋˜ ๋ชจ๋“  fd์˜ wait_queue_head์—” ์—”ํŠธ๋ฆฌ๋กœ ๋“ฑ๋ก๋ผ ์žˆ์Œ

Phase B โ€” ๋ฐ์ดํ„ฐ ๋„์ฐฉ โ†’ wake_up(Blocked โ†’ Runnable)

  1. ์–ด๋–ค fd์— ๋ฐ์ดํ„ฐ ๋„์ฐฉ โ†’ ๋ฐ์ดํ„ฐ ๋„์ฐฉ ์ฝ”๋“œ(sk_data_ready ๋“ฑ)๊ฐ€ ๊ทธ fd์˜ wait_queue_head์— wake_up() ํ˜ธ์ถœ
  2. wake_up()์ด ๊ทธ wait queue์˜ ์—”ํŠธ๋ฆฌ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ๊ฐ ์—”ํŠธ๋ฆฌ์˜ wake function ํ˜ธ์ถœ
  3. wake function(try_to_wake_up)์ด ๊ทธ ์—”ํŠธ๋ฆฌ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” task_struct์— ๋Œ€ํ•ด:
    • state๋ฅผ TASK_RUNNING์œผ๋กœ ๋ณ€๊ฒฝ
    • ๊ทธ ์Šค๋ ˆ๋“œ๋ฅผ run queue์— ์ถ”๊ฐ€(enqueue)
    • ๊นจ์šด ์Šค๋ ˆ๋“œ๊ฐ€ ๋” ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋ฉด ๋ฆฌ์Šค์ผ€์ค„(์„ ์ ) ํ”Œ๋ž˜๊ทธ ์„ค์ •
  4. ์ด์ œ ์Šค๋ ˆ๋“œ๋Š” Runnable
    • โ€œ์‹คํ–‰ ์ค€๋น„๋จโ€์˜ ์‹ค์ฒด = task_struct.state == TASK_RUNNING + run queue ๋ฉค๋ฒ„์‹ญ์ด๊ณ , ์ด ๋‘˜์„ 7๋ฒˆ์ด wake_up ๊ฒฝ๋กœ์—์„œ ๊ธฐ๋ก

Phase C โ€” ์Šค์ผ€์ค„ โ†’ ์žฌ๊ฐœ โ†’ ๊ฒฐ๊ณผ ํ™•์ธ(Runnable โ†’ Running โ†’ ๋ฐ˜ํ™˜)

  1. ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์ด ์Šค๋ ˆ๋“œ๋ฅผ ๊ณจ๋ผ CPU์— ์˜ฌ๋ฆผ โ†’ ์‹ค์ œ Running
  2. ์Šค๋ ˆ๋“œ๋Š” 4๋ฒˆ์˜ schedule() ๋‹ค์Œ ์ง€์ (select ๋‚ด๋ถ€)์—์„œ ์žฌ๊ฐœ
  3. select๊ฐ€ ๊ฐ์‹œ fd ์ „๋ถ€์— ๋Œ€ํ•ด ๋‹ค์‹œ ->poll() โ†’ ์ค€๋น„๋œ fd์˜ ๋น„ํŠธ๋ฅผ ๊ฒฐ๊ณผ fd_set์— set
  4. select๊ฐ€ Phase A์—์„œ ๋“ฑ๋กํ•œ wait_queue_entry๋“ค์„ ๊ฐ wait queue์—์„œ ์ œ๊ฑฐ
  5. 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๋งŒ ๊ฐœ๋ฅผ ๋‹ค ํ›‘๋˜ ๊ฒƒ๊ณผ ์ •๋ฐ˜๋Œ€๋‹ค. ๊ฐ์‹œ ๋ชฉ๋ก์„ ์ปค๋„์ด ๊ณ„์† ๋“ค๊ณ  ์žˆ๊ณ (๋“ฑ๋ก์€ ํ•œ ๋ฒˆ), ์ค€๋น„ ์—ฌ๋ถ€๋ฅผ ์Šค์บ”์ด ์•„๋‹ˆ๋ผ ์ฝœ๋ฐฑ์œผ๋กœ ์•Œ๊ธฐ ๋•Œ๋ฌธ์— ์žฌ๊ฒ€์‚ฌ ์ž์ฒด๊ฐ€ ์—†๋‹ค.

selectpollepoll
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.jslibuv๊ฐ€ ์ •ํ•จ. ์•ˆ ๋งŒ์ง

๊ทธ๋ž˜์„œ ๋Œ€๋ถ€๋ถ„์˜ ์•ฑ ๊ฐœ๋ฐœ์ž๋Š” LT/ET๋ฅผ ๊ฑด๋“œ๋ฆด ์ผ์ด ์—†๊ณ , ์ด๋ฒคํŠธ ๋ฃจํ”„๋‚˜ ์ปค๋„ ๋ฐ€์ฐฉ ๋„คํŠธ์›Œํ‚น์„ ์ง์ ‘ ์งœ๋Š” ์‚ฌ๋žŒ๋งŒ ๋งˆ์ฃผ์น˜๋Š” ์Šค์œ„์น˜๋‹ค.
๋Œ€๋ถ€๋ถ„์€ LT๋กœ ์ถฉ๋ถ„ํ•˜๊ณ , ๊ทนํ•œ ์„ฑ๋Šฅ์ด ํ•„์š”ํ•  ๋•Œ๋งŒ ET์˜ ๋ณต์žกํ•จ์„ ๊ฐ์ˆ˜ํ•œ๋‹ค.


10. epoll ์œ„์— ์–นํžŒ ์ƒ์œ„ ๊ฐœ๋…๋“ค

์‹ค๋ฌด์—์„œ epoll์„ ์ง์ ‘ ๋ถ€๋ฅผ ์ผ์€ ๋“œ๋ฌผ๊ณ , ๋ณดํ†ต ๊ทธ ์œ„์— ์–นํžŒ ์ƒ์œ„ ๊ฐœ๋…์„ ํ†ตํ•ด ๋งŒ๋‚œ๋‹ค. ๋Œ€ํ‘œ์ ์ธ ๊ฒƒ๋“ค์ด๋‹ค.

  • event loop: epoll_wait ๋ฃจํ”„์— โ€œ์ค€๋น„๋œ fd โ†’ ๋Œ€์‘ํ•˜๋Š” ์ฝœ๋ฐฑ ์‹คํ–‰ ๋˜๋Š” coroutine ์žฌ๊ฐœโ€๋ฅผ ์–น์€ ๊ฒƒ์ด๋‹ค. Python asyncio, 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

  1. โ€˜Resource temporarily unavailableโ€™, ๊ณง โ€œ์ง€๊ธˆ์€ ์ค€๋น„ ์•ˆ ๋์œผ๋‹ˆ ๋‚˜์ค‘์— ๋‹ค์‹œ ์‹œ๋„ํ•˜๋ผโ€๋Š” ์‹ ํ˜ธ โ†ฉ

  2. ์†Œ์ผ“ ์ˆ˜์‹  ๋ฒ„ํผ์— ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๊ฐ€ ๋„์ฐฉํ–ˆ์„ ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ํ•ต์‹ฌ ์ฝœ๋ฐฑ ํ•จ์ˆ˜. ๊ธฐ๋ณธ์ ์œผ๋กœ sock_def_readable() ํ•จ์ˆ˜๋กœ ๋งคํ•‘๋˜๋ฉฐ, ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ธฐ ์œ„ํ•ด ๋Œ€๊ธฐ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊นจ์›Œ(Wake-up) I/O ์ฒ˜๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•˜๋„๋ก ์•Œ๋ฆฌ๋Š” ์—ญํ•  โ†ฉ