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が実行される。

8 get_cas_id () at items.c:112

8 assoc_insert (it=0x7ffff45a3f70, hv=3155333867) at assoc.c:160

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