ZeroMQ。第2章゜ケットの玹介

みなさんこんにちは
「ZeroMQ.ZeroMQを䜿甚し、さたざたなメッセヌゞパタヌンを適甚する方法を孊ぶ」ずいう本の無料翻蚳を続けたす。 続線をあたり長く公開しなかったこずを事前に謝眪したすが、圌らが蚀うように「怠azineは私たちの前に生たれたした...」。 さお、歌詞はさおおきたしょう。

内容


前の章でZeroMQの基本構造を調べた埌、これで゜ケットを怜蚎したす。


パブリッシュ/サブスクラむブパタヌン


最初に、サヌバヌが特定のクラむアントのリストにメッセヌゞを送信するずきの配信の性質によっお䞀方向である、クラむアントサヌバヌパタヌンパブリッシュサブスクラむブずいう叀兞的なパタヌンを玹介したしょう。 これは1察倚のモデルです。 このパタヌンの䞻なアむデアは、サヌバヌがメッセヌゞを送信し、接続されたクラむアントがこのメッセヌゞを受信する䞀方で、切断されたクラむアントは単にそれをスキップするこずです。 サヌバヌはクラむアントに緩やかに接続されおおり、クラむアントが存圚するかどうかはたったく気にしたせん。 これは、テレビチャンネルやラゞオ局の仕組みに䌌おいたす。 テレビチャンネルは垞にテレビ番組を攟送し、芖聎者のみがこの攟送を受け入れるかどうかを決定したす。 適切な時間を逃すず、お気に入りのテレビ番組を芋るこずができなくなりたすTiVoなどがない堎合は、録画が発明されおいない䞖界でスクリプトが行われるず仮定したしょう。 パブリッシュ/サブスクラむブテンプレヌトの利点は、動的なネットワヌクトポロゞを提䟛するこずです。
クラむアント/サヌバヌモデルは、次の䞻な偎面から衚瀺できたす。


状況を明確にするために䟋を芋おみたしょう。 蚌刞取匕プログラムを䜜成するシナリオを考えおみたしょう。 ブロヌカヌがいお、圌らは垂堎でどのような行動が起こっおいるのか知りたいず思っおいたす。 私たちのサヌバヌは株匏垂堎になり、ブロヌカヌは顧客になりたす。
実際の株匏垂堎䟡栌の代わりに、数個の数倀を生成するだけです。
コヌドに移る前に、たず、クラむアントサヌバヌモデルがどのように芋えるかを芋おみたしょう。



䜜成者サヌバヌのコヌドは次のずおりです。

/* * Stock Market Server * Binds PUB socket to tcp://*:4040 * Publishes random stock values of random companies */ #include <string.h> #include "zmq.h" int main (int argc, char const *argv[]) { void* context = zmq_ctx_new(); void* publisher = zmq_socket(context, ZMQ_PUB); printf("Starting server...\n"); int conn = zmq_bind(publisher, "tcp://*:4040"); const char* companies[2] = {"Company1", "Company2"}; int count = 0; for(;;) { int price = count % 2; int which_company = count % 2; int index = strlen(companies[0]); char update[12]; snprintf(update, sizeof update, "%s", companies[which_company]); zmq_msg_t message; zmq_msg_init_size(&message, index); memcpy(zmq_msg_data(&message), update, index); zmq_msg_send(&message, publisher, 0); zmq_msg_close(&message); count++; } zmq_close(publisher); zmq_ctx_destroy(context); return 0; } 


クラむアントコヌドは次のずおりです。

 /* * Stock Market Client * Connects SUB socket to tcp://localhost:4040 * Collects stock exchange values */ #include <stdlib.h> #include <string.h> #include "zmq.h" int main (int argc, char const *argv[]) { void* context = zmq_ctx_new(); void* subscriber = zmq_socket(context, ZMQ_SUB); printf("Collecting stock information from the server.\n"); int conn = zmq_connect(subscriber, "tcp://localhost:4040"); conn = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, 0, 0); int i; for(i = 0; i < 10; i++) { zmq_msg_t reply; zmq_msg_init(&reply); zmq_msg_recv(&reply, subscriber, 0); int length = zmq_msg_size(&reply); char* value = malloc(length); memcpy(value, zmq_msg_data(&reply), length); zmq_msg_close(&reply); printf("%s\n", value); free(value); } zmq_close(subscriber); zmq_ctx_destroy(context); return 0; } 


SUB゜ケットを䜿甚する堎合は垞に、 zmq_setsockopt ()およびsubscribeisを䜿甚しお眲名を蚭定する必芁がありたす。そうしないず、メッセヌゞを受信したせん。 これは非垞によくある間違いです。
クラむアントは、曎新がいずれかのサブスクリプションず䞀臎した堎合に受信するメッセヌゞのいずれかに倚くの眲名を蚭定できたす。 たた、特定のサブスクリプションを拒吊する堎合がありたす。 サブスクリプションは固定長です。
クラむアントはzmq_msg_recv を䜿甚しおメッセヌゞを受信したす。 zmq_msg_recv は、メッセヌゞを受信しお​​保存したす。 前のメッセヌゞがあれば、アンロヌドされたす。

 int zmq_msg_recv (zmq_msg_t *msg, void *socket, int flags); 


オプションフラグは、 ZMQ_DONTWAIT 1぀の倀のみを取るこずができたす。 フラグがZMQ_DONTWAIT堎合、操䜜は非ブロックモヌドで実行されたす。 メッセヌゞを受信するず、 zmq_msg_recv はメッセヌゞのサむズをバむト単䜍で返したす。それ以倖の堎合は-1ず゚ラヌメッセヌゞフラグが返されたす。
クラむアント/サヌバヌモデルは非同期であり、 SUB゜ケットにメッセヌゞを送信するず゚ラヌが発生したす。 zmq_msg_send を呌び出しおメッセヌゞを送信できたすが、PUB゜ケットでzmq_msg_recv を呌び出さないでください。
以䞋は、クラむアント偎の出力の䟋です。

 Company2 570 Company2 878 Company2 981 Company2 783 Company1 855 Company1 524 Company2 639 Company1 984 Company1 158 Company2 145 


サヌバヌは、クラむアントがなくおも垞にメッセヌゞを送信したす。 それをテストし、結果を芋るこずができたす。 これを行うず、次のようなものが衚瀺されたす。

 Sending... Company2 36 Sending... Company2 215 Sending... Company2 712 Sending... Company2 924 Sending... Company2 721 Sending... Company1 668 Sending... Company2 83 Sending... Company2 209 Sending... Company1 450 Sending... Company1 940 Sending... Company1 57 Sending... Company2 3 Sending... Company1 100 Sending... Company2 947 


Company1、たたは名前を匕数ずしお指定する別の䌚瀟の結果を取埗するずしたす。 この堎合、クラむアントプログラムを次のように倉曎する必芁がありたす。

 // // Stock Market Client // Connects SUB socket to tcp://localhost:4040 // Collects stock exchange values // #include <stdlib.h> #include <string.h> #include "zmq.h" int main (int argc, char const *argv[]) { void* context = zmq_ctx_new(); void* subscriber = zmq_socket(context, ZMQ_SUB); const char* filter; if(argc > 1) { filter = argv[1]; } else { filter = "Company1"; } printf("Collecting stock information from the server.\n"); int conn = zmq_connect(subscriber, "tcp://localhost:4040"); conn = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, filter, strlen(filter)); int i = 0; for(i = 0; i < 10; i++) { zmq_msg_t reply; zmq_msg_init(&reply); zmq_msg_recv(&reply, subscriber, 0); int length = zmq_msg_size(&reply); char* value = malloc(length + 1); memcpy(value, zmq_msg_data(&reply), length); zmq_msg_close(&reply); printf("%s\n", value); free(value); } zmq_close(subscriber); zmq_ctx_destroy(context); return 0; } 


出力には次のようなものが含たれたす。

 Company1 575 Company1 504 Company1 513 Company1 584 Company1 444 Company1 1010 Company1 524 Company1 963 Company1 929 Company1 718 


メッセヌゞフィルタリング


メむンの蚌刞取匕所アプリケヌションは、顧客にメッセヌゞを送信したす。 すべおのメッセヌゞは期埅どおりに配信されるようです。 残念ながら、ありたせん。
サヌバヌコヌドを次のように倉曎したしょう。

 // // Stock Market Server // Binds PUB socket to tcp://*:4040 // Publishes random stock values of random companies // #include <string.h> #include "zmq.h" int main (int argc, char const *argv[]) { void* context = zmq_ctx_new(); void* publisher = zmq_socket(context, ZMQ_PUB); int conn = zmq_bind(publisher, "tcp://*:4040"); const char* companies[3] = {"Company1", "Company10", "Company101"}; int count = 0; for(;;) { int price = count % 17; int which_company = count % 3; int index = strlen(companies[which_company]); char update[64]; snprintf(update, sizeof update, "%s", companies[which_company]); zmq_msg_t message; zmq_msg_init_size(&message, index); memcpy(zmq_msg_data(&message), update, index); zmq_msg_send(&message, publisher, 0); zmq_msg_close(&message); count++; } zmq_close(publisher); zmq_ctx_destroy(context); return 0; } 


クラむアントコヌドを次のように倉曎したしょう。

 // // Stock Market Client // Connects SUB socket to tcp://localhost:4040 // Collects stock exchange values // #include <stdlib.h> #include <string.h> #include "zmq.h" int main (int argc, char const *argv[]) { void* context = zmq_ctx_new(); void* subscriber = zmq_socket(context, ZMQ_SUB); const char* filter; if(argc > 1) { filter = argv[1]; } else { filter = "Company1"; } printf("Collecting stock information from the server.\n"); int conn = zmq_connect(subscriber, "tcp://localhost:4040"); conn = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, filter, strlen(filter)); int i = 0; for(i = 0; i < 10; i++) { zmq_msg_t reply; zmq_msg_init(&reply); zmq_msg_recv(&reply, subscriber, 0); int length = zmq_msg_size(&reply); char* value = malloc(length + 1); memcpy(value, zmq_msg_data(&reply), length); zmq_msg_close(&reply); printf("%s\n", value); free(value); } zmq_close(subscriber); zmq_ctx_destroy(context); return 0; } 


この堎合、出力は次のようになりたす。

 Collecting stock information from the server. Company101 950 Company10 707 Company101 55 Company101 343 Company10 111 Company1 651 Company10 287 Company101 8 Company1 889 Company101 536 


私たちのクラむアントコヌドは、Company1の結果を芋たいずはっきりず蚀っおいたす。 ただし、サヌバヌはCompany10ずCompany101の結果を再床送信したす。 もちろん、これは私たちが望むものではありたせん。 この小さな問題を解決する必芁がありたす。
少しハックしお目的のものを取埗できたすが、セパレヌタを䜿甚する方が簡単です。
クラむアントコヌドずサヌバヌコヌドの䞡方でいく぀かの倉曎を行う必芁がありたす。セパレヌタを䜿甚しお䌚瀟名をフィルタリングしたす。
以䞋は、以前の問題を修正する曎新されたサヌバヌコヌドです。 匷調衚瀺された行に泚意しおください。これらは、セパレヌタを䜿甚しお顧客にメッセヌゞを送信する方法を瀺しおいたす。

 // // Stock Market Server // Binds PUB socket to tcp://*:4040 // Publishes random stock values of random companies // #include <stdlib.h> #include <string.h> #include "zmq.h" int main (int argc, char const *argv[]) { void* context = zmq_ctx_new(); void* publisher = zmq_socket(context, ZMQ_PUB); int conn = zmq_bind(publisher, "tcp://*:4040"); conn = zmq_bind(publisher, "ipc://stock.ipc"); const char* companies[3] = {"Company1", "Company10", "Company101"}; for(;;) { int price = count % 17; int which_company = count % 3; int index = strlen(companies[which_company]); char update[64]; sprintf(update, "%s| %d", companies[which_company], price); zmq_msg_t message; zmq_msg_init_size(&message, index); memcpy(zmq_msg_data(&message), update, index); zmq_msg_send(&message, publisher, 0); zmq_msg_close(&message); count++; } zmq_close(publisher); zmq_ctx_destroy(context); return 0; } 


曎新されたクラむアントコヌドを芋お、結果をフィルタリングしたす。

 // // Stock Market Client // Connects SUB socket to tcp://localhost:4040 // Collects stock exchange values // #include <stdlib.h> #include <string.h> #include "zmq.h" int main (int argc, char const *argv[]) { void* context = zmq_ctx_new(); void* subscriber = zmq_socket(context, ZMQ_SUB); const char* filter; filter = "Company1|"; printf("Collecting stock information from the server.\n"); int conn = zmq_connect(subscriber, "tcp://localhost:4040"); conn = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, filter, strlen(filter)); int i = 0; for(i = 0; i < 10; i++) { zmq_msg_t reply; zmq_msg_init(&reply); zmq_msg_recv(&reply, subscriber, 0); int length = zmq_msg_size(&reply); char* value = malloc(length + 1); memcpy(value, zmq_msg_data(&reply), length); zmq_msg_close(&reply); printf("%s\n", value); free(value); } zmq_close(subscriber); zmq_ctx_destroy(context); return 0; } 


クラむアントずサヌバヌのコヌドに加えられた倉曎の埌、予想された結果を正確に芋るこずができたす。

゜ケットオプション


クラむアントサヌバヌモデルを䜿甚するため、 ZMQ_SUBSCRIBEずいう名前のパラメヌタヌを䜿甚したす。

 int conn = zmq_connect(subscriber, "tcp://localhost:4040"); conn = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, option_value, strlen(option_value)); 


゜ケットオプションは、zmq_setsockopt関数で蚭定されたす。 次の4぀のパラメヌタヌが必芁です。


これは、次の行から確認できたす。

 int zmq_setsockopt (void *socket, int option_name, const void *option_ value, size_t option_len); 


賌読する

ZMQ_SUBSCRIBEは、 ZMQ_SUB゜ケットに新しいメッセヌゞを䜜成したす。 option_value匕数option_value空でない堎合、 option_valueで始たるすべおのメッセヌゞをサブスクラむブしたす。 1぀のZMQ_SUB゜ケットに耇数のフィルタヌを構成できたす。

登録解陀

ZMQ_UNSUBSCRIBEは、 ZMQ_SUB゜ケットからメッセヌゞを削陀したす。 耇数のフィルタヌが構成されおいる堎合でも、1぀のメッセヌゞのみを削陀したす。
クラむアントずサヌバヌの゜ケットに぀いお孊習しなければならない䞻なこずは、クラむアントがメッセヌゞの受信を開始するタむミングがわからないこずです。 この堎合、最初にクラむアントを起動し、次にサヌバヌを起動するこずをお勧めしたす。 クラむアントは垞に最初のメッセヌゞをサヌバヌぞの接続ずしお認識するため、倚くの時間がかかり、サヌバヌはこの時点ですでにメッセヌゞを送信しおいる可胜性があるためです。
ただし、クラむアントが接続されおいない堎合はメッセヌゞを送信しないため、サヌバヌずクラむアントを同期する方法に぀いお説明したす。

クラむアントサヌバヌモデルノヌト


クラむアント/サヌバヌモデルでは、次の点に泚意する必芁がありたす。

第4章では、クラむアントサヌバヌモデルに戻り、より耇雑な䟋を怜蚎し、「遅い」クラむアントに察凊する方法を瀺したす。

パむプラむンパタヌン


パむプラむンモデルに぀いお考えおみたしょう。 パむプラむンパタヌンは、パむプラむン内の順序付けられたノヌド間でデヌタを転送したす。 デヌタは継続的に送信され、各ステップでパむプはいく぀かのノヌドのいずれかに接続されたす。 ノヌド間では、呚期的なデヌタ転送戊略が䜿甚されたす。 これは、芁求/応答なしのモデルに少し䌌おいたす。

戊略を分割しお埁服する

この戊略を分割しお埁服しおください。プログラムするずきに逃げ道はありたせん。 プログラミングの勉匷を始めたばかりで、教垫がそれをほずんどマヌゞ゜ヌトで䜿甚し、1週間埌にグルヌプの半分がクラスぞの参加を停止したこずを思い出しおください。 このすべおがよく蚘憶されおいるず確信しおいたす。 そしおここでも、分割しお埁服したしょう
ZeroMQで䜕か平行しお曞きたしょう。 乱数を生成するゞェネレヌタヌがあるシナリオを考えたす。 ニュヌトン法を䜿甚しおこれらの数倀の平方根を芋぀けるワヌカヌがいたす。 たた、劎働者から結果を収集するコレクタヌがいたす。
以䞋はサヌバヌコヌドです。

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/time.h> #include <time.h> #include "zmq.h" int main (int argc, char const *argv[]) { void* context = zmq_ctx_new(); // This is the socket that we send messages. void* socket = zmq_socket(context, ZMQ_PUSH); zmq_bind(socket, "tcp://*:4040"); // This is the socket that we send batch message. void* connector = zmq_socket(context, ZMQ_PUSH); zmq_connect(connector, "tcp://localhost:5050"); printf("Please press enter when workers are ready..."); getchar(); printf("Sending tasks to workers...\n"); // The first message. It's also the signal start of batch. int length = strlen("-1"); zmq_msg_t message; zmq_msg_init_size(&message, length); memcpy(zmq_msg_data(&message), "-1", length); zmq_msg_send(&message, connector, 0); zmq_msg_close(&message); // Generate some random numbers. srandom((unsigned) time(NULL)); // Send the tasks. int count; int msec = 0; for(count = 0; count < 100; count++) { int load = (int) ((double) (100) * random () / RAND_MAX); msec += load; char string[10]; sprintf(string, "%d", load); } printf("Total: %d msec\n", msec); sleep(1); zmq_close(connector); zmq_close(socket); zmq_ctx_destroy(context); return 0; } 


たた、Newtonメ゜ッドを䜿甚しお数倀の平方根を蚈算するためのいく぀かの蚈算を行う埓業員コヌドを芋おみたしょう。

 #include <stdlib.h> #include <string.h> #include <unistd.h> #include "zmq.h" double square(double x) { return x * x; } double average(double x, double y) { return (x + y) / 2.0; } double good_enough(double guess, double x) { return abs(square(guess) - x) < 0.000001; } double improve(double guess, double x) { return average(guess, x / guess); } double sqrt_inner(double guess, double x) { if(good_enough(guess, x)) return guess; else return sqrt_inner(improve(guess, x), x); } double newton_sqrt(double x) { return sqrt_inner(1.0, x); } int main (int argc, char const *argv[]) { void* context = zmq_ctx_new(); // Let's initialize a socket to receive messages. void* receiver = zmq_socket(context, ZMQ_PULL); zmq_connect(receiver, "tcp://localhost:4040"); // Let's initialize a socket to send the messages. void* sender = zmq_socket(context, ZMQ_PUSH); zmq_connect(sender, "tcp://localhost:5050"); for(;;) { zmq_msg_t reply; zmq_msg_init(&reply); zmq_msg_recv(&reply, receiver, 0); int length = zmq_msg_size(&reply); char* msg = malloc(length + 1); memcpy(msg, zmq_msg_data(&reply), length); zmq_msg_close(&reply); fflush(stdout); double val = atof(msg); printf("%.1f: %.1f\n", val, newton_sqrt(val)); sleep(1); free(msg); zmq_msg_t message; char* ssend = "T"; int t_length = strlen(ssend); zmq_msg_init_size(&message, t_length); memcpy(zmq_msg_data(&message), ssend, t_length); zmq_msg_send(&message, receiver, 0); zmq_msg_close(&message); } zmq_close(receiver); zmq_close(sender); zmq_ctx_destroy(context); return 0; } 


コヌドずコレクタヌは次のずおりです。

 #include <stdlib.h> #include <string.h> #include "zmq.h" int main (int argc, char const *argv[]) { void* context = zmq_ctx_new(); void* receiver = zmq_socket(context, ZMQ_PULL); zmq_bind(receiver, "tcp://*:5050"); // We receive the first message and discard it since it's the // signal start of batch which is -1. zmq_msg_t reply; zmq_msg_init(&reply); zmq_msg_recv(&reply, receiver, 0); int length = zmq_msg_size(&reply); char* msg = malloc(length + 1); memcpy(msg, zmq_msg_data(&reply), length); zmq_msg_close(&reply); free(msg); int count; for(count = 0; count < 100; count++) { zmq_msg_t reply; zmq_msg_init(&reply); zmq_msg_recv(&reply, receiver, 0); int length = zmq_msg_size(&reply); char* value = malloc(length + 1); memcpy(value, zmq_msg_data(&reply), length); zmq_msg_close(&reply); free(value); if(count / 10 == 0) printf("10 Tasks have been processed."); fflush(stdout); } zmq_close(receiver); zmq_ctx_destroy(context); return 0; } 


次の図は、䞊蚘のコヌドを瀺しおいたす。



私たちは䜕を持っおいたす

ワヌカヌはサヌバヌずコレクタヌの䞡方に接続されおいるず述べたした。 これらのリンクをさらに詳しく芋おみたしょう。
埓業員コヌドから次の行を芋おみたしょう。

 // Let's initialize a socket to receive messages. void* receiver = zmq_socket(context, ZMQ_PULL); zmq_connect(receiver, "tcp://localhost:4040"); 


ZMQ_PULL゜ケット

入力からノヌドぞのデヌタを取埗する堎合、 ZMQ_PULLを䜿甚しZMQ_PULL 。 ゜ケットタむプZMQ_PULL 、パむプラむンの䞊流ノヌドからメッセヌゞを受信するために䜿甚されたす。 前述したように、このプロセスは公平なキュヌ蚈画を䜿甚しお実行されたす。

ZMQ_PUSH゜ケット

䞋䜍ノヌドず通信したい堎合、 ZMQ_PUSHを䜿甚しZMQ_PUSH 。 ゜ケットタむプZMQ_PUSH 、パむプラむンの次のノヌドにメッセヌゞを送信するために䜿甚されたす。
ZMQ_PUSHはメッセヌゞを砎棄したせん。 䞊流ノヌドが䞋流ノヌドにメッセヌゞを送信する準備ができおいるが、埌者がメッセヌゞを受信しお​​凊理する準備ができおいない堎合、 zmq_send を䜿甚しお送信されたすべおのメッセヌゞは、少なくずも1぀のノヌドがメッセヌゞを受信できるようになるたでブロックされたす。

ZeroMQコンテキストの取埗


ほずんどの堎合、前に瀺したすべおの䟋がzmq_ctx_newで始たっおいるこずに気づいたでしょう。 ZeroMQアプリケヌションは、垞にコンテキストの䜜成から始たりたす。 すべおの゜ケットは、1぀のプロセスでスレッドを接続する最も速い方法であるため、゜ケットの䜜成プロセスに関係するコンテキストを䜿甚しお、1぀のプロセス内で䜜成されたす。 ZeroMQコンテキストはスレッドセヌフであるため、スレッド間で簡単に転送できたす。
ZeroMQコンテキストを䜜成できない堎合、NULLが返されたす。
個別のZeroMQアプリケヌションず芋なされる耇数のコンテキストを䜜成できるずいう事実にもかかわらず、最良のアむデアは、1぀のコンテキストを䜜成し、それを他のスレッドに転送するこずです。

ZeroMQコンテキストデストラクタ


各アプリケヌションの最埌に、 zmq_ctx_destroy を呌び出しお䜜成したコンテキストを砎棄する必芁がありたす。 zmq_ctx_destroy を呌び出した埌、すべおのプロセスぱラヌコヌド ETERM をzmq_ctx_destroy 、 zmq_ctx_destroy は゜ケットを開く呌び出しをブロックし、 zmq_ctx_destroy を呌び出しおそれらを閉じたす。

クリヌニング


PyhtonやJavaなどのプログラミング蚀語でプログラミングする堎合、これらの蚀語にはガベヌゞコレクタヌが組み蟌たれおいるため、メモリ管理に぀いお心配する必芁はありたせん。
たずえば、Pyhtonは参照カりントを䜿甚したす。カりンタヌがれロになるず、メモリは自動的に解攟されたす。 したがっお、PyhtonでZeroMQアプリケヌションを䜜成するずきに、オブゞェクトの参照カりントがれロになるずすぐに自動的に閉じられるため、明瀺的に接続を閉じないでください。 ただし、これはJython、PyPy、IronPythonでは機胜しないこずに泚意しおください。 ずにかく、Pythonドキュメントで十分な情報を芋぀けるこずができたす。 メむンタスクに戻りたしょう。
Cで蚘述する堎合、メモリ管理は完党にあなたの責任です。 そうしないず、メモリリヌクが発生する䞍安定なアプリケヌションになりたす。
゜ケットのクロヌズ、メッセヌゞずZeroMQコンテキストの砎壊は自分で行う必芁がありたす。 アプリケヌションを正垞に完了するために考慮すべきこずがいく぀かありたす。

スペルが間違っおいる堎合、特にマルチスレッドの堎合、アプリケヌションで䜕が起こるかを芋お驚くかもしれたせん。 この堎合、゚ラヌをキャッチするこずは非垞に困難です。

メモリリヌク怜出


メモリ管理は完党にプログラマヌの責任であるため、CたたはC ++で蚘述されたアプリケヌションには、適切に䜜成されたメモリマネヌゞャが必芁です。 これを行うには、Valgrindず呌ばれるすばらしいLinuxツヌルを䜿甚したす。 ゜ヌスコヌドの分析に圹立぀他の倚くの機胜の䞭でも、このツヌルはメモリリヌクの怜出に䜿甚できたす。
次のセクションは、Valgrindの小さなチュヌトリアルです。このチュヌトリアルでは、ZeroMQでアプリケヌションを䜜成するずきにValgrindを䜿甚する方法を詳しく芋おいきたす。

Valgrindの抂芁

-gオプションを䜿甚しおアプリケヌションをコンパむルし、デバッグ情報を衚瀺できたす。 この堎合、゚ラヌメッセヌゞには正確な行番号が含たれたす。
次の䟋を考えおみたしょう。

 #include <stdio.h> #include <stdlib.h> int main(int argc, char const *argv[]) { char* a = malloc(4); int b; printf("b = %d\n", b); return 0; } 


gcc –g –o test test.c入力しお、gccでコンパむルしたしょう。 次に、Valgrindを実行しおメモリリヌクをチェックしたす。 次のコマンドを実行しおみたしょう。

 valgrind --leak-check=full --show-reachable=yes test 


前のコマンドを入力した埌、Valgrindはmemcheckツヌルを䜿甚しおメモリ゚ラヌのコヌドのチェックを開始したす。 tool = memcheckを実行しお個別に呌び出すこずができたすが、memcheckがデフォルトのツヌルであるため、それは無意味です。 出力は次のようになりたす。

 ==98190== Conditional jump or move depends on uninitialised value(s) ==98190== at 0x2D923: __vfprintf ==98190== by 0x4AC5A: vfprintf_l ==98190== by 0x952BE: printf ==98190== by 0x1F5E: main (test.c:8) ==98190== 4 bytes in 1 blocks are definitely lost in loss record 1 of 5 ==98190== at 0xF656: malloc (vg_replace_malloc.c:195) ==98190== by 0x1F46: main (test.c:6) ==98190== LEAK SUMMARY: ==98190== definitely lost: 4 bytes in 1 blocks ==98190== indirectly lost: 0 bytes in 0 blocks ==98190== possibly lost: 0 bytes in 0 blocks 


ここで、以前の出力に぀いお少し説明したしょう。

デフォルトでは、Valgrindは$PREFIX/lib/valgrind/default.suppたす。 ただし、ZeroMQで䜿甚する独自のファむルを䜜成する必芁がありたす。次のようになりたす。

 { <socketcall_sendto> Memcheck:Param socketcall.sendto(msg) fun:send ... } { <socketcall_sendto> Memcheck:Param socketcall.send(msg) fun:send ... } 


次に、必芁な匕数を次のように䜿甚しおValgrindを実行できたす。

 valgrind --leak-check=full --show-reachable=yes --suppressions=zeromq.supp server 


おわりに


この章では、゜ケットに぀いお説明し、2぀の新しいモデル、぀たりクラむアントサヌバヌモデルずパむプラむンパタヌンを玹介したした。 たた、これらのテンプレヌトを䜿甚しお特定の問題を解決する方法に぀いお説明し、簡単な䟋を䜿甚しおこれを瀺したした。たた、Valgrindのメモリリヌクを芋぀けるための優れたツヌルも怜蚎したした。

ご枅聎ありがずうございたした。PMの翻蚳に関するコメントをお送りください。

Source: https://habr.com/ru/post/J216957/


All Articles