talk(2)

Table of Contents

gdb on FreeBSD

FreeBSDインストール時に、base-dbgパッケージを入れておくと、いきなりユー ザーランドのコマンドをgdb出来るようになる。debug情報が付属したバイナリー が、/usr/lib/debugの下に導入されている事は知ってたけど、それをgdbがど の様に利用してるかは、追跡してなかった。

予想では、gdbがラッパーになってて、起動すると、 PATH=/usr/lib/debug:$PATHしてから、本物のgdbが動作するんだろうと思って いた。が、調べてみると、そんな小手先な技は使われていなかった。しょうが ないので、Makefileを確認。

CONFIGURE_ARGS= --program-suffix=${VER} \
                --disable-sim \
                --enable-targets=all --enable-64-bit-bfd \
                --with-separate-debug-dir=/usr/lib/debug \
                --with-additional-debug-dirs=${LOCALBASE}/lib/debug \
                ${ICONV_CONFIGURE_ARG} \
                --without-libunwind-ia64

こんな、こっそりな機能をサポートしてるって、どんな我侭なユーザーだい。 いや、貪欲にユーザーの要求に対応する、gdbチームに感謝です。

talk with gdb

で、そのgdbを早速利用して、前回からの題材であるtalkの動きを追跡します。 mainからダラダラしてくのもいいけど、UDPのパケット送受って事で、それを 一手に引受ている手続にBPを置いてみます。

UDPなパケットの送受はどんな関数が利用されるか? そんなの、 Rubyで楽しむソケットプログラミング入門 とかを見れば案内が有るよ、ってんで、sendto/recv が使われている関数をチョ イスしたんだ(まあ、他の流儀もあるけど)。

sakae@fb:~ $ gdb -q talk
Reading symbols from talk...
Reading symbols from /usr/lib/debug//usr/bin/talk.debug...
(gdb) b ctl_transact
Breakpoint 1 at 0x40376b: file /usr/src/usr.bin/talk/ctl_transact.c, line 58.
(gdb) r joy > /dev/null

gdb内でPATH相当のハンドリングを行なってくれるおかげで、emacsからも普通 にgdb出来る。その場合、cursesの起動で、画面が割れてしまって不便なので、 画面は/dev/nullへ捨ててしまうのが良い(副作用として、セッションが出来な くなるけど)。

最初のブレーク

(gdb) bt
#0  ctl_transact (target=..., lmsg=..., type=1, rp=0xffbfeb88)
    at /usr/src/usr.bin/talk/ctl_transact.c:58
#1  0x00404ea2 in look_for_invite (rp=0xffbfeb88)
    at /usr/src/usr.bin/talk/look_up.c:104
#2  check_local () at /usr/src/usr.bin/talk/look_up.c:63
#3  0x0040515f in main (argc=2, argv=0xffbfec14)
    at /usr/src/usr.bin/talk/talk.c:75

次のブレーク

(gdb) bt
#0  ctl_transact (target=..., lmsg=..., type=3, rp=0xffbfeb80)
    at /usr/src/usr.bin/talk/ctl_transact.c:58
#1  0x004046c9 in announce_invite () at /usr/src/usr.bin/talk/invite.c:154
#2  invite_remote () at /usr/src/usr.bin/talk/invite.c:82
#3  0x00405168 in main (argc=2, argv=0xffbfec14)
    at /usr/src/usr.bin/talk/talk.c:76

joy screenにメッセージが届くようになる。

(gdb) bt
#0  ctl_transact (target=..., lmsg=..., type=2, rp=0xffbfeb80)
    at /usr/src/usr.bin/talk/ctl_transact.c:58
#1  0x0040481c in invite_remote () at /usr/src/usr.bin/talk/invite.c:108
#2  0x00405168 in main (argc=2, argv=0xffbfec14)
    at /usr/src/usr.bin/talk/talk.c:76

更に歩を進めると、、

(gdb) bt
#0  ctl_transact (target=..., lmsg=..., type=2, rp=0xffbfeb80)
    at /usr/src/usr.bin/talk/ctl_transact.c:58
#1  0x00404855 in invite_remote () at /usr/src/usr.bin/talk/invite.c:110
#2  0x00405168 in main (argc=2, argv=0xffbfec14)
    at /usr/src/usr.bin/talk/talk.c:76

see header file

いきなりgdbって、プチ悪い癖だな。ヘッダー・ファイルを見るのもソースを 読み解く良い切り口と思うぞ。

ob$ wc *.h
      92     421    2985 talk.h
      42     310    2027 talk_ctl.h

全体で134行有るけど、実質は、クレジットを除けば半分ぐらいなものだ。 今回はtalk.hの中でincludeしてる、面白そうなのに目が行った。 <protocols/talkd.h> なんて、秘密が隠れていそうでワクワクですよ。

解説のコメントが掲載されてたので、 DeepL.com(無料版)で翻訳しました(原文ママ)。最初は、行頭のコメントの 一部 '*' だけを除いたテキスト。

トークサーバーは、招待の保管場所として機能し、以下の目的で待ち合わせを希望するクライアントのリクエストに応じる。 クライアントからのリクエストに応答する。 に応答する。通常の動作では、クライアント(発呼側)は次のようにする、 のCTLMSGをサーバーに送ることでランデブーを開始する。 を送信する。これにより、サーバは招待テーブルを検索する。 テーブルを検索して、発呼側の招待(指定された着呼側と話すための招待)が 現在存在するかどうかを確認する。 (メッセージで指定された着呼側と話すための)招待が存在するかどうかを調べる。検索に失敗した場合 ルックアップに失敗した場合、発呼側は次にANNOUNCEメッセージを送り、 サーバーは着呼側(callee)にアナウンスをブロードキャストする。 を送信し、着呼側のログインポートに、コンタクトを要求するアナウンスを ブロードキャストさせる。 コンタクトを要求する。着呼側からの応答があった場合、ローカルサーバーは、記録された招待状 を使用して、適切なレンダリングで応答する。 を使用して、適切なランデブーアドレスを応答する。 アドレスで応答し、発呼側と着呼側のクライアントプログラムは、会話 が行われるストリーム接続を確立する。 ストリーム接続を確立する。


こちらは、文の終了を示す '.' で改行。それ以外はスペースで行を連結し、世 間一般の英文形式に整形したもの。

トークサーバーは招待のリポジトリとして動作し、会話を行うためにランデブー したいクライアントのリクエストに応答する。

通常の動作では、クライアント(発呼側)はLOOKUP型のCTLMSGをサーバーに送ることで、ランデブー を開始する。

これにより、サーバーは招待テーブルを検索し、発呼側(メッセージで指定された着呼側と会話する)の招待が現在存在するかどうかを確認する。

ルックアップに失敗した場合、発呼側は次にANNOUNCEメッセージを送り、 サーバーに、着呼側のログインポートにコンタクトを要求するアナウンスを ブロードキャストさせる。

着呼側が応答すると、ローカルサーバーは記録された招待を使用して、適切なランデブーアドレスで応答し、発呼側と着呼側のクライアントプログラムは、会話が行われるストリーム接続を確立する。


今度、英文形式に整形するスクリプトを作らないとな。

struct osockaddr {
        unsigned short  sa_family;      /* address family */
        char            sa_data[14];    /* up to 14 bytes of direct address */
};

/*
 * Client->server request message format.
 */
typedef struct {
        unsigned char     vers;                 /* protocol version */
        unsigned char     type;                 /* request type, see below */
        unsigned char     answer;               /* not used */
        unsigned char     pad;
        u_int32_t         id_num;               /* message id */
        struct    osockaddr addr;               /* old (4.3) style */
        struct    osockaddr ctl_addr;           /* old (4.3) style */
        int32_t           pid;                  /* caller's process id */
#define NAME_SIZE       12
        char              l_name[NAME_SIZE];    /* caller's name */
        char              r_name[NAME_SIZE];    /* callee's name */
#define TTY_SIZE        16
        char              r_tty[TTY_SIZE];      /* callee's tty name */
} CTL_MSG;

これが要求フォーマット。これに対する CTL_RESPONSE な構造体も当然ながら、 定義されてる。

check paket

この構造体のデータがパケットになって、飛んで行くんだな。

下記は、wiresharkで、Payloadを中心に、表示させてみたもの。

Ethernet II, Src: ...... Dst: .......
Internet Protocol Version 4, Src: fb, Dst: ob.my.domain
User Datagram Protocol, Src Port: 49395, Dst Port: 518
Data (74 bytes)

0000  01 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00   ................
0010  00 00 00 00 00 00 00 00 00 02 c0 f3 c0 a8 01 85   ................
0020  00 00 00 00 00 00 00 00 00 00 04 26 6a 6f 79 00   ...........&joy.
0030  00 00 00 00 00 00 00 00 73 61 6b 61 65 00 00 00   ........sakae...
0040  00 00 00 00 00 00 00 00 00 00                     ..........

Data (74 bytes)

0000  01 03 00 00 ff ff ff ff 00 02 f8 51 c0 a8 01 85   ...........Q....
0010  00 00 00 00 00 00 00 00 00 02 c0 f3 c0 a8 01 85   ................
0020  00 00 00 00 00 00 00 00 00 00 04 26 6a 6f 79 00   ...........&joy.
0030  00 00 00 00 00 00 00 00 73 61 6b 61 65 00 00 00   ........sakae...
0040  00 00 00 00 00 00 00 00 00 00                     ..........

Data (74 bytes)

0000  01 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00   ................
0010  00 00 00 00 00 00 00 00 00 02 65 98 c0 a8 01 63   ..........e....c
0020  00 00 00 00 00 00 00 00 00 00 c7 bc 73 61 6b 61   ............saka
0030  65 00 00 00 00 00 00 00 6a 6f 79 00 00 00 00 00   e.......joy.....
0040  00 00 00 00 00 00 00 00 00 00                     ..........

auto show by gdb

送出されたパケットと構造体との対応を取って表示させれば、意味がある情報 を入手できるだろうけど、ちと面倒。ならば、gdbを利用して、構造体をプリ ントしちゃえって発想が出てくる。過去にやったgdbスクリプトの出番。 zg.scr

set print pretty
break ctl_transact
commands
  silent
  bt
  print/x msg
  echo \n
  cont
end
run joy > /dev/null
quit

breakした時に実行したいコマンド行を、commands ~ end中に記述。だから、 break,set,commandsみたいに記述しちゃうと、silentの所でエラーになってし まう。注意せよ。それから、silentはBPにヒットしましたってメッセージを抑 止する機能が有る。

ob$ gdb -q talk -x zg.scr >LOG
talk: Connection closed.  Exiting

joy側の端末で、応答してから終了させる。下記は、最後に送出されたパケッ ト。

#0  ctl_transact (target=..., msg=..., type=2, rp=0xcf7bf710)
    at ctl_transact.c:55
#1  0x18ba3253 in invite_remote () at invite.c:116
#2  0x18ba402f in main (argc=2, argv=0xcf7bf794) at talk.c:66
$5 = {
  vers = 0x1,
  type = 0x0,
  answer = 0x0,
  pad = 0x0,
  id_num = 0x1000000,
  addr = {
    sa_family = 0x200,
    sa_data = {0x87, 0x17, 0xc0, 0xa8, 0x1, 0x63, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0}
  },
  ctl_addr = {
    sa_family = 0x200,
    sa_data = {0xb7, 0xce, 0xc0, 0xa8, 0x1, 0x63, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0}
  },
  pid = 0x52680100,
  l_name = {0x73, 0x61, 0x6b, 0x61, 0x65, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, # sakae
  r_name = {0x6a, 0x6f, 0x79, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, # joy
  r_tty = {0x0 <repeats 16 times>}
}

[Inferior 1 (process 92242) exited normally]

$5 = { .. } となってるんで、5番目にヒットしたって事になるな。

あれ、 ctl_transact の入口では、送出されるパケットになっていないな。 モニターするなら、sendtoじゃなければならないか。

やってみたらエラーになった。手作業でBPしてみたら、こんな反応。

Breakpoint 1, __i386_read_tcb (offset=0) at /usr/include/machine/tcb.h:41
41              __asm__ ("movl %%gs:(%1),%0" : "=r" (val) : "r" (offset));
(gdb) bt
#0  __i386_read_tcb (offset=0) at /usr/include/machine/tcb.h:41
#1  _libc_sendto_cancel (fd=3, msg=0xcf7daa08, len=84, flags=0,
    to=0x36545254 <daemon_addr>, tolen=16)
    at /usr/src/lib/libc/sys/w_sendto.c:27
#2  0x165472b7 in ctl_transact (target=..., msg=..., type=1, rp=0xcf7dab00)
    at ctl_transact.c:68
#3  0x16549d69 in look_for_invite (rp=0xcf7dab00) at look_up.c:97
#4  0x16549b43 in check_local () at look_up.c:58
#5  0x1654a01e in main (argc=2, argv=0xcf7dab84) at talk.c:65

sendto(2)だからgdbは、それをハンドリング出来ないのか。ってか、いきなり アセンブラが出てくる奴は、スクリプトの対象外なのかな。

丁寧に

sendtoに注目すると、下記の様に、 ctl_transct 以外にも出現してる。

ob$ grep sendto *.c
ctl_transact.c:                 cc = sendto(ctl_sockt, (char *)&msg, sizeof (msg), 0,
invite.c:       if (sendto(ctl_sockt, &msg, sizeof (msg), 0,
invite.c:       if (sendto(ctl_sockt, &msg, sizeof (msg), 0,

何処で利用されているか? 答は、終了時に、登録を抹消するパケットを投げ ていた。

sakae@fb:/tmp $ grep '#0' LOG
#0  ctl_transact (target=..., lmsg=..., type=1, rp=0xffbfec78)
#0  ctl_transact (target=..., lmsg=..., type=3, rp=0xffbfec70)
#0  ctl_transact (target=..., lmsg=..., type=0, rp=0xffbfec70)
#0  ctl_transact (target=..., lmsg=..., type=2, rp=0xffbfec70)
#0  ctl_transact (target=..., lmsg=..., type=2, rp=0xffbfec70)

typeの所が、サーバーへの要求コードになる。

/* message type values */
#define LEAVE_INVITE    0       /* leave invitation with server */
#define LOOK_UP         1       /* check for invitation by callee */
#define DELETE          2       /* delete invitation by caller */
#define ANNOUNCE        3       /* announce invitation by caller */

for deepL

素直に、*と改行をスペースに置き換え。続いて、'.' を '\n' に複数回変更。但 し、'\n' は、そのまま受付ないので、バックスリャッシュしてから、本物の 改行をしといて、/g する。

#! /bin/sh
cat -          |
tr '*\n' '  '  |
sed 's#\.#.\
\
#g'

上記を deepl.sh としてコマンド登録したとすると、

sakae@atom:~$ deepl.sh
 * The standard type used for color values, and for color-pairs.  The latter
 * allows the curses library to enumerate the combinations of foreground and
 * background colors used by an application, and is normally the product of the
 * total foreground and background colors.
 *
 * X/Open uses "short" for both of these types, ultimately because they are
 * numbers from the SVr4 terminal database, which uses 16-bit signed values.
### ここで Ctl-d する。
   The standard type used for color values, and for color-pairs.

  The latter       allows the curses library to enumerate the combinations of f
  reground and       background colors used by an application, and is normally
  he product of the    total foreground and background colors.


  X/Open uses "short" for both of these types, ultimately because they are
  numbers from the SVr4 terminal database, which uses 16-bit signed values.

起動したらコメントを貼り付ける。入力終了を告げる為にCtl-dを入力。Ctd-d 以下が、(余計なスペースや改行がはいっているけど)正しい英文である。これ をdeepl.comに貼り付ければよい。これ、Debina/WSL2の端末だけど、気分は、 X Windowsでxtermを利用してるみたいなGUI感覚だな。

なお、例文は、curses.hの一節である。BSD系は行頭が、 ' *' となってるけ ど、GNU系は、コメントの最初と最後を '/* ... */' してて、中間行には、 ' *' を入れない流儀なのね。風体で、どちらか判別出来るって、刑事の眼差し だなあ!

ChatGPT

gdbやwiresharkって、一種のセンサーや測定器だ。測定器について、疑問が出 てきたので聞いてみた。松尾先生によれば、Let's think step by stepが良い 回答を引き出すマジック・ワードらしい。それから、質問者に忖度する所が多 分に有るので、回答者が対立する様に仕向けると、辛辣な意見を述べる様にな るそうだ。

Q: 温度測定に関する質問です。常温付近の温度だと、水銀温度計とかアルコー ル温度計で可能です。極低温(例えば、液体窒素とか液体ヘリウム)を測定する 場合、どんな温度計(センサー)が使われのでしょうか? 3000文字で説明して ください。

A: 極低温を測定する際には、一般的には特殊な温度計やセンサーが使用されます。ここでは、液体窒素や液体ヘリウムなどの極低温を測定するために使用される主な温度計やセンサーについて説明します。

  1. 発熱抵抗体温度計(RTD):
    • 極低温では、発熱抵抗体温度計(RTD)が一般的に使用されます。RTDは、抵抗が温度に応じて変化する特殊なセンサーです。RTDは通常、プラチナ(プラチナ抵抗温度計、またはPt100などと呼ばれることがあります)で構成されています。極低温下でのプラチナの特性は非常に安定しており、高い精度と信頼性を提供します。
  2. サーモカップル:
    • もう一つの一般的な極低温測定センサーはサーモカップルです。サーモカップルは、異なる金属の接合部分に生じる熱電起電力を測定して温度を計測します。サーモカップルは一般に広い温度範囲で使用されますが、極低温においても信頼性があります。
  3. 薄膜温度計:
    • 液体窒素や液体ヘリウムのような極低温を測定するための別の選択肢として、薄膜温度計があります。薄膜温度計は、薄い金属膜の抵抗が温度変化に応じて変化する特性を利用して温度を測定します。これらの温度計は、極低温での高い感度と応答性を提供することができます。
  4. 超伝導量子干渉素子(SQUID):
    • 極低温下での測定においては、超伝導量子干渉素子(SQUID)も利用されることがあります。SQUIDは超伝導体の微小な変化を検出することで、極低温下での高感度温度測定を可能にします。これらは非常に精密な測定に使用され、特に超伝導体の性質を研究する際に役立ちます。
  5. ダイアモンド温度計:
    • 極低温の測定においては、ダイアモンド温度計も注目される技術です。これは、ダイヤモンドの特性を利用して温度を測定するもので、極低温下でも高い精度と安定性を提供します。

これらの温度計やセンサーは、極低温下での測定において高い精度と信頼性を 提供します。それぞれのアプリケーションや測定要件に応じて、最適な温度計 やセンサーを選択することが重要です。

Q: ジャンクション接続の順方向電圧の温度依存性を利用した、極低温領域の 温度センサーは実用になりますか?

A: ジャンクション接続の順方向電圧を利用した温度センサーは、一般的に温度依存性が高いため、極低温領域での測定には適しています。このタイプのセンサーは、温度が低いほど順方向電圧が増加するという特性を利用して温度を測定します。

一般的に、ジャンクション接続の順方向電圧は、温度が上昇するにつれて減少する傾向がありますが、これは極低温領域では逆の特性を示すことがあります。具体的には、極低温では半導体のバンドギャップが広がり、これにより順方向電圧が増加します。そのため、極低温領域ではジャンクション接続の順方向電圧の温度依存性が有用になります。

ただし、実用性に関してはいくつかの考慮事項があります。

  1. 精度と線形性: 温度センサーの精度と線形性は、極低温領域で特に重要です。ジャンクション接続の順方向電圧を測定する場合、その温度依存性が十分に精度があり、また線形であることが必要です。
  2. キャリブレーション: 極低温領域では、測定装置やセンサーのキャリブレーションがより困難になる場合があります。ジャンクション接続の順方向電圧を測定するセンサーも、適切なキャリブレーションが必要です。
  3. 材料特性の影響: センサーの材料特性は温度測定に影響を与える可能性があります。極低温領域では、半導体や接合部の物理的特性が大きく変化するため、これらの影響を正確に評価する必要があります。
  4. 環境への耐性: 極低温環境では、センサーが適切に動作するための十分な環境への耐性が必要です。特に液体窒素や液体ヘリウムなどの冷却材を使用する場合、センサーはその環境に対して耐性を持つ必要があります。

以上の要因を考慮すると、ジャンクション接続の順方向電圧を利用した極低温領域の温度センサーは、十分な検討と開発が行われれば実用的である可能性があります。ただし、特定のアプリケーションに適したセンサーを選択し、十分な評価とキャリブレーションを行うことが重要です。


This year's Index

Home