目次: OpenPilot
最近はOSSの運転支援ソフトウェアOpenPilotのコードを見ています。今日はOpenPilotのプロセス間通信について調べます。
OpenPilotは複数のプロセスが共有メモリを通じてデータを送受信しています。データ送受信のモデルはPublisher/Subscriberモデルを採用していて、1つの通信チャネルについて1つだけPublisherがいてデータ送信役を担い、複数のSubscriberがいてデータ受信役を担います。Client/Serverモデルとは異なりデータはPublisherからSubscriberへの一方通行で、SubscriberからPublisherに送ることはできません。
Publisher/Subscriberの仕組みの名前が見当たらなかったので、関連するコードのディレクトリ(openpilot/cereal)からcerealと呼びます。画像用のPublisher/Subscriberの仕組みだけ特別にVisionIpc(openpilot/msgq/msgq/visionipc)と名前が付いていてcerealと実装が異なりますが、背後にあるデータ送受信の仕組みmsgq(openpilot/msgq)は共通です。
今回の着目点はmsgqですから、cerealとVisionIpcの違いは扱いません。簡単に双方ともmsgqを使う経路がある説明だけに留めます。
まずcerealです。cerealはPython用およびC++用のクラス定義を行っています。Publisher用がPubMaster、Subscriber用がSubMasterです。PythonもC++も同じクラス名で、クラス定義、実装箇所は下記のとおりです。
背後のデータ送受信の仕組みはopenpilot/msgq/msgq/msgq.ccにあります。関数がプレフィクスmsgq_から始まるので見分けやすいです。cerealのPubMaster/SubMasterからmsgqのコードに至る経路は下記のとおりです。
PubMaster::PubMaster() PubSocket::create() MSGQPubSocket::connect() msgq_new_queue() SubMaster::SubMaster() SubSocket::create() MSGQSubSocket::connect() msgq_new_queue()
次にVisionIpcのServer/Clientからmsgqのコードに至る経路は下記のとおりです。コンポーネントによって呼び出し元は違うので、例としてPublisher側はreplayのCameraServer、Subscriber側はuiのCameraWidgetからの経路を挙げます。
CameraServer::startVipcServer() VisionIpcServer::create_buffers_with_sizes() PubSocket::create() MSGQPubSocket::connect() msgq_new_queue() CameraWidget::vipcThread() VisionIpcClient::VisionIpcClient() SubSocket::create() MSGQSubSocket::connect() msgq_new_queue()
経路は多少違うもののcerealもVisionIpcも両方ともmsgqに行き着きます。ただしcerealはmsgq以外の経路(ZMQ)が選択可能で、ZMQを選択した場合は上記と異なる実行経路となります。
ファイルの共有メモリマップをお互いのプロセスから読み書きしてデータ共有します。msgq_new_queue()にて/dev/shmにあるtmpfsファイルシステム(メモリ上にファイルシステムを作る仕組み)上に1つの通信チャネルごとに1つファイルを作成します。1つのファイルをPublisherとSubscriberが共有メモリマッピングするので、お互いに同じファイルのデータが見えます。Publisherがメモリマップ経由でファイルを書き換えたらSubscriber全員に変更が見える仕組みです。
通常、ファイルの共有メモリマップに書き込んだ内容を他のプロセスに可視化するときはmsync()を呼ぶ必要があります。しかしmsgqの実装はtmpfs決め打ちというか、msync()の手間を惜しんだのか、atomicアクセスもしくはメモリバリア__sync_synchronize()だけで済ませています。うーん?これでも動くんだ……?
// openpilot/msgq_repo/msgq/msgq.cc
int msgq_msg_send(msgq_msg_t * msg, msgq_queue_t *q){
//...
//★★q->dataは共有メモリマップへのポインタ★★
char *p = q->data + write_pointer; // add base offset
//...
// Write size tag
//★★atomicアクセス★★
std::atomic<int64_t> *size_p = reinterpret_cast<std::atomic<int64_t>*>(p);
*size_p = msg->size;
// Copy data
//★★memcpy + メモリバリア★★
memcpy(p + sizeof(int64_t), msg->data, msg->size);
__sync_synchronize();
ファイル内のデータは下記のようにヘッダ領域とデータ領域の2領域からなります。各プロセスにはmsgq_queue_t構造体が確保され、ヘッダ領域とデータ領域へのポインタを保持します。
データ領域のサイズはDEFAULT_SEGMENT_SIZE(10MiB)固定です。Subscriber数の最大数はread_pointers[]などの配列長で決まります、現状だとNUM_READERS(15)固定です。なぜ16じゃなくて15なのか?理由が良くわかりません。特に理由は書いていませんでした。
// openpilot/msgq_repo/msgq/msgq.h
#define DEFAULT_SEGMENT_SIZE (10 * 1024 * 1024)
#define NUM_READERS 15
#define ALIGN(n) ((n + (8 - 1)) & -8)
struct msgq_header_t {
uint64_t num_readers;
uint64_t write_pointer;
uint64_t write_uid;
uint64_t read_pointers[NUM_READERS];
uint64_t read_valids[NUM_READERS];
uint64_t read_uids[NUM_READERS];
};
struct msgq_queue_t {
std::atomic<uint64_t> *num_readers;
std::atomic<uint64_t> *write_pointer;
std::atomic<uint64_t> *write_uid;
std::atomic<uint64_t> *read_pointers[NUM_READERS];
std::atomic<uint64_t> *read_valids[NUM_READERS];
std::atomic<uint64_t> *read_uids[NUM_READERS];
char * mmap_p;
char * data;
size_t size;
int reader_id;
uint64_t read_uid_local;
uint64_t write_uid_local;
bool read_conflate;
std::string endpoint;
};
PublisherとSubscriberが1プロセスずついるときはこんな感じです。msgq_header_tの実体は1つで、各プロセスのmsgq_queue_tがmsgq_header_tとデータ領域へのポインタを保持します。
PublisherとSubscriber間のtmpファイル共有
データ領域はPublisherが書き手、Subscriberが読み手のリングバッファとして機能します。つまりPublisherはデータ領域に送信したいデータを書いてwrite_pointerを進め、Subscriberはデータ領域からデータを読み取ってread_pointerを進めます。
目次: ゲーム
前回の振り返り(2022年5月13日の日記参照)から2年半経ちました。所持しているゲームのリストを見るとほとんど変わっていなくて、最近ゲームしてないことが良く表れてます。特に続編系は情熱をもって遊べていないですね。小規模ゲームはいくつかクリアしました。
クリアした(実績90〜100%、シナリオコンプなど)。
たくさん遊んだ(クリア条件がないor困難すぎて挑む気なし)。
未クリア(実績、シナリオクリア率が50%以下)。
ほぼ遊んでいない。
メモ: 技術系の話はFacebookから転記しておくことにした。
目次: 射的
スピードシューティングを始めてから2年半が経ちました。11月には3回目の参加を予定しているJTSA Limited(リミティッド - 一般社団法人日本トイガン射撃協会JTSA)が開催されます。
以前と変わらず、基本的に練習は週1回のみです。たまに練習以外のシューティング系イベントにも行きますが、記録を見る限りタイム上達とは関係なさそうです。2023年の結果はこんな感じでした。
記録は横ばいで80秒台からタイムが早くなる様子がなく、週1回の練習ならこんなもんか?と諦めていました。ところが2024年の夏から70秒台のタイムが出るようになりました。何が起きたのかわかりません。謎です。
今日は初の69秒台のタイムが出ました。とはいえ各ステージのタイムのゆらぎを見る限り、全ステージが偶然早いタイム側に寄っただけの偶然の産物です。実力は75秒くらいでしょうか。
ここ1か月は最近は1発目を早く撃てるように頑張りましたが、その前からなぜか早くなっていたし、何が良くなったのかイマイチわからないです。この後良くなるのか悪くなるのかわからないですね……。
今の企業は公式サイトを持っていなほうが珍しいと思いますが、ドメイン名の使い方は各社でバラバラで面白いです。特にグローバル企業は日本だけでなく世界各国向けに公式サイトを構築する必要があるため、迂闊に「会社名.co.jp」みたいなドメインを使うと海外展開時に困ってしまいます。
解決方法はいくつかパターンがあるみたいです。
一番良く見かけるタイプでオーソドックスな解決方法だと思われます。gTLD(generic Top Level Domain)である.comドメインのあとに地域名や国名を表すパスが続きます。ドメインを増やさずに各国地域対応が可能で、スマートですね。
亜流としてNECのように、.comドメイン&ドメイン先頭(www部分)が地域名になる会社もありました。.comドメインだけどパス名の代わりにドメインの先頭を使うんですね。これはこれでスマートです。
もう一つはccTLD(country code Top Level Domain、つまり汎用JPドメインなど)を使う会社です。一貫性がありますね。
亜流として日本だけ属性JPドメイン(.co.jp)、他国はccTLDドメインを使う会社もあります。日本だけ違うのは過去の互換性のためでしょうか?
珍しいパターンとしては企業名ドメインもありました。このタイプのドメインの名称が良くわからないんですが、企業名がトップレベルドメインになっています。でも他国向けはccTLDでした。謎の作りですね。
日産やホンダは.co.jpを使っていますが、.jpはアドレス解決ができないところから使っていないように見えます。.jpドメインはどうなっているんでしょうか?Whois情報を見ると、
$ whois nissan.jp Domain Information: [ドメイン情報] [Domain Name] NISSAN.JP [登録者名] 日産自動車株式会社 [Registrant] Nissan Motor Co., Ltd. [Name Server] [Signing Key] [登録年月日] 2001/03/26 [有効期限] 2025/03/31 [状態] Active [ロック状態] DomainTransferLocked [ロック状態] AgentChangeLocked [最終更新] 2024/08/06 16:25:46 (JST)
見慣れない"DomainTransferLocked"と"AgentChangeLocked"が設定されています。これはドメイン名移転申請ロック、指定事業者変更ロックのことだそうでJPRSのサイト(指定事業者変更ロック - JPドメイン名のルール - JPドメイン名について - JPRS)に解説があります。
企業と関係のない登録者に勝手に使われないように申請を弾いてくれる仕組みですね。こんな機能があるとは知らなかったです、なるほど便利だ。
目次: ゲーム
書くの忘れてました。放置してたshapezを夏ごろにレベル100まで進めました。レベル27から納品物がランダムになるので「何でも作れる機構」を構築しないとレベル100達成はしんどいです。
実績は41/45まで取りました。取り逃しは、取る気が元々ないスピードラン系3つ+操作ミスで取り逃した「King of Inefficiency」です。
レベル100程度ならさほど納品速度は要求されませんが、高レベルになると作成したShapeを納品する場所(ハブ)に超高速で納品しなければならないです。何でも作れる機構+倉庫に大量のShapeをためて、たまったら一気に流し込む機構が必要です。
レベル100までやってみた感想ですけど、総合的には面白かったです。効率的な機構を考え付いたときが爽快です。個人的な面白さのピークは「何でも作れる機構」を作りレベル30くらいを突破する辺りでした。そのあとはコピペ感が強くなってやや盛り下がるのはシステム上仕方ないですね。
近々shapez2がリリースされる(今はearly access版)みたいですが、さらに複雑になっているようで買うかどうか微妙です。やると疲れるんで……息抜きには向いてないです。
メモ: 技術系の話はFacebookから転記しておくことにした。
< | 2024 | > | ||||
<< | < | 10 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | - | - |
合計:
本日: