ื›ื•ืจ I/O ื—ืฉื•ืฃ ืขื ืชื›ื•ื ื•ืช ืžืœืื•ืช

ื›ื•ืจ I/O ื—ืฉื•ืฃ ืขื ืชื›ื•ื ื•ืช ืžืœืื•ืช

ืžื‘ื•ื

ื›ื•ืจ I/O (ื”ื‘ืจื’ื” ื‘ื•ื“ื“ืช ืœื•ืœืืช ืื™ืจื•ืขื™ื) ื”ื•ื ื“ืคื•ืก ืœื›ืชื™ื‘ืช ืชื•ื›ื ื” ื‘ืขื•ืžืก ื’ื‘ื•ื”, ื‘ืฉื™ืžื•ืฉ ื‘ืคืชืจื•ื ื•ืช ืคื•ืคื•ืœืจื™ื™ื ืจื‘ื™ื:

ื‘ืžืืžืจ ื–ื”, ื ื‘ื—ืŸ ืืช ื”ืคืจื˜ื™ื ื”ืงื˜ื ื™ื ืฉืœ ื›ื•ืจ I/O ื•ื›ื™ืฆื“ ื”ื•ื ืคื•ืขืœ, ื ื›ืชื•ื‘ ืžื™ืžื•ืฉ ื‘ืคื—ื•ืช ืž-200 ืฉื•ืจื•ืช ืงื•ื“, ื•ื ื‘ืฆืข ืชื”ืœื™ืš ืคืฉื•ื˜ ืฉืœ ืฉืจืช HTTP ืžืขืœ 40 ืžื™ืœื™ื•ืŸ ื‘ืงืฉื•ืช ืœื“ืงื”.

ืคึฐึผืชึดื™ื—ึท

  • ื”ืžืืžืจ ื ื›ืชื‘ ื›ื“ื™ ืœืขื–ื•ืจ ืœื”ื‘ื™ืŸ ืืช ืชืคืงื•ื“ ื›ื•ืจ ื”-I/O, ื•ืœื›ืŸ ืœื”ื‘ื™ืŸ ืืช ื”ืกื™ื›ื•ื ื™ื ื‘ืขืช ื”ืฉื™ืžื•ืฉ ื‘ื•.
  • ื ื“ืจืฉ ื™ื“ืข ื‘ื™ืกื•ื“ื•ืช ื›ื“ื™ ืœื”ื‘ื™ืŸ ืืช ื”ืžืืžืจ. ืฉืคืช ื’ ื•ื ื™ืกื™ื•ืŸ ืžืกื•ื™ื ื‘ืคื™ืชื•ื— ืืคืœื™ืงืฆื™ื•ืช ืจืฉืช.
  • ื›ืœ ื”ืงื•ื“ ื›ืชื•ื‘ ื‘ืฉืคืช C ืืš ื•ืจืง ืœืคื™ (ื–ื”ื™ืจื•ืช: PDF ืืจื•ืš) ืœืชืงืŸ C11 ืขื‘ื•ืจ ืœื™ื ื•ืงืก ื•ื–ืžื™ืŸ ื‘ GitHub.

ืœืžื” ืœืขืฉื•ืช ืืช ื–ื”?

ืขื ื”ืคื•ืคื•ืœืจื™ื•ืช ื”ื’ื•ื‘ืจืช ืฉืœ ื”ืื™ื ื˜ืจื ื˜, ืฉืจืชื™ ืื™ื ื˜ืจื ื˜ ื”ื—ืœื• ืœื˜ืคืœ ื‘ืžืกืคืจ ืจื‘ ืฉืœ ื—ื™ื‘ื•ืจื™ื ื‘ื•-ื–ืžื ื™ืช, ื•ืœื›ืŸ ื ื•ืกื• ืฉืชื™ ื’ื™ืฉื•ืช: ื—ืกื™ืžืช I/O ื‘ืžืกืคืจ ืจื‘ ืฉืœ ืฉืจืฉื•ืจื™ ืžืขืจื›ืช ื”ืคืขืœื” ื•-I/O ืœื ื—ื•ืกื ื‘ืฉื™ืœื•ื‘ ืขื ืžืขืจื›ืช ื”ืชืจืื•ืช ืขืœ ืื™ืจื•ืขื™ื, ื”ื ืงืจืืช ื’ื "ื‘ื•ืจืจ ืžืขืจื›ืช" (ืืคื•ืœ/kqueue/IOCP/ื•ื›ื•).

ื”ื’ื™ืฉื” ื”ืจืืฉื•ื ื” ื›ืœืœื” ื™ืฆื™ืจืช ืฉืจืฉื•ืจ ืžืขืจื›ืช ื”ืคืขืœื” ื—ื“ืฉ ืขื‘ื•ืจ ื›ืœ ื—ื™ื‘ื•ืจ ื ื›ื ืก. ื”ื—ื™ืกืจื•ืŸ ืฉืœื• ื”ื•ื ืžื“ืจื’ื™ื•ืช ื’ืจื•ืขื”: ืžืขืจื›ืช ื”ื”ืคืขืœื” ืชืฆื˜ืจืš ืœื™ื™ืฉื ืจื‘ื™ื ืžืขื‘ืจื™ ื”ืงืฉืจ ะธ ืฉื™ื—ื•ืช ืžืขืจื›ืช. ืžื“ื•ื‘ืจ ื‘ืคืขื•ืœื•ืช ื™ืงืจื•ืช ื•ื™ื›ื•ืœื•ืช ืœื”ื•ื‘ื™ืœ ืœืžื—ืกื•ืจ ื‘-RAM ืคื ื•ื™ ืขื ืžืกืคืจ ืžืจืฉื™ื ืฉืœ ื—ื™ื‘ื•ืจื™ื.

ื”ื’ืจืกื” ืฉืฉื•ื ืชื” ืžื“ื’ื™ืฉื” ืžืกืคืจ ืงื‘ื•ืข ืฉืœ ื—ื•ื˜ื™ื (ืžืื’ืจ ืฉืจืฉื•ืจื™ื), ื•ื‘ื›ืš ืžื•ื ืข ืžื”ืžืขืจื›ืช ืœื‘ื˜ืœ ืืช ื”ื‘ื™ืฆื•ืข, ืืš ื‘ื•-ื–ืžื ื™ืช ืžืฆื™ื’ื” ื‘ืขื™ื” ื—ื“ืฉื”: ืื ืžืื’ืจ ืืฉื›ื•ืœื•ืช ื ื—ืกื ื›ืขืช ืขืœ ื™ื“ื™ ืคืขื•ืœื•ืช ืงืจื™ืื” ืืจื•ื›ื•ืช, ืื– ืฉืงืขื™ื ืื—ืจื™ื ืฉื›ื‘ืจ ืžืกื•ื’ืœื™ื ืœืงื‘ืœ ื ืชื•ื ื™ื ืœื ื™ื•ื›ืœื• ืขืฉื” ื›ืš.

ื”ื’ื™ืฉื” ื”ืฉื ื™ื™ื” ืžืฉืชืžืฉืช ืžืขืจื›ืช ื”ื•ื“ืขื•ืช ืขืœ ืื™ืจื•ืขื™ื (ื‘ื•ืจืจ ืžืขืจื›ืช) ืžืกื•ืคืง ืขืœ ื™ื“ื™ ืžืขืจื›ืช ื”ื”ืคืขืœื”. ืžืืžืจ ื–ื” ื“ืŸ ื‘ืกื•ื’ ื”ื ืคื•ืฅ ื‘ื™ื•ืชืจ ืฉืœ ื‘ื•ืจืจ ืžืขืจื›ืช, ื‘ื”ืชื‘ืกืก ืขืœ ื”ืชืจืื•ืช (ืื™ืจื•ืขื™ื, ื”ืชืจืื•ืช) ืขืœ ืžื•ื›ื ื•ืช ืœืคืขื•ืœื•ืช ืงืœื˜/ืคืœื˜, ื•ืœื ืขืœ ื”ื•ื“ืขื•ืช ืขืœ ื”ืฉืœืžืชื. ื“ื•ื’ืžื” ืคืฉื•ื˜ื” ืœืฉื™ืžื•ืฉ ื‘ื• ื™ื›ื•ืœื” ืœื”ื™ื•ืช ืžื™ื•ืฆื’ืช ืขืœ ื™ื“ื™ ืชืจืฉื™ื ื”ื‘ืœื•ืง ื”ื‘ื:

ื›ื•ืจ I/O ื—ืฉื•ืฃ ืขื ืชื›ื•ื ื•ืช ืžืœืื•ืช

ื”ื”ื‘ื“ืœ ื‘ื™ืŸ ื”ื’ื™ืฉื•ืช ื”ืœืœื• ื”ื•ื ื›ื“ืœืงืžืŸ:

  • ื—ืกื™ืžืช ืคืขื•ืœื•ืช ืงืœื˜/ืคืœื˜ ืœึฐื”ึทืฉืึฐืขื•ึนืช ื–ืจื™ืžืช ืžืฉืชืžืฉื™ื ืขื“ืขื“ ืฉืžืขืจื›ืช ื”ื”ืคืขืœื” ืชื”ื™ื” ืชืงื™ื ื” ืื™ื—ื•ื™ ื ึดื›ื ึธืก ืžื ื•ืช IP ืœื–ืจื ื‘ืชื™ื (TCP, ืžืงื‘ืœ ื ืชื•ื ื™ื) ืื• ืฉืœื ื™ื”ื™ื” ืžืกืคื™ืง ืžืงื•ื ืคื ื•ื™ ื‘ืžืื’ืจื™ ื”ื›ืชื™ื‘ื” ื”ืคื ื™ืžื™ื™ื ืœืฉืœื™ื—ื” ืฉืœืื—ืจ ืžื›ืŸ ื‘ืืžืฆืขื•ืช NIC (ืฉืœื™ื—ืช ื ืชื•ื ื™ื).
  • ื‘ื•ืจืจ ืžืขืจื›ืช ืื—ืจื™ ื–ืžืŸ ืžื” ืžื•ื“ื™ืข ืœืชื•ื›ื ื™ืช ืฉืžืขืจื›ืช ื”ื”ืคืขืœื” ื›ื‘ืจ ืžื ื•ืช IP ืžืื•ื—ื•ืช (TCP, ืงืœื™ื˜ืช ื ืชื•ื ื™ื) ืื• ืžืกืคื™ืง ืžืงื•ื ื‘ืžืื’ืจื™ ื›ืชื™ื‘ื” ืคื ื™ืžื™ื™ื ื›ื‘ืจ ื–ืžื™ืŸ (ืฉืœื™ื—ืช ื ืชื•ื ื™ื).

ืœืกื™ื›ื•ื, ืฉืžื™ืจืช ืฉืจืฉื•ืจ ืฉืœ ืžืขืจื›ืช ื”ืคืขืœื” ืœื›ืœ I/O ื”ื™ื ื‘ื–ื‘ื•ื– ืฉืœ ื›ื•ื— ืžื—ืฉื•ื‘, ืžื›ื™ื•ื•ืŸ ืฉื‘ืžืฆื™ืื•ืช, ื”ืฉืจืฉื•ืจื™ื ืœื ืขื•ืฉื™ื ืขื‘ื•ื“ื” ืžื•ืขื™ืœื” (ืžื›ืืŸ ืžื’ื™ืข ื”ืžื•ื ื— "ื”ืคืกืงืช ืชื•ื›ื ื”"). ื‘ื•ืจืจ ื”ืžืขืจื›ืช ืคื•ืชืจ ื‘ืขื™ื” ื–ื•, ื•ืžืืคืฉืจ ืœืชื•ื›ื ื™ืช ื”ืžืฉืชืžืฉ ืœื”ืฉืชืžืฉ ื‘ืžืฉืื‘ื™ CPU ื‘ืฆื•ืจื” ื”ืจื‘ื” ื™ื•ืชืจ ื—ืกื›ื•ื ื™ืช.

ื“ื’ื ื›ื•ืจ I/O

ื›ื•ืจ ื”-I/O ืคื•ืขืœ ื›ืฉื›ื‘ื” ื‘ื™ืŸ ื‘ื•ืจืจ ื”ืžืขืจื›ืช ืœืงื•ื“ ื”ืžืฉืชืžืฉ. ืขืงืจื•ืŸ ืคืขื•ืœืชื• ืžืชื•ืืจ ืขืœ ื™ื“ื™ ืชืจืฉื™ื ื”ื‘ืœื•ืง ื”ื‘ื:

ื›ื•ืจ I/O ื—ืฉื•ืฃ ืขื ืชื›ื•ื ื•ืช ืžืœืื•ืช

  • ื”ืจืฉื• ืœื™ ืœื”ื–ื›ื™ืจ ืœื›ื ืฉืื™ืจื•ืข ื”ื•ื ื”ื•ื“ืขื” ืขืœ ื›ืš ืฉืฉืงืข ืžืกื•ื™ื ืžืกื•ื’ืœ ืœื‘ืฆืข ืคืขื•ืœืช I/O ืœื ื—ื•ืกืžืช.
  • ืžื˜ืคืœ ื‘ืื™ืจื•ืขื™ื ื”ื•ื ืคื•ื ืงืฆื™ื” ื”ื ืงืจืืช ืขืœ ื™ื“ื™ ื›ื•ืจ ื”-I/O ื›ืืฉืจ ืื™ืจื•ืข ืžืชืงื‘ืœ, ืืฉืจ ืœืื—ืจ ืžื›ืŸ ืžื‘ืฆืข ืคืขื•ืœืช I/O ืœื ื—ื•ืกืžืช.

ื—ืฉื•ื‘ ืœืฆื™ื™ืŸ ื›ื™ ื›ื•ืจ ื”-I/O ื”ื•ื ื‘ื”ื’ื“ืจื” ื—ื“-ื”ื‘ืจื’ื”, ืืš ืื™ืŸ ื“ื‘ืจ ืฉืžืคืจื™ืข ืœืžื•ืฉื’ ืœื”ืฉืชืžืฉ ื‘ืกื‘ื™ื‘ื” ืžืจื•ื‘ืช-ื”ื‘ืจื’ื” ื‘ื™ื—ืก ืฉืœ 1 ื—ื•ื˜: ื›ื•ืจ 1, ื•ื‘ื›ืš ืœืžื—ื–ืจ ืืช ื›ืœ ืœื™ื‘ื•ืช ื”-CPU.

ะ ะตะฐะปะธะทะฐั†ะธั

ื ืžืงื ืืช ื”ืžืžืฉืง ื”ืฆื™ื‘ื•ืจื™ ื‘ืงื•ื‘ืฅ reactor.h, ื•ื™ื™ืฉื•ื - ื‘ reactor.c. reactor.h ื™ื›ืœื•ืœ ืืช ื”ื”ื›ืจื–ื•ืช ื”ื‘ืื•ืช:

ื”ืฆื’ ื”ืฆื”ืจื•ืช ื‘ื›ื•ืจ.ื—

typedef struct reactor Reactor;

/*
 * ะฃะบะฐะทะฐั‚ะตะปัŒ ะฝะฐ ั„ัƒะฝะบั†ะธัŽ, ะบะพั‚ะพั€ะฐั ะฑัƒะดะตั‚ ะฒั‹ะทั‹ะฒะฐั‚ัŒัั I/O ั€ะตะฐะบั‚ะพั€ะพะผ ะฟั€ะธ ะฟะพัั‚ัƒะฟะปะตะฝะธะธ
 * ัะพะฑั‹ั‚ะธั ะพั‚ ัะธัั‚ะตะผะฝะพะณะพ ัะตะปะตะบั‚ะพั€ะฐ.
 */
typedef void (*Callback)(void *arg, int fd, uint32_t events);

/*
 * ะ’ะพะทะฒั€ะฐั‰ะฐะตั‚ `NULL` ะฒ ัะปัƒั‡ะฐะต ะพัˆะธะฑะบะธ, ะฝะต-`NULL` ัƒะบะฐะทะฐั‚ะตะปัŒ ะฝะฐ `Reactor` ะฒ
 * ะฟั€ะพั‚ะธะฒะฝะพะผ ัะปัƒั‡ะฐะต.
 */
Reactor *reactor_new(void);

/*
 * ะžัะฒะพะฑะพะถะดะฐะตั‚ ัะธัั‚ะตะผะฝั‹ะน ัะตะปะตะบั‚ะพั€, ะฒัะต ะทะฐั€ะตะณะธัั‚ั€ะธั€ะพะฒะฐะฝะฝั‹ะต ัะพะบะตั‚ั‹ ะฒ ะดะฐะฝะฝั‹ะน ะผะพะผะตะฝั‚
 * ะฒั€ะตะผะตะฝะธ ะธ ัะฐะผ I/O ั€ะตะฐะบั‚ะพั€.
 *
 * ะกะปะตะดัƒัŽั‰ะธะต ั„ัƒะฝะบั†ะธะธ ะฒะพะทะฒั€ะฐั‰ะฐัŽั‚ -1 ะฒ ัะปัƒั‡ะฐะต ะพัˆะธะฑะบะธ, 0 ะฒ ัะปัƒั‡ะฐะต ัƒัะฟะตั…ะฐ.
 */
int reactor_destroy(Reactor *reactor);

int reactor_register(const Reactor *reactor, int fd, uint32_t interest,
                     Callback callback, void *callback_arg);
int reactor_deregister(const Reactor *reactor, int fd);
int reactor_reregister(const Reactor *reactor, int fd, uint32_t interest,
                       Callback callback, void *callback_arg);

/*
 * ะ—ะฐะฟัƒัะบะฐะตั‚ ั†ะธะบะป ัะพะฑั‹ั‚ะธะน ั ั‚ะฐะนะผ-ะฐัƒั‚ะพะผ `timeout`.
 *
 * ะญั‚ะฐ ั„ัƒะฝะบั†ะธั ะฟะตั€ะตะดะฐัั‚ ัƒะฟั€ะฐะฒะปะตะฝะธะต ะฒั‹ะทั‹ะฒะฐัŽั‰ะตะผัƒ ะบะพะดัƒ ะตัะปะธ ะพั‚ะฒะตะดั‘ะฝะฝะพะต ะฒั€ะตะผั ะฒั‹ัˆะปะพ
 * ะธะปะธ/ะธ ะฟั€ะธ ะพั‚ััƒั‚ัั‚ะฒะธะธ ะทะฐั€ะตะณะธัั‚ั€ะธั€ะพะฒะฐะฝะฝั‹ั… ัะพะบะตั‚ะพะฒ.
 */
int reactor_run(const Reactor *reactor, time_t timeout);

ืžื‘ื ื” ื›ื•ืจ ื”-I/O ืžื•ืจื›ื‘ ืž ืžืชืืจ ืงื•ื‘ืฅ ื‘ื•ื—ืจ ืืคื•ืœ ะธ ื˜ื‘ืœืื•ืช ื—ืฉื™ืฉ GHashTable, ืืฉืจ ืžืžืคื” ื›ืœ ืฉืงืข ืืœื™ื• CallbackData (ืžื‘ื ื” ืฉืœ ืžื˜ืคืœ ื‘ืื™ืจื•ืขื™ื ื•ืืจื’ื•ืžื ื˜ ืžืฉืชืžืฉ ืขื‘ื•ืจื•).

ื”ืฆื’ Reactor ื•-CallbackData

struct reactor {
    int epoll_fd;
    GHashTable *table; // (int, CallbackData)
};

typedef struct {
    Callback callback;
    void *arg;
} CallbackData;

ืœื™ื“ื™ืขืชืš, ืืคืฉืจื ื• ืืช ื”ื™ื›ื•ืœืช ืœื˜ืคืœ ืกื•ื’ ืœื ืฉืœื ืœืคื™ ื”ืžื“ื“. IN reactor.h ืื ื• ืžื›ืจื™ื–ื™ื ืขืœ ื”ืžื‘ื ื” reactor, ื‘ืขื•ื“ ื‘ reactor.c ืื ื• ืžื’ื“ื™ืจื™ื ืื•ืชื•, ื•ื‘ื›ืš ืžื•ื ืขื™ื ืžื”ืžืฉืชืžืฉ ืœืฉื ื•ืช ื‘ืžืคื•ืจืฉ ืืช ื”ืฉื“ื•ืช ืฉืœื•. ื–ื” ืื—ื“ ื”ื“ืคื•ืกื™ื ื”ืกืชืจืช ื ืชื•ื ื™ื, ืฉืžืชืื™ื ื‘ืื•ืคืŸ ืชืžืฆื™ืชื™ ืœืกืžื ื˜ื™ืงื” ืฉืœ C.

ืคื•ื ืงืฆื™ื•ืช reactor_register, reactor_deregister ะธ reactor_reregister ืขื“ื›ืŸ ืืช ืจืฉื™ืžืช ืฉืงืขื™ ื”ืขื ื™ื™ืŸ ื•ืžื˜ืคืœื™ ืื™ืจื•ืขื™ื ืžืชืื™ืžื™ื ื‘ื‘ื•ืจืจ ื”ืžืขืจื›ืช ื•ื‘ื˜ื‘ืœืช ื”ื’ื™ื‘ื•ื‘.

ื”ืฆื’ ืคื•ื ืงืฆื™ื•ืช ืจื™ืฉื•ื

#define REACTOR_CTL(reactor, op, fd, interest)                                 
    if (epoll_ctl(reactor->epoll_fd, op, fd,                                   
                  &(struct epoll_event){.events = interest,                    
                                        .data = {.fd = fd}}) == -1) {          
        perror("epoll_ctl");                                                   
        return -1;                                                             
    }

int reactor_register(const Reactor *reactor, int fd, uint32_t interest,
                     Callback callback, void *callback_arg) {
    REACTOR_CTL(reactor, EPOLL_CTL_ADD, fd, interest)
    g_hash_table_insert(reactor->table, int_in_heap(fd),
                        callback_data_new(callback, callback_arg));
    return 0;
}

int reactor_deregister(const Reactor *reactor, int fd) {
    REACTOR_CTL(reactor, EPOLL_CTL_DEL, fd, 0)
    g_hash_table_remove(reactor->table, &fd);
    return 0;
}

int reactor_reregister(const Reactor *reactor, int fd, uint32_t interest,
                       Callback callback, void *callback_arg) {
    REACTOR_CTL(reactor, EPOLL_CTL_MOD, fd, interest)
    g_hash_table_insert(reactor->table, int_in_heap(fd),
                        callback_data_new(callback, callback_arg));
    return 0;
}

ืœืื—ืจ ืฉื›ื•ืจ ื”-I/O ื™ื™ืจื˜ ืืช ื”ืื™ืจื•ืข ืขื ื”ืžืชืืจ fd, ื”ื•ื ืงื•ืจื ืœืžื˜ืคืœ ื”ืื™ืจื•ืข ื”ืžืชืื™ื, ืฉืืœื™ื• ื”ื•ื ืขื•ื‘ืจ fd, ืžืกื›ืช ืงืฆืช ืื™ืจื•ืขื™ื ืฉื ื•ืฆืจื• ื•ืžืฆื‘ื™ืข ืœืžืฉืชืžืฉ void.

ื”ืฆื’ ืืช ื”ืคื•ื ืงืฆื™ื” reactor_run()

int reactor_run(const Reactor *reactor, time_t timeout) {
    int result;
    struct epoll_event *events;
    if ((events = calloc(MAX_EVENTS, sizeof(*events))) == NULL)
        abort();

    time_t start = time(NULL);

    while (true) {
        time_t passed = time(NULL) - start;
        int nfds =
            epoll_wait(reactor->epoll_fd, events, MAX_EVENTS, timeout - passed);

        switch (nfds) {
        // ะžัˆะธะฑะบะฐ
        case -1:
            perror("epoll_wait");
            result = -1;
            goto cleanup;
        // ะ’ั€ะตะผั ะฒั‹ัˆะปะพ
        case 0:
            result = 0;
            goto cleanup;
        // ะฃัะฟะตัˆะฝะฐั ะพะฟะตั€ะฐั†ะธั
        default:
            // ะ’ั‹ะทะฒะฐั‚ัŒ ะพะฑั€ะฐะฑะพั‚ั‡ะธะบะพะฒ ัะพะฑั‹ั‚ะธะน
            for (int i = 0; i < nfds; i++) {
                int fd = events[i].data.fd;

                CallbackData *callback =
                    g_hash_table_lookup(reactor->table, &fd);
                callback->callback(callback->arg, fd, events[i].events);
            }
        }
    }

cleanup:
    free(events);
    return result;
}

ืœืกื™ื›ื•ื, ืฉืจืฉืจืช ืงืจื™ืื•ืช ื”ืคื•ื ืงืฆื™ื•ืช ื‘ืงื•ื“ ื”ืžืฉืชืžืฉ ืชืจืื” ืืช ื”ืฆื•ืจื” ื”ื‘ืื”:

ื›ื•ืจ I/O ื—ืฉื•ืฃ ืขื ืชื›ื•ื ื•ืช ืžืœืื•ืช

ืฉืจืช ืคืชื™ืœ ื‘ื•ื“ื“

ืขืœ ืžื ืช ืœื‘ื“ื•ืง ืืช ื›ื•ืจ ื”-I/O ื‘ืขื•ืžืก ื’ื‘ื•ื”, ื ื›ืชื•ื‘ ืฉืจืช HTTP ืคืฉื•ื˜ ืฉืžื’ื™ื‘ ืœื›ืœ ื‘ืงืฉื” ืขื ืชืžื•ื ื”.

ื”ืชื™ื™ื—ืกื•ืช ืžื”ื™ืจื” ืœืคืจื•ื˜ื•ืงื•ืœ HTTP

HTTP - ื–ื” ื”ืคืจื•ื˜ื•ืงื•ืœ ืจืžืช ื”ื™ื™ืฉื•ื, ืžืฉืžืฉ ื‘ืขื™ืงืจ ืœืื™ื ื˜ืจืืงืฆื™ื” ื‘ื™ืŸ ืฉืจืช ืœื“ืคื“ืคืŸ.

ื ื™ืชืŸ ืœื”ืฉืชืžืฉ ื‘-HTTP ื‘ืงืœื•ืช ืชื—ื‘ื•ืจื” ื ื•ื”ืœ TCP, ืฉืœื™ื—ื” ื•ืงื‘ืœื” ืฉืœ ื”ื•ื“ืขื•ืช ื‘ืคื•ืจืžื˜ ืฉืฆื•ื™ืŸ ืžึดืคืจึธื˜.

ืคื•ืจืžื˜ ื‘ืงืฉื”

<ะšะžะœะะะ”ะ> <URI> <ะ’ะ•ะ ะกะ˜ะฏ HTTP>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš 1>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš 2>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš N>CRLF CRLF
<ะ”ะะะะซะ•>

  • CRLF ื”ื•ื ืจืฆืฃ ืฉืœ ืฉืชื™ ืชื•ื•ื™ื: r ะธ n, ื”ืคืจื“ื” ื‘ื™ืŸ ื”ืฉื•ืจื” ื”ืจืืฉื•ื ื” ืฉืœ ื”ื‘ืงืฉื”, ื›ื•ืชืจื•ืช ื•ื ืชื•ื ื™ื.
  • <ะšะžะœะะะ”ะ> - ืื—ื“ ืž CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE. ื”ื“ืคื“ืคืŸ ื™ืฉืœื— ืคืงื•ื“ื” ืœืฉืจืช ืฉืœื ื• GET, ื›ืœื•ืžืจ "ืฉืœื— ืœื™ ืืช ืชื•ื›ืŸ ื”ืงื•ื‘ืฅ."
  • <URI> - ืžื–ื”ื” ืžืฉืื‘ ืื—ื™ื“. ืœื“ื•ื’ืžื”, ืื URI = /index.html, ืื– ื”ืœืงื•ื— ืžื‘ืงืฉ ืืช ื”ื“ืฃ ื”ืจืืฉื™ ืฉืœ ื”ืืชืจ.
  • <ะ’ะ•ะ ะกะ˜ะฏ HTTP> - ื’ืจืกื” ืฉืœ ืคืจื•ื˜ื•ืงื•ืœ HTTP ื‘ืคื•ืจืžื˜ HTTP/X.Y. ื”ื’ืจืกื” ื”ื ืคื•ืฆื” ื‘ื™ื•ืชืจ ื›ื™ื•ื ื”ื™ื HTTP/1.1.
  • <ะ—ะะ“ะžะ›ะžะ’ะžะš N> ื”ื•ื ืฆืžื“ ืžืคืชื—-ืขืจืš ื‘ืคื•ืจืžื˜ <ะšะ›ะฎะง>: <ะ—ะะะงะ•ะะ˜ะ•>, ื ืฉืœื— ืœืฉืจืช ืœื ื™ืชื•ื— ื ื•ืกืฃ.
  • <ะ”ะะะะซะ•> - ื ืชื•ื ื™ื ื”ื ื“ืจืฉื™ื ืขืœ ื™ื“ื™ ื”ืฉืจืช ืœื‘ื™ืฆื•ืข ื”ืคืขื•ืœื”. ืœืขืชื™ื ืงืจื•ื‘ื•ืช ื–ื” ืคืฉื•ื˜ JSON ืื• ื›ืœ ืคื•ืจืžื˜ ืื—ืจ.

ืคื•ืจืžื˜ ืชื’ื•ื‘ื”

<ะ’ะ•ะ ะกะ˜ะฏ HTTP> <ะšะžะ” ะกะขะะขะฃะกะ> <ะžะŸะ˜ะกะะะ˜ะ• ะกะขะะขะฃะกะ>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš 1>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš 2>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš N>CRLF CRLF
<ะ”ะะะะซะ•>

  • <ะšะžะ” ะกะขะะขะฃะกะ> ื”ื•ื ืžืกืคืจ ื”ืžื™ื™ืฆื’ ืืช ื”ืชื•ืฆืื” ืฉืœ ื”ืคืขื•ืœื”. ื”ืฉืจืช ืฉืœื ื• ืชืžื™ื“ ื™ื—ื–ื™ืจ ืกื˜ื˜ื•ืก 200 (ืคืขื•ืœื” ืžื•ืฆืœื—ืช).
  • <ะžะŸะ˜ะกะะะ˜ะ• ะกะขะะขะฃะกะ> - ื™ื™ืฆื•ื’ ืžื—ืจื•ื–ืช ืฉืœ ืงื•ื“ ื”ืžืฆื‘. ืขื‘ื•ืจ ืงื•ื“ ืกื˜ื˜ื•ืก 200 ื–ื”ื• OK.
  • <ะ—ะะ“ะžะ›ะžะ’ะžะš N> - ื›ื•ืชืจืช ื‘ืื•ืชื• ืคื•ืจืžื˜ ื›ืžื• ื‘ื‘ืงืฉื”. ื ื—ื–ื™ืจ ืืช ื”ื›ื•ืชืจื•ืช Content-Length (ื’ื•ื“ืœ ืงื•ื‘ืฅ) ื• Content-Type: text/html (ืกื•ื’ ื ืชื•ื ื™ ื”ื—ื–ืจ).
  • <ะ”ะะะะซะ•> - ื ืชื•ื ื™ื ืฉื”ืชื‘ืงืฉื• ืขืœ ื™ื“ื™ ื”ืžืฉืชืžืฉ. ื‘ืžืงืจื” ืฉืœื ื•, ื–ื” ื”ื ืชื™ื‘ ืœืชืžื•ื ื” ืคื ื™ืžื” HTML.

ืงื•ื‘ืฅ http_server.c (ืฉืจืช ืคืชื™ืœ ื‘ื•ื“ื“) ื›ื•ืœืœ ืงื•ื‘ืฅ common.h, ื”ืžื›ื™ืœ ืืช ืื‘ื•ืช ื”ื˜ื™ืคื•ืก ืฉืœ ื”ืคื•ื ืงืฆื™ื•ืช ื”ื‘ืื•ืช:

ื”ืฆื’ ืื‘ื•ืช ื˜ื™ืคื•ืก ืฉืœ ืคื•ื ืงืฆื™ื•ืช ื‘ืžืฉื•ืชืฃ.h

/*
 * ะžะฑั€ะฐะฑะพั‚ั‡ะธะบ ัะพะฑั‹ั‚ะธะน, ะบะพั‚ะพั€ั‹ะน ะฒั‹ะทะพะฒะตั‚ัั ะฟะพัะปะต ั‚ะพะณะพ, ะบะฐะบ ัะพะบะตั‚ ะฑัƒะดะตั‚
 * ะณะพั‚ะพะฒ ะฟั€ะธะฝัั‚ัŒ ะฝะพะฒะพะต ัะพะตะดะธะฝะตะฝะธะต.
 */
static void on_accept(void *arg, int fd, uint32_t events);

/*
 * ะžะฑั€ะฐะฑะพั‚ั‡ะธะบ ัะพะฑั‹ั‚ะธะน, ะบะพั‚ะพั€ั‹ะน ะฒั‹ะทะพะฒะตั‚ัั ะฟะพัะปะต ั‚ะพะณะพ, ะบะฐะบ ัะพะบะตั‚ ะฑัƒะดะตั‚
 * ะณะพั‚ะพะฒ ะพั‚ะฟั€ะฐะฒะธั‚ัŒ HTTP ะพั‚ะฒะตั‚.
 */
static void on_send(void *arg, int fd, uint32_t events);

/*
 * ะžะฑั€ะฐะฑะพั‚ั‡ะธะบ ัะพะฑั‹ั‚ะธะน, ะบะพั‚ะพั€ั‹ะน ะฒั‹ะทะพะฒะตั‚ัั ะฟะพัะปะต ั‚ะพะณะพ, ะบะฐะบ ัะพะบะตั‚ ะฑัƒะดะตั‚
 * ะณะพั‚ะพะฒ ะฟั€ะธะฝัั‚ัŒ ั‡ะฐัั‚ัŒ HTTP ะทะฐะฟั€ะพัะฐ.
 */
static void on_recv(void *arg, int fd, uint32_t events);

/*
 * ะŸะตั€ะตะฒะพะดะธั‚ ะฒั…ะพะดัั‰ะตะต ัะพะตะดะธะฝะตะฝะธะต ะฒ ะฝะตะฑะปะพะบะธั€ัƒัŽั‰ะธะน ั€ะตะถะธะผ.
 */
static void set_nonblocking(int fd);

/*
 * ะŸะตั‡ะฐั‚ะฐะตั‚ ะฟะตั€ะตะดะฐะฝะฝั‹ะต ะฐั€ะณัƒะผะตะฝั‚ั‹ ะฒ stderr ะธ ะฒั‹ั…ะพะดะธั‚ ะธะท ะฟั€ะพั†ะตััะฐ ั
 * ะบะพะดะพะผ `EXIT_FAILURE`.
 */
static noreturn void fail(const char *format, ...);

/*
 * ะ’ะพะทะฒั€ะฐั‰ะฐะตั‚ ั„ะฐะนะปะพะฒั‹ะน ะดะตัะบั€ะธะฟั‚ะพั€ ัะพะบะตั‚ะฐ, ัะฟะพัะพะฑะฝะพะณะพ ะฟั€ะธะฝะธะผะฐั‚ัŒ ะฝะพะฒั‹ะต
 * TCP ัะพะตะดะธะฝะตะฝะธั.
 */
static int new_server(bool reuse_port);

ื”ืžืืงืจื• ื”ืคื•ื ืงืฆื™ื•ื ืœื™ ืžืชื•ืืจ ื’ื ื›ืŸ SAFE_CALL() ื•ื”ืคื•ื ืงืฆื™ื” ืžื•ื’ื“ืจืช fail(). ื”ืžืืงืจื• ืžืฉื•ื•ื” ืืช ื”ืขืจืš ืฉืœ ื”ื‘ื™ื˜ื•ื™ ืขื ื”ืฉื’ื™ืื”, ื•ืื ื”ืชื ืื™ ื ื›ื•ืŸ, ืงื•ืจื ืœืคื•ื ืงืฆื™ื” fail():

#define SAFE_CALL(call, error)                                                 
    do {                                                                       
        if ((call) == error) {                                                   
            fail("%s", #call);                                                 
        }                                                                      
    } while (false)

ืคื•ื ืงืฆื™ื” fail() ืžื“ืคื™ืก ืืช ื”ืืจื’ื•ืžื ื˜ื™ื ืฉื”ื•ืขื‘ืจื• ืœืžืกื•ืฃ (ื›ืžื• printf()) ื•ืžืกื™ื™ื ืืช ื”ืชื•ื›ื ื™ืช ืขื ื”ืงื•ื“ EXIT_FAILURE:

static noreturn void fail(const char *format, ...) {
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    fprintf(stderr, ": %sn", strerror(errno));
    exit(EXIT_FAILURE);
}

ืคื•ื ืงืฆื™ื” new_server() ืžื—ื–ื™ืจื” ืืช ืžืชืืจ ื”ืงื•ื‘ืฅ ืฉืœ ืฉืงืข "ืฉืจืช" ืฉื ื•ืฆืจ ืขืœ ื™ื“ื™ ืงืจื™ืื•ืช ืžืขืจื›ืช socket(), bind() ะธ listen() ื•ืžืกื•ื’ืœ ืœืงื‘ืœ ื—ื™ื‘ื•ืจื™ื ื ื›ื ืกื™ื ื‘ืžืฆื‘ ืœื ื—ื•ืกื.

ื”ืฆื’ ืืช ื”ืคื•ื ืงืฆื™ื” new_server().

static int new_server(bool reuse_port) {
    int fd;
    SAFE_CALL((fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)),
              -1);

    if (reuse_port) {
        SAFE_CALL(
            setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)),
            -1);
    }

    struct sockaddr_in addr = {.sin_family = AF_INET,
                               .sin_port = htons(SERVER_PORT),
                               .sin_addr = {.s_addr = inet_addr(SERVER_IPV4)},
                               .sin_zero = {0}};

    SAFE_CALL(bind(fd, (struct sockaddr *)&addr, sizeof(addr)), -1);
    SAFE_CALL(listen(fd, SERVER_BACKLOG), -1);
    return fd;
}

  • ืฉื™ืžื• ืœื‘ ืฉื”ืฉืงืข ื ื•ืฆืจ ื‘ืชื—ื™ืœื” ื‘ืžืฆื‘ ืœื ื—ื•ืกื ื‘ืืžืฆืขื•ืช ื”ื“ื’ืœ SOCK_NONBLOCKื›ืš ืฉื‘ืคื•ื ืงืฆื™ื” on_accept() (ืงืจื ืขื•ื“) ืฉื™ื—ืช ืžืขืจื›ืช accept() ืœื ืขืฆืจ ืืช ื‘ื™ืฆื•ืข ื”ืฉืจืฉื•ืจ.
  • ืื reuse_port ืฉื•ื•ื” true, ืื– ืคื•ื ืงืฆื™ื” ื–ื• ืชื’ื“ื™ืจ ืืช ื”ืฉืงืข ืขื ื”ืืคืฉืจื•ืช SO_REUSEPORT ื‘ืืžืฆืขื•ืช setsockopt()ืœื”ืฉืชืžืฉ ื‘ืื•ืชื” ื™ืฆื™ืื” ื‘ืกื‘ื™ื‘ื” ืžืจื•ื‘ืช ื”ืœื™ื›ื™ ื”ืœื™ื›ื” (ืจืื” ืกืขื™ืฃ "ืฉืจืช ืžืจื•ื‘ื” ื”ืœื™ื›ื™").

ืžื ื”ืœ ืื™ืจื•ืขื™ื on_accept() ื ืงืจื ืœืื—ืจ ืฉืžืขืจื›ืช ื”ื”ืคืขืœื” ื™ื•ืฆืจืช ืื™ืจื•ืข EPOLLIN, ื‘ืžืงืจื” ื–ื” ื›ืœื•ืžืจ ื ื™ืชืŸ ืœืงื‘ืœ ืืช ื”ื—ื™ื‘ื•ืจ ื”ื—ื“ืฉ. on_accept() ืžืงื‘ืœ ื—ื™ื‘ื•ืจ ื—ื“ืฉ, ืžืขื‘ื™ืจ ืื•ืชื• ืœืžืฆื‘ ืœื ื—ื•ืกื ื•ื ืจืฉื ืืฆืœ ืžื˜ืคืœ ื‘ืื™ืจื•ืขื™ื on_recv() ื‘ื›ื•ืจ I/O.

ื”ืฆื’ ืืช ื”ืคื•ื ืงืฆื™ื” on_accept()

static void on_accept(void *arg, int fd, uint32_t events) {
    int incoming_conn;
    SAFE_CALL((incoming_conn = accept(fd, NULL, NULL)), -1);
    set_nonblocking(incoming_conn);
    SAFE_CALL(reactor_register(reactor, incoming_conn, EPOLLIN, on_recv,
                               request_buffer_new()),
              -1);
}

ืžื ื”ืœ ืื™ืจื•ืขื™ื on_recv() ื ืงืจื ืœืื—ืจ ืฉืžืขืจื›ืช ื”ื”ืคืขืœื” ื™ื•ืฆืจืช ืื™ืจื•ืข EPOLLIN, ื‘ืžืงืจื” ื–ื” ื›ืœื•ืžืจ ืฉื”ื—ื™ื‘ื•ืจ ื ืจืฉื on_accept(), ืžื•ื›ืŸ ืœืงื‘ืœ ื ืชื•ื ื™ื.

on_recv() ืงื•ืจื ื ืชื•ื ื™ื ืžื”ื—ื™ื‘ื•ืจ ืขื“ ืฉื‘ืงืฉืช ื”-HTTP ืžืชืงื‘ืœืช ืœื—ืœื•ื˜ื™ืŸ, ื•ืื– ื”ื•ื ืจื•ืฉื ืžื˜ืคืœ on_send() ื›ื“ื™ ืœืฉืœื•ื— ืชื’ื•ื‘ืช HTTP. ืื ื”ืœืงื•ื— ืžื ืชืง ืืช ื”ื—ื™ื‘ื•ืจ, ื”ืฉืงืข ื ื‘ื˜ืœ ื•ื ืกื’ืจ ื‘ืืžืฆืขื•ืช close().

ื”ืฆื’ ืืช ื”ืคื•ื ืงืฆื™ื” on_recv()

static void on_recv(void *arg, int fd, uint32_t events) {
    RequestBuffer *buffer = arg;

    // ะŸั€ะธะฝะธะผะฐะตะผ ะฒั…ะพะดะฝั‹ะต ะดะฐะฝะฝั‹ะต ะดะพ ั‚ะตั… ะฟะพั€, ั‡ั‚ะพ recv ะฒะพะทะฒั€ะฐั‚ะธั‚ 0 ะธะปะธ ะพัˆะธะฑะบัƒ
    ssize_t nread;
    while ((nread = recv(fd, buffer->data + buffer->size,
                         REQUEST_BUFFER_CAPACITY - buffer->size, 0)) > 0)
        buffer->size += nread;

    // ะšะปะธะตะฝั‚ ะพะฑะพั€ะฒะฐะป ัะพะตะดะธะฝะตะฝะธะต
    if (nread == 0) {
        SAFE_CALL(reactor_deregister(reactor, fd), -1);
        SAFE_CALL(close(fd), -1);
        request_buffer_destroy(buffer);
        return;
    }

    // read ะฒะตั€ะฝัƒะป ะพัˆะธะฑะบัƒ, ะพั‚ะปะธั‡ะฝัƒัŽ ะพั‚ ะพัˆะธะฑะบะธ, ะฟั€ะธ ะบะพั‚ะพั€ะพะน ะฒั‹ะทะพะฒ ะทะฐะฑะปะพะบะธั€ัƒะตั‚
    // ะฟะพั‚ะพะบ
    if (errno != EAGAIN && errno != EWOULDBLOCK) {
        request_buffer_destroy(buffer);
        fail("read");
    }

    // ะŸะพะปัƒั‡ะตะฝ ะฟะพะปะฝั‹ะน HTTP ะทะฐะฟั€ะพั ะพั‚ ะบะปะธะตะฝั‚ะฐ. ะขะตะฟะตั€ัŒ ั€ะตะณะธัั‚ั€ะธั€ัƒะตะผ ะพะฑั€ะฐะฑะพั‚ั‡ะธะบะฐ
    // ัะพะฑั‹ั‚ะธะน ะดะปั ะพั‚ะฟั€ะฐะฒะบะธ ะดะฐะฝะฝั‹ั…
    if (request_buffer_is_complete(buffer)) {
        request_buffer_clear(buffer);
        SAFE_CALL(reactor_reregister(reactor, fd, EPOLLOUT, on_send, buffer),
                  -1);
    }
}

ืžื ื”ืœ ืื™ืจื•ืขื™ื on_send() ื ืงืจื ืœืื—ืจ ืฉืžืขืจื›ืช ื”ื”ืคืขืœื” ื™ื•ืฆืจืช ืื™ืจื•ืข EPOLLOUT, ื›ืœื•ืžืจ ื”ื—ื™ื‘ื•ืจ ื ืจืฉื on_recv(), ืžื•ื›ืŸ ืœืฉืœื•ื— ื ืชื•ื ื™ื. ืคื•ื ืงืฆื™ื” ื–ื• ืฉื•ืœื—ืช ืชื’ื•ื‘ืช HTTP ื”ืžื›ื™ืœื” HTML ืขื ืชืžื•ื ื” ืœืœืงื•ื— ื•ืœืื—ืจ ืžื›ืŸ ืžืฉื ื” ืืช ื”ืžื˜ืคืœ ื‘ืื™ืจื•ืขื™ื ื‘ื—ื–ืจื” on_recv().

ื”ืฆื’ ืืช ื”ืคื•ื ืงืฆื™ื” on_send().

static void on_send(void *arg, int fd, uint32_t events) {
    const char *content = "<img "
                          "src="https://habrastorage.org/webt/oh/wl/23/"
                          "ohwl23va3b-dioerobq_mbx4xaw.jpeg">";
    char response[1024];
    sprintf(response,
            "HTTP/1.1 200 OK" CRLF "Content-Length: %zd" CRLF "Content-Type: "
            "text/html" DOUBLE_CRLF "%s",
            strlen(content), content);

    SAFE_CALL(send(fd, response, strlen(response), 0), -1);
    SAFE_CALL(reactor_reregister(reactor, fd, EPOLLIN, on_recv, arg), -1);
}

ื•ืœื‘ืกื•ืฃ, ื‘ืงื•ื‘ืฅ http_server.c, ื‘ืชืคืงื•ื“ main() ืื ื• ื™ื•ืฆืจื™ื ื›ื•ืจ I/O ื‘ืืžืฆืขื•ืช reactor_new(), ืฆื•ืจ ืฉืงืข ืฉืจืช ื•ืจืฉื•ื ืื•ืชื•, ื”ืคืขืœ ืืช ื”ื›ื•ืจ ื‘ืืžืฆืขื•ืช reactor_run() ืœืžืฉืš ื“ืงื” ืื—ืช ื‘ื“ื™ื•ืง, ื•ืื– ืื ื• ืžืฉื—ืจืจื™ื ืžืฉืื‘ื™ื ื•ื™ื•ืฆืื™ื ืžื”ืชื•ื›ื ื™ืช.

ื”ืฆื’ ืืช http_server.c

#include "reactor.h"

static Reactor *reactor;

#include "common.h"

int main(void) {
    SAFE_CALL((reactor = reactor_new()), NULL);
    SAFE_CALL(
        reactor_register(reactor, new_server(false), EPOLLIN, on_accept, NULL),
        -1);
    SAFE_CALL(reactor_run(reactor, SERVER_TIMEOUT_MILLIS), -1);
    SAFE_CALL(reactor_destroy(reactor), -1);
}

ื‘ื•ื ื ื‘ื“ื•ืง ืฉื”ื›ืœ ืขื•ื‘ื“ ื›ืžืฆื•ืคื”. ืงื•ืžืคื™ืœืฆื™ื” (chmod a+x compile.sh && ./compile.sh ื‘ืฉื•ืจืฉ ื”ืคืจื•ื™ืงื˜) ื•ื”ืคืขืœ ืืช ื”ืฉืจืช ืฉื ื›ืชื‘ ื‘ืขืฆืžื•, ืคืชื— http://127.0.0.1:18470 ื‘ื“ืคื“ืคืŸ ื•ืจืื” ืžื” ืฆื™ืคื™ื ื•:

ื›ื•ืจ I/O ื—ืฉื•ืฃ ืขื ืชื›ื•ื ื•ืช ืžืœืื•ืช

ืžื“ื™ื“ืช ื‘ื™ืฆื•ืขื™ื

ื”ืฆื’ ืืช ืžืคืจื˜ ื”ืžื›ื•ื ื™ืช ืฉืœื™

$ screenfetch
 MMMMMMMMMMMMMMMMMMMMMMMMMmds+.        OS: Mint 19.1 tessa
 MMm----::-://////////////oymNMd+`     Kernel: x86_64 Linux 4.15.0-20-generic
 MMd      /++                -sNMd:    Uptime: 2h 34m
 MMNso/`  dMM    `.::-. .-::.` .hMN:   Packages: 2217
 ddddMMh  dMM   :hNMNMNhNMNMNh: `NMm   Shell: bash 4.4.20
     NMm  dMM  .NMN/-+MMM+-/NMN` dMM   Resolution: 1920x1080
     NMm  dMM  -MMm  `MMM   dMM. dMM   DE: Cinnamon 4.0.10
     NMm  dMM  -MMm  `MMM   dMM. dMM   WM: Muffin
     NMm  dMM  .mmd  `mmm   yMM. dMM   WM Theme: Mint-Y-Dark (Mint-Y)
     NMm  dMM`  ..`   ...   ydm. dMM   GTK Theme: Mint-Y [GTK2/3]
     hMM- +MMd/-------...-:sdds  dMM   Icon Theme: Mint-Y
     -NMm- :hNMNNNmdddddddddy/`  dMM   Font: Noto Sans 9
      -dMNs-``-::::-------.``    dMM   CPU: Intel Core i7-6700 @ 8x 4GHz [52.0ยฐC]
       `/dMNmy+/:-------------:/yMMM   GPU: NV136
          ./ydNMMMMMMMMMMMMMMMMMMMMM   RAM: 2544MiB / 7926MiB
             .MMMMMMMMMMMMMMMMMMM

ื‘ื•ืื• ื ืžื“ื•ื“ ืืช ื”ื‘ื™ืฆื•ืขื™ื ืฉืœ ืฉืจืช ื—ื•ื˜ ื™ื—ื™ื“. ื‘ื•ืื• ื ืคืชื— ืฉื ื™ ื˜ืจืžื™ื ืœื™ื: ื‘ืื—ื“ ื ืจื•ืฅ ./http_server, ื‘ืื—ืจ - wrk. ืœืื—ืจ ื“ืงื”, ื”ื ืชื•ื ื™ื ื”ืกื˜ื˜ื™ืกื˜ื™ื™ื ื”ื‘ืื™ื ื™ื•ืฆื’ื• ื‘ื˜ืจืžื™ื ืœ ื”ืฉื ื™:

$ wrk -c100 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
  8 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   493.52us   76.70us  17.31ms   89.57%
    Req/Sec    24.37k     1.81k   29.34k    68.13%
  11657769 requests in 1.00m, 1.60GB read
Requests/sec: 193974.70
Transfer/sec:     27.19MB

ื”ืฉืจืช ื”ื—ื“-ืคืชื™ืœ ืฉืœื ื• ื”ืฆืœื™ื— ืœืขื‘ื“ ืœืžืขืœื” ืž-11 ืžื™ืœื™ื•ืŸ ื‘ืงืฉื•ืช ืœื“ืงื” ืฉืžืงื•ืจืŸ ื‘-100 ื—ื™ื‘ื•ืจื™ื. ืชื•ืฆืื” ืœื ืจืขื”, ืื‘ืœ ื”ืื ื ื™ืชืŸ ืœืฉืคืจ ืื•ืชื”?

ืฉืจืช ืžืจื•ื‘ื” ื”ืœื™ื›ื™

ื›ืคื™ ืฉื”ื•ื–ื›ืจ ืœืขื™ืœ, ื ื™ืชืŸ ืœื™ืฆื•ืจ ืืช ื›ื•ืจ ื”-I/O ื‘ืฉืจืฉื•ืจื™ื ื ืคืจื“ื™ื, ื•ื‘ื›ืš ืœื ืฆืœ ืืช ื›ืœ ืœื™ื‘ื•ืช ื”ืžืขื‘ื“. ื‘ื•ืื• ืœื™ื™ืฉื ืืช ื”ื’ื™ืฉื” ื”ื–ื• ื‘ืคื•ืขืœ:

ื”ืฆื’ ืืช http_server_multithreaded.c

#include "reactor.h"

static Reactor *reactor;
#pragma omp threadprivate(reactor)

#include "common.h"

int main(void) {
#pragma omp parallel
    {
        SAFE_CALL((reactor = reactor_new()), NULL);
        SAFE_CALL(reactor_register(reactor, new_server(true), EPOLLIN,
                                   on_accept, NULL),
                  -1);
        SAFE_CALL(reactor_run(reactor, SERVER_TIMEOUT_MILLIS), -1);
        SAFE_CALL(reactor_destroy(reactor), -1);
    }
}

ืขื›ืฉื™ื• ื›ืœ ื—ื•ื˜ ื”ื‘ืขืœื™ื ืฉืœื• ื›ื•ืจ:

static Reactor *reactor;
#pragma omp threadprivate(reactor)

ืฉื™ืžื• ืœื‘ ืฉื”ืืจื’ื•ืžื ื˜ ืฉืœ ื”ืคื•ื ืงืฆื™ื” new_server() ืชื•ืžื›ื™ื true. ื–ื” ืื•ืžืจ ืฉืื ื—ื ื• ืžืงืฆื™ื ืืช ื”ืืคืฉืจื•ืช ืœืฉืงืข ื”ืฉืจืช SO_REUSEPORTืœื”ืฉืชืžืฉ ื‘ื• ื‘ืกื‘ื™ื‘ื” ืžืจื•ื‘ืช ื—ื•ื˜ื™ื. ืชื•ื›ืœ ืœืงืจื•ื ืคืจื˜ื™ื ื ื•ืกืคื™ื ื›ืืŸ.

ืจื™ืฆื” ืฉื ื™ื”

ืขื›ืฉื™ื• ื‘ื•ืื• ื ืžื“ื•ื“ ืืช ื”ื‘ื™ืฆื•ืขื™ื ืฉืœ ืฉืจืช ืžืจื•ื‘ื” ื”ืœื™ื›ื™ ืฉืจืฉืจืช:

$ wrk -c100 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
  8 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.14ms    2.53ms  40.73ms   89.98%
    Req/Sec    79.98k    18.07k  154.64k    78.65%
  38208400 requests in 1.00m, 5.23GB read
Requests/sec: 635876.41
Transfer/sec:     89.14MB

ืžืกืคืจ ื”ื‘ืงืฉื•ืช ืฉืขื•ื‘ื“ื• ื‘ื“ืงื” ืื—ืช ื’ื“ืœ ืคื™ ~1! ืื‘ืœ ื—ืกืจ ืœื ื• ืจืง 3.28 ืžื™ืœื™ื•ืŸ ืžื”ืžืกืคืจ ื”ืขื’ื•ืœ, ืื– ื‘ื•ืื• ื ื ืกื” ืœืชืงืŸ ืืช ื–ื”.

ืจืืฉื™ืช ื‘ื•ืื• ื ืกืชื›ืœ ืขืœ ื”ืกื˜ื˜ื™ืกื˜ื™ืงื” ืฉื ื•ืฆืจื” perf:

$ sudo perf stat -B -e task-clock,context-switches,cpu-migrations,page-faults,cycles,instructions,branches,branch-misses,cache-misses ./http_server_multithreaded

 Performance counter stats for './http_server_multithreaded':

     242446,314933      task-clock (msec)         #    4,000 CPUs utilized          
         1โ€ฏ813โ€ฏ074      context-switches          #    0,007 M/sec                  
             4โ€ฏ689      cpu-migrations            #    0,019 K/sec                  
               254      page-faults               #    0,001 K/sec                  
   895โ€ฏ324โ€ฏ830โ€ฏ170      cycles                    #    3,693 GHz                    
   621โ€ฏ378โ€ฏ066โ€ฏ808      instructions              #    0,69  insn per cycle         
   119โ€ฏ926โ€ฏ709โ€ฏ370      branches                  #  494,653 M/sec                  
     3โ€ฏ227โ€ฏ095โ€ฏ669      branch-misses             #    2,69% of all branches        
           808โ€ฏ664      cache-misses                                                

      60,604330670 seconds time elapsed

ืฉื™ืžื•ืฉ ื‘-CPU Affinity, ืงื•ืžืคื™ืœืฆื™ื” ืขื -march=native, PGO, ืขืœื™ื™ื” ื‘ืžืกืคืจ ื”ื›ื ื™ืกื•ืช ืžื˜ืžื•ืŸ, ืœื”ื’ื‘ื™ืจ MAX_EVENTS ื•ืœื”ืฉืชืžืฉ EPOLLET ืœื ื ืชืŸ ืขืœื™ื™ื” ืžืฉืžืขื•ืชื™ืช ื‘ื‘ื™ืฆื•ืขื™ื. ืื‘ืœ ืžื” ืงื•ืจื” ืื ืžื’ื“ื™ืœื™ื ืืช ืžืกืคืจ ื”ื—ื™ื‘ื•ืจื™ื ื‘ื• ื–ืžื ื™ืช?

ืกื˜ื˜ื™ืกื˜ื™ืงื” ืขื‘ื•ืจ 352 ื—ื™ื‘ื•ืจื™ื ื‘ื• ื–ืžื ื™ืช:

$ wrk -c352 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
  8 threads and 352 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     2.12ms    3.79ms  68.23ms   87.49%
    Req/Sec    83.78k    12.69k  169.81k    83.59%
  40006142 requests in 1.00m, 5.48GB read
Requests/sec: 665789.26
Transfer/sec:     93.34MB

ื”ืชืงื‘ืœื” ื”ืชื•ืฆืื” ื”ืจืฆื•ื™ื”, ื•ืื™ืชื” ื’ืจืฃ ืžืขื ื™ื™ืŸ ื”ืžืจืื” ืืช ื”ืชืœื•ืช ืฉืœ ืžืกืคืจ ื”ื‘ืงืฉื•ืช ื”ืžืขื•ื‘ื“ื•ืช ื‘ื“ืงื” ืื—ืช ื‘ืžืกืคืจ ื”ื—ื™ื‘ื•ืจื™ื:

ื›ื•ืจ I/O ื—ืฉื•ืฃ ืขื ืชื›ื•ื ื•ืช ืžืœืื•ืช

ืื ื• ืจื•ืื™ื ืฉืื—ืจื™ ื›ืžื” ืžืื•ืช ื—ื™ื‘ื•ืจื™ื, ืžืกืคืจ ื”ื‘ืงืฉื•ืช ื”ืžืขื•ื‘ื“ื•ืช ืขื‘ื•ืจ ืฉื ื™ ื”ืฉืจืชื™ื ื™ื•ืจื“ ื‘ื—ื“ื•ืช (ื‘ื’ืจืกืช ืจื™ื‘ื•ื™ ื”ื”ืœื™ื›ื™ื ื–ื” ื‘ื•ืœื˜ ื™ื•ืชืจ). ื”ืื ื–ื” ืงืฉื•ืจ ืœืžื™ืžื•ืฉ ืžื—ืกื ื™ืช TCP/IP ืฉืœ Linux? ืืœ ืชื”ืกืก ืœื›ืชื•ื‘ ืืช ื”ื”ื ื—ื•ืช ืฉืœืš ืœื’ื‘ื™ ื”ืชื ื”ื’ื•ืช ื–ื• ืฉืœ ื”ื’ืจืฃ ื•ืื•ืคื˜ื™ืžื™ื–ืฆื™ื•ืช ืœืืคืฉืจื•ื™ื•ืช ืžืจื•ื‘ื™ ื—ื•ื˜ื™ื ื•ืฉืจืฉื•ืจ ื‘ื•ื“ื“ ื‘ื”ืขืจื•ืช.

ื›ืžื• ืฆื™ื™ื ืชื™ ื‘ื”ืขืจื•ืช, ืžื‘ื—ืŸ ื‘ื™ืฆื•ืขื™ื ื–ื” ืื™ื ื• ืžืจืื” ืืช ื”ื”ืชื ื”ื’ื•ืช ืฉืœ ื›ื•ืจ ื”-I/O ืชื—ืช ืขื•ืžืกื™ื ืืžื™ืชื™ื™ื, ืžื›ื™ื•ื•ืŸ ืฉื›ืžืขื˜ ืชืžื™ื“ ื”ืฉืจืช ืžืงื™ื™ื ืื™ื ื˜ืจืืงืฆื™ื” ืขื ืžืกื“ ื”ื ืชื•ื ื™ื, ืžื•ืฆื™ื ื™ื•ืžื ื™ื, ืžืฉืชืžืฉ ื‘ืงืจื™ืคื˜ื•ื’ืจืคื™ื” ืขื TLS ื•ื›ื•', ื›ืชื•ืฆืื” ืžื›ืš ื”ืขื•ืžืก ื”ื•ืคืš ืœื ืื—ื™ื“ (ื“ื™ื ืžื™). ื‘ื“ื™ืงื•ืช ื™ื—ื“ ืขื ืจื›ื™ื‘ื™ ืฆื“ ืฉืœื™ืฉื™ ื™ื‘ื•ืฆืขื• ื‘ืžืืžืจ ืขืœ ืคืจื•ืืงื˜ื•ืจ ื”-I/O.

ื—ืกืจื•ื ื•ืช ืฉืœ ื›ื•ืจ I/O

ืืชื” ืฆืจื™ืš ืœื”ื‘ื™ืŸ ืฉื›ื•ืจ ื”-I/O ืื™ื ื• ื—ืฃ ืžื—ืกืจื•ื ื•ืชื™ื•, ื›ืœื•ืžืจ:

  • ื”ืฉื™ืžื•ืฉ ื‘ื›ื•ืจ ืงืœื˜/ืคืœื˜ ื‘ืกื‘ื™ื‘ื” ืžืจื•ื‘ืช ืคืชื™ืœื™ื ื”ื•ื ืงืฆืช ื™ื•ืชืจ ืงืฉื”, ืžื›ื™ื•ื•ืŸ ืชืฆื˜ืจืš ืœื ื”ืœ ืืช ื”ื–ืจื™ืžื•ืช ื‘ืื•ืคืŸ ื™ื“ื ื™.
  • ื”ืชืจื’ื•ืœ ืžืจืื” ืฉื‘ืจื•ื‘ ื”ืžืงืจื™ื ื”ืขื•ืžืก ืื™ื ื• ืื—ื™ื“, ืžื” ืฉื™ื›ื•ืœ ืœื”ื•ื‘ื™ืœ ืœืจื™ืฉื•ื ืฉืจืฉื•ืจ ืื—ื“ ื‘ืขื•ื“ ืื—ืจ ืขืกื•ืง ื‘ืขื‘ื•ื“ื”.
  • ืื ืžื˜ืคืœ ืื—ื“ ื‘ืื™ืจื•ืขื™ื ื—ื•ืกื ืฉืจืฉื•ืจ, ืื– ื’ื ื‘ื•ืจืจ ื”ืžืขืจื›ืช ืขืฆืžื• ื™ื—ืกื•ื, ืžื” ืฉืขืœื•ืœ ืœื”ื•ื‘ื™ืœ ืœื‘ืื’ื™ื ืฉืงืฉื” ืœืžืฆื•ื ืื•ืชื.

ืคื•ืชืจ ืืช ื”ื‘ืขื™ื•ืช ื”ืœืœื• ืคืจื•ืืงื˜ื•ืจ I/O, ืฉืœืขื™ืชื™ื ืงืจื•ื‘ื•ืช ื™ืฉ ืœื• ืžืชื–ืžืŸ ืฉืžื—ืœืง ืืช ื”ืขื•ืžืก ื‘ืื•ืคืŸ ืฉื•ื•ื” ืœืžืื’ืจ ืฉืœ ืฉืจืฉื•ืจื™ื, ื•ื™ืฉ ืœื• ื’ื API ื ื•ื— ื™ื•ืชืจ. ื ื“ื‘ืจ ืขืœ ื–ื” ืžืื•ื—ืจ ื™ื•ืชืจ, ื‘ืžืืžืจ ืื—ืจ ืฉืœื™.

ืžืกืงื ื”

ื›ืืŸ ื”ื’ื™ืข ืœืกื™ื•ืžื• ื”ืžืกืข ืฉืœื ื• ืžื”ืชื™ืื•ืจื™ื” ื”ื™ื™ืฉืจ ืืœ ืื’ื–ื•ื– ื”ืคืจื•ืคื™ืœื™ื.

ืืชื” ืœื ืฆืจื™ืš ืœื”ืชืขื›ื‘ ืขืœ ื–ื”, ื›ื™ ื™ืฉ ืขื•ื“ ื”ืจื‘ื” ื’ื™ืฉื•ืช ืžืขื ื™ื™ื ื•ืช ืœื ืคื—ื•ืช ืœื›ืชื™ื‘ืช ืชื•ื›ื ื•ืช ืจืฉืช ื‘ืจืžื•ืช ืฉื•ื ื•ืช ืฉืœ ื ื•ื—ื•ืช ื•ืžื”ื™ืจื•ืช. ืžืขื ื™ื™ืŸ, ืœื“ืขืชื™, ืงื™ืฉื•ืจื™ื ืžื•ื‘ืื™ื ืœืžื˜ื”.

ืœื”ืชืจืื•ืช ืฉื•ื‘!

ืคืจื•ื™ืงื˜ื™ื ืžืขื ื™ื™ื ื™ื

ืžื” ืขื•ื“ ื›ื“ืื™ ืœื™ ืœืงืจื•ื?

ืžืงื•ืจ: www.habr.com

ื”ื•ืกืคืช ืชื’ื•ื‘ื”