prefork ãµã¼ãã¼ã¨ thundering herd åé¡
Catalyst ã POE ã§åãã Engine ã® Catalyst::Engine::HTTP::POE ã¨ããå®è£ ã CPAN ã«ããã¾ãã"Single-threaded multi-tasking Catalyst engine " ã ããã§ãã"Single-threaded" ã¨è¨ãã¤ã¤ãå®è£ ãè¦ãã¦ã¿ãã¨ç°å¢å¤æ° CATALYST_POE_MAX_PROC ã 1 ããã大ããè¨å®ãããã¨ã§ prefork ããå®è£ ã«ãªã£ã¦ã¾ããPOEã·ã³ã°ã«ã¹ã¬ããã§ã¯ã¢ããªã±ã¼ã·ã§ã³å ã§çºçãããããã¯ãé¿ãããã¨ãé£ããã®ã§ãã®ããã®å®è£ ãããªãããªã¨æãã¾ãã
ã¨ããã§ãã® Catalyst POE ã¨ã³ã¸ã³ãprefork ã®å®è£ ã¯ã©ã®ããã«è¡ã£ã¦ãããã¨ãã㨠POE ãã prefork ã¨åã®ä»ããã¤ãã³ããçºçããã¨ããããã«åããã»ã¹ãçæãããã¨ããã®ãã®ãè¤æ°ã®ããã»ã¹ãç«ã¡ä¸ãã¦ãã㦠accept(2) ãçºè¡ãããã¨ã§ããã«ãããã»ã¹ã§ã¯ã©ã¤ã¢ã³ãããã®æ¥ç¶ã«å¿çããã¨ããæ®éã® prefork ãªã®ã§ããç¹ã« accept(2) å¼ã³åºããã·ãªã¢ã©ã¤ãºãããããªãã¨ã¯ãã¦ããªãããã§ãã
以åã«èªãã æ¬ã§ã¯ accept(2) ãè¤æ°ã®ããã»ã¹ããå¼ã³åºãã¨åé¡ãããã®ã§ã·ãªã¢ã©ã¤ãºãã! ã¨ãã£ãã®ã§ããããã® Catalyst POE ã¨ã³ã¸ã³ã®å®è£ ã§ã¯ãã®ãããªæ§åãè¦ããªãã®ã§å¤§ä¸å¤«ãªã®ããª...ã¨æã調ã¹ãã¨ããã¦ããã深追ããã¦ãã¾ãã¾ããã以ä¸ããã®ãµããªã§ãã
ã¾ã㯠Perl ã§ãaccept(2) ãé ååããªã prefork ã® echo ãµã¼ãã¼ã®å®è£ ãæ¸ãã¦è¦ã¾ãã
#!/usr/local/bin/perl use strict; use warnings; use IO::Socket; my @children; my $listen = IO::Socket::INET->new( LocalPort => 9999, Listen => SOMAXCONN, Reuse => 1, ) or die $!; for (1..10) { unless (fork) { while (my $con = $listen->accept) { while ($con->sysread(my $buffer, 1024)) { $con->syswrite($buffer); } $con->close; } exit (0); } } $listen->close; wait;
ãããªæãã§ãã¨ã©ã¼å¦çã§ãã¨ããã½ã±ããã®ãã³ããããã³ã°å¦çã¨ããããã¨ã³ã¼ããè¤éã«ãªãã®ã§ãã®è¾ºã¯å å³ããæå°éã®ãã®ã«ãã¦ããã¾ãã
å¾ æ©ã½ã±ãããä½ã£ã¦åããã»ã¹ãããããçæãããã®åããã»ã¹ãããã㧠IO::Socket->accept ãå¼ã³ã¾ããåããã»ã¹ãä½ã£ãæç¹ã§ããã®ã¨ã親ãæã£ã¦ãããã¡ã¤ã«ãã£ã¹ã¯ãªãã¿ã¯ãã¹ã¦åã«ãã³ãã¼ããã¾ãããã㧠OS ã« accept(2) ãçºè¡ããã¦ã¯ã©ã¤ã¢ã³ãããã®æ¥ç¶ãåãä»ãããã¨ãã§ãã¾ãã
ãªã Linux ã®å ´åã¯ãåã®ããããã®ãã¡ã¤ã«ãã£ã¹ã¯ãªãã¿ã¯ããã»ã¹æ§é ä½(ã«ã¼ãã«å ã® task_struct æ§é ä½) ã®ã¡ã³ãã®é åã®ã¤ã³ããã¯ã¹ã§ãããã®é åã®ä¸èº«ã¯ãã¡ã¤ã«æ§é ä½ã¸ã®ãã¤ã³ã¿ã§ããããã¦åããã»ã¹ã«ã³ãã¼ããããã¡ã¤ã«ãã£ã¹ã¯ãªãã¿ã¯ãã¹ã¦åãä¸ã¤ã®å¾ æ©ã½ã±ããã示ãã¦ãããã¨ã«ãªãã¾ããã½ã±ãããåã«ã³ãã¼ããã¦ããã®ã§ã¯ãªããããã¾ã§ãã¤ã³ã¿ãã³ãã¼ããã¦ãã ãã§ããå¾ã£ã¦è¤æ°ã®åããã»ã¹ãã accept(2) ãçºè¡ããã¨ãã£ã¦ãå¥ã ã®ã½ã±ããã« accept(2) ãçºè¡ããã¦ããããã§ã¯ãªããåä¸ã®ã½ã±ããã«å¯¾ãã¦åæãã accept(2) ãå¼ã°ãã¦ããã¨ãããã¨ã«ãªãã¾ãã
ãã¦ãè¤æ°ã®ããã»ã¹ãåæã« accept(2) ãçºè¡ããã¨ã©ããªãããaccept(2) ãçºè¡ããã¨ããããã³ã°ãçºçããããã»ã¹ã¯ã¹ãªã¼ãç¶æ ã«ãªãã¾ããã¯ã©ã¤ã¢ã³ãããæ¥ç¶ãããã¨å ¨ã¦ã®ããã»ã¹ã¯èµ·åºãã¾ããã©ããä¸ã¤ã®ããã»ã¹ããã®ã¯ã©ã¤ã¢ã³ãæ¥ç¶ãç²å¾ãã¦å¦çãéå§ãä»ã®ããã»ã¹ã¯ã¾ãã¹ãªã¼ãã«å ¥ãã¾ãã(å®ã¯ãããæ£ç¢ºãªèª¬æãããªãã¨ããã®ã¯å¾ã§ã) å¾ã£ã¦ãä¸è¨ã®ããã°ã©ã ã¯ä¸å¿ prefork ãµã¼ãã¼ã¨ãã¦åãã¾ããè¤æ°ã®ç«¯æ«ãã telnet ãã¦ããã¡ããã¨ãããã¯ãããã«ããããã«å¿çã帰ãã¾ãã
ããaccept(2) ã®åº¦ã«è¤æ°ã®åããã»ã¹ãä¸æã«èµ·ãä¸ãã£ã¦ã¯ä¸ã¤ã ããèµ·ãç¶ãã¦å¾ã¯å¯ããã¨ããå®è£ ã¯ã©ãã§ããããæããã«éå¹çã§ããããã®ããã«ä¸ã¤ã®ã³ãã¯ã·ã§ã³ãèµ·ç¹ã«ãªã£ã¦ãã¹ã¦ã® preforked ãªããã»ã¹/ã¹ã¬ãããèµ·ãä¸ãã£ã¦ãã¾ãã·ã¹ãã ã«è² è·ããããç¾è±¡ã "thundering herd" ã¨å¼ã¶ãããã§ãã
Linux ã® thundering herd åé¡ã«ã¤ãã¦ã¯ä»¥ä¸ã®è«æã詳ããã§ãããªãããã®è«æã®å 容ãå½ã¦ã¯ã¾ãã®ã¯å¤ãã«ã¼ãã«ã ãã®è©±ã¨ãããªããããã®ã¯å¾è¿°ã
thundering herd ãèµ·ããªãããã«ããã«ã¯ãaccept(2) ãã³ã¼ã«ãããå¦çãé ååããã®ãå®çªã§ããPerlãããã¯ã¼ã¯ããã°ã©ãã³ã°âã½ã±ããã®ä½¿ãæ¹ããã¯ã©ã¤ã¢ã³ã/ãµã¼ãã¼ã·ã¹ãã ã®éçºã¾ã§ ã§ã解説ããã¦ãããã¡ã¤ã«ããã¯ã«ããã¢ããã¤ã¶ãªããã¯ãè¡ãæ¹æ³ã ã¨ä»¥ä¸ã®ããã«ãªãã¾ãã ãã¡ã¤ã«ããã¯ã«ããã¢ããã¤ã¶ãªããã¯ã¯ thundering herd ãé¿ããã®ãç®çã§ã¯ããã¾ããã§ãããåéããã¦ãã¾ãããã³ã¡ã³ãæ¬åç
§ã
#!/usr/local/bin/perl use strict; use warnings; use IO::Socket; use Path::Class; use Fcntl ':flock'; my $lock = file('lockfile'); $lock->touch; local $SIG{INT} = sub { $lock->remove; exit(0); }; my $listen = IO::Socket::INET->new( LocalPort => 9999, Listen => SOMAXCONN, Reuse => 1, ) or die $!; for (1..10) { unless (fork) { while (1) { my $fh = $lock->openr or die $!; flock($fh, LOCK_EX); my $con = $listen->accept; flock($fh, LOCK_UN); $fh->close; while ($con->sysread(my $buffer, 1024)) { $con->syswrite($buffer); } $con->close; } exit 0; } } $listen->close; wait;
$listen->accept ã®åå¾ã§ flock ã§ããã¯ãããã¾ããflock ã¯ãããã¯ãç²å¾ãã以å¤ã®ããã»ã¹ããã® flock çºè¡ã«å¯¾ãã¦ã¢ããªã±ã¼ã·ã§ã³ããããã¯ãã¾ãã®ã§ãããã«ãã£ã¦ããæéã«ã¯ããã¯ãç²å¾ããããã»ã¹ã ãã accept(2) å¼ã³åºãããããã¨ãã§ãã¾ããããã§ãã§ãã thundering herd åé¡ã¯è§£æ±ºã§ãã¾ããããã ããããã¯ã«ãã¡ã¤ã«ããã¯ã使ãã®ã¯ãã¾ãå¹çãè¯ããªãã®ã§ãå¹çãéè¦ããã®ã§ããã°ã»ããã©ãªã©ãã£ã¨è»½éãªããã¯æ©æ§ã¸ã®ç½®ãæããå¿ è¦ã§ãããã
ã¨ã¾ããããã¾ã§ã¯è¯ãã£ãã®ã§ãããå®éã« thundering herd ãèµ·ãã£ã¦ããæ§åãè¦ã¦ã¿ããã¨æããæåã®ããã°ã©ã ã®ã·ã¹ãã ã³ã¼ã«ããã¬ã¼ã¹ãã¦ã¿ã¾ãããstrace 㧠-f ãã¤ããã¨åããã»ã¹ãã¨ãã¬ã¼ã¹ãåããã¨ãã§ãã¾ãã
% strace -f perl prefork_echo.pl ... [pid 5467] <... getppid resumed> ) = 5459 [pid 5459] close(3 <unfinished ...> [pid 5467] accept(3, <unfinished ...> [pid 5459] <... close resumed> ) = 0 [pid 5459] waitpid(-1, Process 5459 suspended <unfinished ...> [pid 5468] getpid( <unfinished ...> [pid 5469] getpid( <unfinished ...> [pid 5468] <... getpid resumed> ) = 5468 [pid 5469] <... getpid resumed> ) = 5469 [pid 5468] getppid( <unfinished ...> [pid 5469] getppid( <unfinished ...> [pid 5468] <... getppid resumed> ) = 5459 [pid 5469] <... getppid resumed> ) = 5459 [pid 5469] accept(3, <unfinished ...> [pid 5468] accept(3,
ã¨ããæãã§ãµã¼ãã¼ãç«ã¡ä¸ããã¨è¤æ°ã®ããã»ã¹ã accept(2) ã§ãããã¯ãããã®ãåããã¾ããããã§ããç¹å®ã®ç«¯æ«ããæ¥ç¶ãããã¨ãä¸æã«åããã»ã¹ãç«ã¡ä¸ããæ§åãè¦ãããã ãã...ã¨æãå®éã«ãã£ã¦ã¿ãã¨
<unfinished ...> [pid 5460] <... accept resumed> {sa_family=AF_INET, sin_port=htons(2959), sin_addr=inet_addr("127.0.0.1")}, [16]) = 4 [pid 5460] ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfffe078) = -1 EINVAL (Invalid argument) [pid 5460] _llseek(4, 0, 0xbfffe0b0, SEEK_CUR) = -1 ESPIPE (Illegal seek) [pid 5460] ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfffe068) = -1 EINVAL (Invalid argument) [pid 5460] _llseek(4, 0, 0xbfffe0a0, SEEK_CUR) = -1 ESPIPE (Illegal seek) [pid 5460] fcntl64(4, F_SETFD, FD_CLOEXEC) = 0 [pid 5460] read(4,
ã¨ãäºæ³ã«åãã¦ä¸ã¤ã®åããã»ã¹ããåå¿ãã¾ããããããã話ãéããthundering herd ã¯èµ·ãã¦ãªã?
ããã§å ã®è«æã詳ããèªãã§è¦ã¾ãã
- sock->state_change.................... (pointer to sock_def_wakeup)
- sock->data_ready...................... (pointer to sock_def_readable)
- sock->write_space..................... (pointer to tcp_write_space)
- sock->error_report.................... (pointer to sock_def_error_report)
The code for each one of these methods invokes the wake_up_interruptible() function. This means that every time one of these methods is called, tasks may be unnecessarily awakened. In fact, in the accept() call alone, Linux invokes three of these methods, essentially tripling impact of the "thundering herd" problem. The three methods invoked in every call to accept() in the 2.2.9 kernel are tcp_write_space(), sock_def_readable() and sock_def_wakeup(), in that order.
accept() ã®ä¸ã§ä½¿ããã¦ããããã¤ãã®ã½ã±ããç¨é¢æ°ã wake_up_interruptible é¢æ°(ãã¯ã)ãå¼ãã§ã¦ãããåå ã ã¨æ¸ãã¦ããã¾ããwake_up_interruptible ã¯ã«ã¼ãã«ã®ã¹ã±ã¸ã¥ã¼ã©ã®éè¦ãªãã¯ãã§ãã¥ã¼ã«æºã¾ã£ãããã»ã¹ã®ãã¡å¾ æ©ç¶æ ã®ãã®ãèµ·åºããããã®ã§ãããã®è¾ºã¯ 負荷とは何か - naoyaのはてなダイアリー ããããåç §ãã¦ããã ããã¨ãã幸ãã
ããã§ãã§ããè²ã 調ã¹ã¦ã¿ãã¨ãã® wake_up_interruptible ãªã©ã«é¢é£ããã³ã¼ãããLinux 2.2 㨠Linux 2.4 以éã§ã¯å®è£ ãç°ãªã£ã¦ããããã
v2.2ã¾ã§ã¯ãwake_upé¢æ°ã¯å¯¾è±¡ã¨ãªãWAITãã¥ã¼ãããã§å¾ ã¡ã«å ¥ã£ã¦ãã ããã»ã¹ãå ¨ã¦RUNç¶æ ã«ãã¦ããããæ§è½æ¹åã®ããv2.4ãã㯠WAITãã¥ã¼ãããã§å¾ ã¡ã«å ¥ã£ã¦ããããã»ã¹ã®ãã¡å é ã®ããã»ã¹ã ãã RUNç¶æ ã«ãããã¨ãã§ããããã«ãªã£ãã
ã ããã§ã確ãã« Linux 2.2 㨠2.6 㧠wake_up é¢æ°ç¾¤ãæ¯è¼ãã㨠2.2 ã§ã¯
- wake_up
- wake_up_interruptible
ã®äºç¨®é¡ã«å¯¾ã 2.6 ã§ã¯
- wake_up
- wake_up_interruptible
- wake_up_all
- wake_up_interruptible_all
- wake_up_all_sync
- wake_up_interruptible_sync
ã¨æ°ãå¢ãã¦ãã¾ããååããããããã¨ãããããã»ã¹ãã¾ã¨ãã¦èµ·åºããããã®ã¨ããã§ãªããã®ã¨ãããã¾ããæ°ããã«ã¼ãã«ã§ã¯å ã®è«æã«ãããã½ã±ããå¨ãã®é¢æ°ã§ã¯è¤æ°ã®ãã¡ã²ã¨ã¤ã®ããã»ã¹ã ããèµ·åºããã wake_up / wake_up_interruptible é¢æ°ã使ããã¦ãã¾ããå¾ã£ã¦ãaccept(2) ã§ããã»ã¹ãä¸æã«èµ·åãã thundering herd ã¯çºçããªããã¨ãããã¨ã«ãªãã¾ãã
ãªãä¸è¨ã®å¼ç¨é¨åã§ã¯ã«ã¼ãã« 2.4 ãã...ã¨ã®ãã¨ã§ãããè«æã«ãæãã£ã¦ãã 2.2.9 ã¨ã«ã¼ãã« 2.2 ç³»ã®ææ°ã®ã³ã¼ãã¨ã§ã¯ã¹ã±ã¸ã¥ã¼ã©å¨ãã®å®è£ ãã¾ãã¡ãã£ã¨éã£ã¦ãã¦ã2.2.9 ã§ã¯è§£èª¬ã®ã¨ããå ¨é¨ã¾ã¨ãã¦ããèµ·åºãããããªããã®ã®ã2.2.26 ã® kernel/sched.c ã§ã¯
void __wake_up(struct wait_queue **q, unsigned int mode) { struct task_struct *p, *best_exclusive; struct wait_queue *head, *next; unsigned int do_exclusive; if (!q) goto out; /* * this is safe to be done before the check because it * means no deference, just pointer operations. */ head = WAIT_QUEUE_HEAD(q); read_lock(&waitqueue_lock); next = *q; if (!next) goto out_unlock; best_exclusive = 0; do_exclusive = mode & TASK_EXCLUSIVE; while (next != head) { p = next->task; next = next->next; if (p->state & mode) { if (do_exclusive && p->task_exclusive) { if (best_exclusive == NULL) best_exclusive = p; } else { wake_up_process(p); } } } if (best_exclusive) wake_up_process(best_exclusive); out_unlock: read_unlock(&waitqueue_lock); out: return; }
㨠TASK_EXCLUSIVE ã¢ã¼ããå°å ¥ããã¦å¾ æ©ããã»ã¹ä¸ã¤ã ããèµ·åºããããã¨ãã§ããããã«ãªã£ã¦ããããã§ãã
ãã¦ã話ã¯å¤ãã£ã¦ prefork ãªã¦ã§ããµã¼ãã¼ã®ä»£è¡¨çãªå®è£ ã¨è¨ãã°ãã¯ã Apache ã§ããApache 㯠accept() å¨ãã®å®è£ ã¯ã©ããã¦ãããã¨æ°ã«ãªã£ã¦ã¡ãã£ã¨ã³ã¼ããè¦ãã¦ã¿ã¾ãããserver/mpm/prefork/prefork.c ã§ãã
static void accept_mutex_on(void) { apr_status_t rv = apr_proc_mutex_lock(accept_mutex); if (rv != APR_SUCCESS) { const char *msg = "couldn't grab the accept mutex"; if (ap_my_generation != ap_scoreboard_image->global->running_generation) { ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, NULL, msg); clean_child_exit(0); } else { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, NULL, msg); exit(APEXIT_CHILDFATAL); } } } static void accept_mutex_off(void) { apr_status_t rv = apr_proc_mutex_unlock(accept_mutex); if (rv != APR_SUCCESS) { const char *msg = "couldn't release the accept mutex"; if (ap_my_generation != ap_scoreboard_image->global->running_generation) { ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, NULL, msg); /* don't exit here... we have a connection to * process, after which point we'll see that the * generation changed and we'll exit cleanly */ } else { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, NULL, msg); exit(APEXIT_CHILDFATAL); } } }
ã¨ãaccept ãããã¯/ããã¯è§£é¤ããããã®é¢æ°ãç¨æããã¦ã¾ããã¾ããã® accept_mutex_* ã§ã¯ããã¯ã«è²ã ãªå®è£ ã使ããããã«ãªã£ã¦ãã¦ãhttpd.conf ã® AcceptMutex ãã£ã¬ã¯ãã£ãã§è¨å®ã§ããããã§ãã
ããã«ããä¸ã¤ãããªç®æãããã¾ãã
/* On some architectures it's safe to do unserialized accept()s in the single * Listen case. But it's never safe to do it in the case where there's * multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT * when it's safe in the single Listen case. */ #ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT #define SAFE_ACCEPT(stmt) do {if (ap_listeners->next) {stmt;}} while(0) #else #define SAFE_ACCEPT(stmt) do {stmt;} while(0) #endif
ã³ã¡ã³ãã«ãããã¨ãããç¹å®ã®ã¢ã¼ããã¯ãã£ã§ã¯ accept() ã¯ä¸æã«å¼ã³åºãã¦ã OK ãããã®å ´å㯠SINGLE_LISTEN_UNSERIALIZED_ACCEPT ãæå¹ã«ãã! ã¨ã®ãã¨ã§ããLinux ã®å ´åããã®è¨å®ã¯ã©ããªãããconfigure ãã¯ããè¦ãã¦ã¿ã¾ãã
case $host in ... ;; *-linux-*) case `uname -r` in 2.0* ) AP_SIG_GRACEFUL=WINCH ;; 2.[2-9]* ) echo " forcing SINGLE_LISTEN_UNSERIALIZED_ACCEPT to \"1\"" SINGLE_LISTEN_UNSERIALIZED_ACCEPT="1" ;; * ) ;; esac ;;
Linux 2.2 以éã§ã¯ SINGLE... ãæå¹ã«ãªã£ã¦ã¾ãããå ã«è¿°ã¹ãããã«ã«ã¼ãã« 2.2 ã§ãæ°ãç®ã®ãã®ã§ã¯ wake_up é¢æ°ã®å®è£ ã«æ¹è¯ãå ãããã¦ããã®ã§ 2.2 以éã¨ããæå®ãªã®ã§ãããããããããã¾ãä»ã® OS ã§ã¯ãã®å¤æ°ãæå¹ã«ãªããªããã®ãçµæ§ããã¾ããOS ã«ãã£ã¦ã¯ accept(2) ãè¤æ°ã®ããã»ã¹/ã¹ã¬ããããå¼ã¶ã¨ã¨ã©ã¼ãè¿ããã®ãããããã§ããã®è¾ºã«ä¸ã¤ä¸ã¤å¯¾å¦ãã¦ããã®ãåããã¾ãã
ã¨ããããã¨ã§æåã®å®é¨ãã thundering herd åé¡ã®ããããªãããApache ã§ã¯ã©ããã¦ãã®ããªã©ãã¿ã¦ããããªãã¨ãã¤ãªãã£ã¦ãã£ãããã¾ããããã§ãããã§ããã
ããã¡ãã£ã¨ã ãç¶ãã¾ããUNIXãããã¯ã¼ã¯ããã°ã©ãã³ã°ãVol.1ããããã¯ã¼ã¯API:ã½ã±ããã¨XTI ã«ã¯ prefork ã® thundering herd ãé¿ããããã®å®è£ æ¹æ³ã¨ãã¦ããã¯ãããã以å¤ã«ããã£ã¹ã¯ãªãã¿ããã·ã³ã°ã¨ããææ³ã使ã£ããã®ãç´¹ä»ããã¦ãã¾ãããã£ã¹ã¯ãªãã¿ããã·ã³ã°ã¯ ファイル記述子をUnixドメインソケット経由で渡す - bkブログ ã§ã詳ãã解説ããã¦ããããã«ãUNIXãã¡ã¤ã³ã½ã±ããã使ã£ã¦ãã¡ã¤ã«ãã£ã¹ã¯ãªãã¿ãå¥ã®ããã»ã¹ã«è»¢éããææ³ã§ãã
åããã»ã¹ãããããã« accept() ããã®ã§ã¯ãªãã親㧠accept() ãã¦ã¯ã©ã¤ã¢ã³ãããã®æ¥ç¶ã½ã±ãããåå¾ã§ãããããã®ãã¡ã¤ã«ãã£ã¹ã¯ãªãã¿ã prefork ã§å¾ æ©ãã¦ããåã«æ¸¡ãã¦ãã£ã¦è¦ªã¯ããã«ãã®æ¥ç¶ã½ã±ãããéãããã¨ããã®ãã®ããã®æ¹æ³ãªããããã accept() ã¯è¦ªããå¼ã°ãªãã®ã§ãthundering herd ã¯èµ·ããããããã¾ããã
ãªãããã£ã¹ã¯ãªãã¿ããã·ã³ã°ã§ã¯ãã¡ã¤ã«ãã£ã¹ã¯ãªãã¿ã渡ãã¨è¨ã£ã¦ãæ°åãã®ã¾ã¾ã渡ãããã§ã¯ããã¾ãããã«ã¼ãã«ä¸ã®ãã¡ã¤ã«ãã¼ãã«å ã«ããããéä¿¡å´ããã»ã¹ãéä¿¡ãããã£ã¹ã¯ãªãã¿ã«å¯¾å¿ããã¨ã³ããªãåç §ããæ°ãããã£ã¹ã¯ãªãã¿ãåä¿¡å´ã®ããã»ã¹ã§ä½æ(UNIXãããã¯ã¼ã¯ããã°ã©ãã³ã° 第2ç Vol.1 P.372)ãã¾ãããã®ãã親ã¨åã®é信㯠read(2)/write(2) ã send(2)/recv(2) ã§ã¯ã ãã§ãsendmsg(2)/recvmsg(2) ã®è£å©ãã¼ã¿æ ã使ã£ã¦éåä¿¡ãã¾ãã
ã¨ããããã¨ã§æ¬ã«è¼ã£ã¦ãããã£ã¹ã¯ãªãã¿ããã·ã³ã°ã® prefork ã®ã³ã¼ãã Perl ã«ç§»æ¤ãã¦ã¿ã¾ãããæ¸ç±ã®ã¯ã¦ã§ããµã¼ãã¼ã§ã·ã°ãã«å¨ãã®å¦çãã¡ããã¨æ¸ããã¦ã¾ãããããã§ã¯å¿ è¦æå°éã®é¨åã ãã echo ãµã¼ãã¼ã¨ãã¦ã
#!/usr/local/bin/perl use strict; use warnings; package Child; use base qw/Class::Accessor::Lvalue::Fast/; __PACKAGE__->mk_accessors(qw/pid pipe status/); package main; use IO::Socket; use IO::Select; use Socket::MsgHdr; use List::Util qw/first/; my $listen = IO::Socket::INET->new( LocalPort => 9999, Listen => SOMAXCONN, Reuse => 1, ) or die $!; my $select = IO::Select->new; $select->add($listen); my @pipes = IO::Socket->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC); $select->add(@pipes); my @children; for (0..5) { if (my $pid = fork) { push @children, Child->new({ pid => $pid, pipe => $pipes[0], status => 0, }); } else { $listen->close; $pipes[0]->close; while (my $con_fd = $pipes[1]->recvfd) { my $con = IO::Socket->new_from_fd($con_fd, 'w') or die $!; while ($con->sysread(my $buffer, 1024)) { $con->syswrite($buffer); } ## signal to parent $pipes[1]->syswrite($con->fileno); $con->close; } exit; } } while (1) { for ($select->can_read) { if ($_ eq $listen) { my $con = $listen->accept; my $child = first { $_->status == 0 } @children; $child or die "no idle child"; $child->status = 1; # busy $child->pipe->sendfd($con->fileno); $con->close; } else { for my $child (@children) { if ($_ eq $child->pipe) { $child->pipe->sysread(my $discard, 32); $child->status = 0; last; } } } } } sub IO::Socket::sendfd { my ($socket, $fd) = @_; my $outmsg = Socket::MsgHdr->new(buflen => 8192, controllen => 256); $outmsg->cmsghdr(SOL_SOCKET, SCM_RIGHTS, pack("i", $fd)); $socket->sendmsg($outmsg); } sub IO::Socket::recvfd { my $socket = shift; my $inhdr = Socket::MsgHdr->new(buflen => 8192, controllen => 256); $socket->recvmsg($inhdr, 0); unpack('i', ($inhdr->cmsghdr)[2]); }
親ã¨åã®éä¿¡ã«ã¯åæ¹åãã¤ãã使ãã¾ããã¾ãã親ã§ã¯ãã¤ãã¨å¾ æ©ã½ã±ããã® I/O ãåæã«é¢åãã¿ãå¿ è¦ãããã®ã§ select(2) = IO::Select ã使ã£ã¦ããããå¤éåãã¾ãã
- ã¯ã©ã¤ã¢ã³ãããã®æ¥ç¶ãããã¨è¦ªã¯åä¸è¦§ããã¢ã¤ãã«ãªããã»ã¹ãä¸ã¤é¸ã³ãUNIXãã¡ã¤ã³ã½ã±ããã®ãã¤ãçµç±ã§ãã£ã¹ã¯ãªãã¿ããã®åã«è»¢éãã¾ãã
- ãã®ã¨ãé¸ã°ããåã«ã¯ãã¸ã¼ç¶æ ã®ãã¼ã¯ãã¤ãã¦ããã¾ãã
- ãã£ã¹ã¯ãªãã¿ãåãåã£ãåã¯ãã®ã½ã±ããããã®èªã¿åããéå§ã echo ãµã¼ãã¼ã®ä»äºããã¾ãã
- ã¯ã©ã¤ã¢ã³ãã®æ¥ç¶ãåããã¨è¦ªã«å¯¾ãã¦ãã®æ¨ããã¤ãã§éç¥ãã¾ãã
- 親㯠select(2) ã§ãã¤ããç£è¦ãããã¨ã§åãä»äºãçµãããã¨ãã¤ãã³ãã¨ãã¦åãåããåãã¢ã¤ãã«ç¶æ ã«æ»ãã¾ãã
ãªã Perl çµã¿è¾¼ã¿ã®é¢æ°ã§ã¯ sendmsg() / recvmsg() ã¯ãµãã¼ãããã¦ãã¾ããããã㧠Socket::MsgHdr ã¨ãã XS ã¢ã¸ã¥ã¼ã«ã®ãä¸è©±ã«ãªãã¾ãããã®ã¢ã¸ã¥ã¼ã«ã use ãã㨠IO::Socket ã« sendmsg() / recvmsg() ã¡ã½ããã追å ããã¾ããã¹ã¯ãªããã®æ«å°¾ã§ã¯ãã®äºã¤ã®ã¡ã½ããã使ã£ã¦ãIO::Socket ã§ãã£ã¹ã¯ãªãã¿ãéåä¿¡ãã sendfd() / recvfd() ã¨ããã¡ã½ããã追å ãã¦ããã¾ãã
ã¾ã¨ã
prefork ãµã¼ãã¼ã® thundering herd åé¡ãèµ·ç¹ã«æ·±è¿½ãããå 容ãã¾ã¨ãã¦ã¿ã¾ããã
- prefork ãµã¼ãã¼ã¯è¤æ°ã®åã accept(2) ãåæã«å¼ã³åºããã¨ã§ãããå®ç¾ãã
- accept(2) ãã¾ã¨ãã¦å¼ã¶ã¨ç°å¢ã«ãã£ã¦ã¯ thundering herd åé¡ãçºçãã
- ããã¯ã使ã£ã¦ accept(2) ãé ååãããã¨ã§ãã®åé¡ãé¿ãããã¨ãã§ãã
- Linux ã§ã¯ 2.2 ç³»ã®å¤ãã«ã¼ãã«ã¨ãã以éã®ã«ã¼ãã«ã§ wake_up é¢æ°ç¾¤ã®å®è£ ãç°ãªããæ°ãããã®ã§ã¯äºè±¡ãå¾ ã¡åããã¦ããããã»ã¹ç¾¤ããä¸ã¤ã®ããã»ã¹ã ããèµ·åºããããã¨ãã§ãã
- Linux ã§ã¯ accept(2) ã«ãã thundering herd åé¡ã¯å¤ãã«ã¼ãã«ã§ã®ã¿çºçãã
- æ ã« Catalyst::Engine::HTTP::POE ã®å®è£ ã§ãæè¿ã® OS ã§ã¯åé¡ãªã
- Apache ã§ã¯ accept_mutex_* é¢æ°ã§ accept(2) ã®é ååãè¡ã£ã¦ãã
- ã¾ã configure ã¹ã¯ãªãã㧠OS ãã¨ã® accpet(2) ã®å®è£ å·®ç°ãå¸åãã¦ãã
- thundering herd ãé¿ããã«ã¯ãã£ã¹ã¯ãªãã¿ããã·ã³ã°ã使ãã
- Perl 㧠sendmsg(2) / recvmsg(2) ããã«ã¯ Socket::MsgHdr ãªã©ã使ã
ã¨ãã£ããã¨ãåããã¾ããã
ã«ã¼ãã«å¨ãã®æ¸ç±ã¯ Linuxã«ã¼ãã«2.6解èªå®¤ ããªã¹ã¹ã¡ã§ãããUNIXãããã¯ã¼ã¯ããã°ã©ãã³ã°ã«ã¤ãã¦ã¯ãã¯ã
UNIXãããã¯ã¼ã¯ããã°ã©ãã³ã°ãVol.1ããããã¯ã¼ã¯API:ã½ã±ããã¨XTI
- ä½è : W.ãªãã£ã¼ãã¹ãã£ã¼ã´ã³ã¹,W.Richard Stevens,ç¯ ç°é½ä¸
- åºç社/ã¡ã¼ã«ã¼: ãã¢ã½ã³ã¨ãã¥ã±ã¼ã·ã§ã³
- çºå£²æ¥: 1999/07
- ã¡ãã£ã¢: åè¡æ¬
- è³¼å ¥: 8人 ã¯ãªãã¯: 151å
- ãã®ååãå«ãããã° (35件) ãè¦ã
ãæ¬å½ã§ããããå®ã¯æè¿ã¾ã§ã¾ã£ããéãããªãã¾ã¾å®¶ã«ç©èªã«ãªã£ã¦ã¾ããããã¨ãã©ã調ã¹ãã¨ãããã¨ã»ã¨ãã©ã®ãã¨ããã®æ¬ã®ã©ããã«è©³ããè¼ã£ã¦ãããã¦æ¹ãã¦å¤å ¸ã®éè¦ããçæãã¦ãã¾ãã