Dive Deep Memcached ~ SETコマンド実行時の動作 ~
Dive Deep Memcached ~ SETコマンド実行時の動作 ~
Memcached 1.5.14をソースからビルドしてデフォルト設定時、SETコマンド実行時の動作をソースコードで確認します。 https://github.com/memcached/memcached
概要
最初の初期化
1 main at memcached.c
2 sanitycheck at memcached.c
2 settings_init at memcached.c
2 init_lru_maintainer at items.c
2 hash_init at hash.c
2 logger_init at logger.c
3 start_logger_thread at logger.c
3 STATS_LOCK, STATS_UNLOCK at thread.c
2 stats_init at memcached.c
3 stats_prefix_init at stats.c
3 assoc_init at assoc.c
2 conn_init at memcached.c
2 slabs_init at slabs.c
2 memcached_thread_init at thread.c
3 setup_thread at thread.c
4 cq_init at thread.c
4 cache_create at cache.c
3 create_worker at thread.c
2 init_lru_crawler at crawler.c
2 start_assoc_maintenance_thread at assoc.c
2 start_item_crawler_thread at crawler.c
2 start_lru_maintainer_thread at items.c
2 start_slab_maintenance_thread at slabs.c
2 clock_handler at memcached.c
3 assoc_start_expand at assoc.c
2 server_sockets at memcached.c
3 server_socket at memcached.c
4 new_socket at memcached.c
4 conn_new at memcached.c
2 uriencode_init at util.c
2 event_base_loop at memcached.c : main_baseに対するイベント処理のループ
SETコマンドの挙動
1 event_handler at memcached.c
2 drive_machine at memcached.c
3 dispatch_conn_new at thread.c
4 cqi_new at thread.c
4 cq_push at thread.c
3 reset_cmd_handler at memcached.c
4 conn_shrink at memcached.c
4 conn_set_state at memcached.c
3 update_event at memcached.c
3 try_read_network at memcached.c
4 tcp_read at memcached.c
3 try_read_command at memcached.c
4 process_command at memcached.c
5 add_msghdr at memcached.c
5 tokenize_command at memcached.c
5 process_update_command at memcached.c
6 set_noreply_maybe at memcached.c
6 realtime at memcached.c
6 item_alloc at thread.c
7 do_item_alloc at items.c
8 item_make_header at items.c
8 slabs_clsid at slabs.c
8 slabs_alloc at slabs.c
9 do_slabs_alloc at slabs.c
10 do_slabs_newslab at slabs.c
11 grow_slab_list at slabs.c
11 memory_allocate at slabs.c
11 split_slab_page_into_freelist at slabs.c
12 do_slabs_free at slabs.c
3 complete_nread at memcached.c
4 complete_nread_ascii at memcached.c
5 store_item at thread.c
6 hash(MurmurHash3_x86_32)
7 getblock32 at murmur3_hash.c
7 rotl32 at murmur3_hash.c
7 fmix32 at murmur3_hash.c
6 item_lock, item_unlock at thread.c
6 do_store_item at memcached.c
7 do_item_get at items.c
7 do_item_link at items.c
8 get_cas_id at items.c
8 assoc_insert at assoc.c
8 item_link_q at items.c
9 do_item_link_q at items.c
8 item_stats_sizes_add at items.c
5 out_string at memcached.c
5 item_remove at thread.c
6 do_item_remove at items.c
3 add_iov at memcached.c
4 ensure_iov_space at memcached.c
3 transmit at memcached.c
4 tcp_sendmsg at memcached.c
3 reset_cmd_handler at memcached.c
詳細
最初の初期化
memcached -u ec2-user -vvv コマンドを実行した際の挙動
1 main at memcached.c
sanitycheck関数後に、signal関数。その後、settings_init
2 sanitycheck at memcached.c
event_get_versionはlibeventの関数。ここでは、"2.0.21-stable"という文字列を取得しeverに格納されている。
2 settings_init at memcached.c
パラメータのデフォルト値が設定されている
1 main at memcached.c
2 init_lru_maintainer at items.c
1 main at memcached.c
-u ec2-user のようにユーザー名を与えているので、username 変数に"ec2-user"が設定される。
-vvv と指定しているので、getopt_longの取得結果のvの処理はwhile文で3回繰り返される。
その後、パラメータの値が、制限値外のものが指定されていたらエラーで終了する。
hash_init関数が実行されている
2 hash_init at hash.c
typeでMURMUR3_HASHが指定されている。
1 main at memcacached.c
必要に応じて多くのコネクション数を許可するためにgetrlimit
mainスレッドのlibeventインスタンスの初期化
各種スレッド等の初期化
2 logger_init at logger.c
3 start_logger_thread at logger.c
3 STATS_LOCK, STATS_UNLOCK at thread.c
2 stats_init at memcached.c
3 stats_prefix_init at stats.c
3 assoc_init at assoc.c
2 conn_init at memcached.c
2 slabs_init (limit=67108864, factor=1.25, prealloc=false, slab_sizes=0x0) at slabs.c:158
Slabの初期化 ★5
slabclass配列の初期化
1 main at memcacached.c
workerスレッドの設定
2 memcached_thread_init at thread.c
3 setup_thread (me=0x6486f0) at thread.c:321
4 cq_init (cq=0x64fab0) at thread.c:196
4 cache_create (name=0x42b442 "suffix", bufsize=50, align=8, constructor=0x0, destructor=0x0) at cache.c:22
3 create_worker (func=0x41dddf <worker_libevent>, arg=0x6486f0) at thread.c:296
2 init_lru_crawler (arg=0x0) at crawler.c:686
2 start_assoc_maintenance_thread () at assoc.c:270
2 start_item_crawler_thread () at crawler.c:497
2 start_lru_maintainer_thread (arg=0x0) at items.c:1680
2 start_slab_maintenance_thread () at slabs.c:1311
2 clock_handler (fd=0, which=0, arg=0x0) at memcached.c:6321
6358行目で関数がreturnされる。
3 assoc_start_expand (curr_items=0) at assoc.c:144
1 main at memcached.c
2 server_sockets (port=11211, transport=tcp_transport, portnumber_file=0x0) at memcached.c:6146
3 server_socket (interface=0x0, port=11211, transport=tcp_transport, portnumber_file=0x0, ssl_enabled=false) at memcached.c:5998
4 new_socket (ai=0x6520f0) at memcached.c:5936
3
ソケットオプションの設定
bind処理
conn_new関数が呼ばれる
4 conn_new (sfd=35, init_state=conn_listening, event_flags=18, read_buffer_size=1, transport=tcp_transport, base=0x646040, ssl=0x0) at memcached.c:547
ここで event_set で event_handler が設定されている。この event_handler関数が、後ほどコマンド実行される度に呼び出される関数。
1
最後にmain_baseがevent_base_loopによりイベントのループ処理が始まる。
2 uriencode_init () at util.c:16
SETコマンドの挙動
1 event_handler (fd=35, which=2, arg=0x6521e0) at memcached.c:5913
2 drive_machine (c=0x6521e0) at memcached.c:5500
c->stateがconn_listening
3 dispatch_conn_new (sfd=37, init_state=conn_new_cmd, event_flags=18, read_buffer_size=2048, transport=tcp_transport
, ssl=0x0) at thread.c:491
thread->notify_send_fdにはbufに格納された"c"をwriteする。
4 cqi_new () at thread.c:240
4 cq_push (cq=0x64f250, item=0x658270) at thread.c:225
1 event_handler
2 drive_machine
その後一旦libeventの処理に戻り、event_handlerからdrive_machine
c->stateがconn_new_cmdとなっている。
3 reset_cmd_handler (c=0x7ffff00109a0) at memcached.c:2766
最後にconn_set_state(c, conn_waiting)が実行される。
4 conn_shrink (c=0x7ffff00109a0) at memcached.c:910
4 conn_set_state (c=0x7ffff00109a0, state=conn_waiting) at memcached.c:985
1 event_handler
2 drive_machine
c->stateがconn_waitingになっている。最後にconn_set_state関数でconn_readに設定している。
3 update_event (c=0x7ffff00109a0, new_flags=18) at memcached.c:5295
1 event_handler
2 drive_machine
再びlibeventの処理に戻る。event.cのevent_process_active関数中でevent_process_activeが実行されると再びevent_handlerに戻ってくる c->stateがconn_readに設定されている。
resがREAD_DATA_RECEIVEDより、conn_parse_cmdに設定される。
3 try_read_network (c=0x7ffff00109a0) at memcached.c:5231
5276行目でbreak。その後、関数もretrunされる。
4 tcp_read (c=0x7ffff00109a0, buf=0x7ffff0010bd0, count=2048) at memcached.c:162
2
3 try_read_command (c=0x7ffff00109a0) at memcached.c:5053
実際に入力した内容を読み込む処理。
MemcachedではASCIIモードとバイナリーモードが利用できるが、ここではASCIIモードを利用しているので、そちらの処理を実行。
4
4 process_command (c=0x7ffff00109a0, command=0x7ffff0010bd0 "set test1 0 0 7") at memcached.c:4731
5 add_msghdr (c=0x7ffff00109a0) at memcached.c:329
5 tokenize_command (command=0x7ffff0010bd0 "set test1 0 0 7", tokens=0x7ffff6da8bd0, max_tokens=8) at memcached.c:3045
ntokensでは、"set test1 0 0 7 sample1"の要素数である6が返される。
4
tokenize_command関数の実行結果を元に処理が実行される。ここではSETコマンドなのでprocess_update_command関数の実行
5 process_update_command (c=0x7ffff00109a0, tokens=0x7ffff6da8bd0, ntokens=6, comm=2, handle_cas=false) at memcached.c:4107
6 set_noreply_maybe (c=0x7ffff00109a0, tokens=0x7ffff6da8bd0, ntokens=6) at memcached.c:3102
ここではSETコマンド実行時にnoreplyオプションを指定していないので何も実行されない。
5
item_allocが実行される
6 realtime (exptime=0) at memcached.c:208
6 item_alloc (key=0x7ffff0010bd4 "test1", nkey=5, flags=0, exptime=0, nbytes=9) at thread.c:579
7 do_item_alloc (key=0x7ffff0010bd4 "test1", nkey=5, flags=0, exptime=0, nbytes=9) at items.c:260
CASを有効にしていると、uint64_tのサイズ分の8バイトが全体サイズに追加されている処理も行われている。
8 item_make_header (nkey=6 '\006', flags=0, nbytes=9, suffix=0x7ffff6da8a70 "", nsuffix=0x7ffff6da8aa7 "") at items.c:171
8 slabs_clsid (size=71) at slabs.c:92
7
elseのdo_item_alloc_pullが実行される
8 do_item_alloc (key=0x7ffff0010bd4 "test1", nkey=5, flags=0, exptime=0, nbytes=9) at items.c:272
slabs_allocが実行される。
9 slabs_alloc (size=71, id=1, total_bytes=0x7ffff6da8a28, flags=0) at slabs.c:658
10 do_slabs_alloc (size=71, id=1, total_bytes=0x7ffff6da8a28, flags=0) at slabs.c:337
do_slabs_newslabが実行される。
11 do_slabs_newslab (id=1) at slabs.c:302
12 grow_slab_list (id=1) at slabs.c:270
12 memory_allocate (size=1048576) at slabs.c:611
12 split_slab_page_into_freelist (ptr=0x7ffff44a4010 "", id=1) at slabs.c:282
13 do_slabs_free (ptr=0x7ffff44a4010, size=0, id=1) at slabs.c:447
8
5
conn_nreadの状態に設定される。
2
drive_machineに再度戻ってきて、conn_nreadの処理が実行される。1週目はc->rlbytes > 0で特に何も実行されないが、2週目で再度case文が実行されて、c->rlbytesが0なので、complete_nreadが実行される
3 complete_nread (c=0x7ffff00109a0) at memcached.c:2785
ASCIIモードなので、 complete_nread_asciiが実行される。
4 complete_nread_ascii (c=0x7ffff00109a0) at memcached.c:1231
対象のデータをキャッシュに保存するstore_item関数が実行される
5 store_item (item=0x7ffff45a3f70, comm=2, c=0x7ffff00109a0) at thread.c:689
6 hash
MurmurHash3_x86_32関数が実行される。
7 getblock32 (i=-1, p=0x7ffff45a3fac) at murmur3_hash.c:50
7 rotl32 (x=787512756, r=15 '\017') at murmur3_hash.c:35
7 fmix32 (h=2252073768) at murmur3_hash.c:58
6 item_lock (hv=3155333867) at thread.c:104
6 item_unlock (hv=3155333867) at thread.c:120
6 do_store_item (it=0x7ffff45a3f70, comm=2, c=0x7ffff00109a0, hv=3155333867) at memcached.c:2897
7 do_item_get (key=0x7ffff45a3fa8 "test1", nkey=5, hv=3155333867, c=0x7ffff00109a0, do_update=false) at items.c:955
Lazy expirationですでにあるキー中のメモリは上書きされる。ここでは特に保存したこともないので、"> NOT FOUND ")が表示される。
6
do_item_linkが実行される。
7 do_item_link (it=0x7ffff45a3f70, hv=3155333867) at items.c:474
8 get_cas_id () at items.c:112
8 assoc_insert (it=0x7ffff45a3f70, hv=3155333867) at assoc.c:160
8 item_link_q (it=0x7ffff45a3f70) at items.c:421
9 do_item_link_q (it=0x7ffff45a3f70) at items.c:397
8 item_stats_sizes_add (it=0x7ffff45a3f70) at items.c:907
6
4
retがSTOREDとなっている。
5 out_string (c=0x7ffff00109a0, str=0x424f99 "STORED") at memcached.c:1171
conn_writeに設定される。
5 item_remove (item=0x7ffff45a3f70) at thread.c:638
hash関数は、先程と同様MurmurHash3_x86_32関数が実行される。
6 do_item_remove (it=0x7ffff45a3f70) at items.c:529
2
drive_machineに戻ってくる。c->stateはconn_write
3 add_iov (c=0x7ffff00109a0, buf=0x7ffff00113e0, len=8) at memcached.c:1048
その後、ensure_iov_space関数が呼ばれる。ここでは特に何もしない。
4 ensure_iov_space (c=0x7ffff00109a0) at memcached.c:1008
3 transmit (c=0x7ffff00109a0) at memcached.c:5361
TRANSMIT_INCOMPLETEをreturnする。その後、抜けて、drive_machineからc->stateはconn_writeで再度この関数が実行される。その時は、TRANSMIT_COMPLETEをreturnする。
4 tcp_sendmsg (c=0x7ffff00109a0, msg=0x7ffff0013e80, flags=0) at memcached.c:167
2
c->write_and_goにはconn_new_cmdが設定されており、c->stateはこの状態に設定される。
再びループでc->stateの状態に基づいた処理が実行され、ここでは、conn_new_cmd
3 reset_cmd_handler (c=0x7ffff00109a0) at memcached.c:2766
conn_waitingの状態に設定。
2
update_event関数ではtrueが返され、conn_readが実行される。stop変数のフラグにtrueが設定されたことによりwhile文のループは終了する。
事前準備
Memcached
デバッグ用にビルドしておく。また、-O2で最適化しているとgdbの際に順にコードを追っていくと困難になるので-O0に変更しておく。そうでないと、デバッグの際に、順番に実行してもソースコード中の動きがばらばらになる。
cgdb
https://cgdb.github.io/
-std=c++11をつけないと、make時に"kui.cpp:310:15: error: ‘it’ does not name a type"のエラー https://github.com/cgdb/cgdb/issues/184
デバッグ
MemcachedはLibeventを利用しており、コマンド実行時に呼ばれる関数はevent_handlerを起点とする。そのため、ここにブレークポイントを打っておくと良い。
event_base_loop の前まで実行
SETコマンド実行時、ここで別ターミナルから $ telnet localhost 11211 を実行。以下のコマンドを実行。
スレッド3に切り替えて、nextを続けていると、先程ブレークポイントを設定しておいたevent_handler関数で止まる。
繰り返し処理が多く、ループを飛ばすには以下を実行
定数確認にはシェルでgrep
Last updated