2つのシリアルポート(ttyS0とttyUSB0)を持つ2台のコンピュータがあります。 (実際には同じコンピュータにありますが、これはテスト用です)。このポートはヌルモデムケーブルで接続されています。私は単にバイトを一方の端に送信し、もう一方の端に送信するか、その逆に送信できるようにしたいと思います。次はなぜ機能しないのですか? :
# set both serial ports to 9600 8n1
# `-parenb` means no parity,
# `-cstopb` means 1 stop bit
# cs8 means 8 bits at a time
stty -F /dev/ttyUSB0 cs8 -cstopb -parenb 9600
stty -F /dev/ttyS0 cs8 -cstopb -parenb 9600
# in one terminal:
echo "asdf" > /dev/ttyUSB0
# in another terminal, this hangs and does nothing
cat < /dev/ttyS0
andpipe netcat
(下)と同様の作業を簡単に行うことができるので、上記のような作業も可能になりそうです。
mkfifo mypipe
# in one terminal
cat < mypipe
# in another. works as expected
echo "asdf" > mypipe
答え1
次はなぜ機能しないのですか?
# in one terminal:
echo "asdf" > /dev/ttyUSB0
# in another terminal, this hangs and does nothing
cat < /dev/ttyS0
通常、シリアルポートはデータをバッファリングしないためです。クライアントアプリケーションがシリアルポートに到着するバイトを受信しない場合、そのバイトは単に破棄されます。
実験的に、受信コンピュータで別のシリアル端末プログラムを起動し、転送コンピュータでminicom
コマンドを再実行してみます。転送速度とフレームが一定であると仮定すると、ターゲットに「asdf」が表示されることがわかります。cu
echo
答え2
私は単にバイトを一方の端に送信し、もう一方の端に送信するか、その逆に送信できるようにしたいと思います。次はなぜ機能しないのですか?
順序を変えればいいからだそうです。リスナーの開始最初そしてそれからデータの送信(パイプの例で行ったように - 聞き始めます。最初):
# Absolutely first, configure each port using `stty`, as you already
# do:
# set both serial ports to 9600 8n1
# `-parenb` means no parity,
# `-cstopb` means 1 stop bit
# cs8 means 8 bits at a time
stty -F /dev/ttyUSB0 cs8 -cstopb -parenb 9600
stty -F /dev/ttyS0 cs8 -cstopb -parenb 9600
# THEN, do in this order
# first, in the receiving terminal, start listening
cat < /dev/ttyS0
# then, in a separate terminal for sending, send the data
echo "asdf" > /dev/ttyUSB0
私は私の答えが次のようになると確信しています。プログラム正解、意味:盲目的には効果があります。
しかし、問題はまだ残っています。なぜ?なぜ最初に聞き始めるのですか? Linuxカーネルバッファリングはあなたのためではありませんか?それでは、データを読み取るためにバッファに入れてはいけませんか?確かにそうです。いいえ、そうでなければ最初に書いてから読んでも大丈夫です。しかし、それは本当ではありません。
私はここで推測していますが、ここで答えは、ドライバがデータを受信したときに最初に受信ダミーファイルが開いていて/dev/ttyUSB0
他のプロセスによって開かれていることを確認することです。もしいいえ、データを受け取る人がいないため、ドライバはデータを破棄します。もしファイルはいopen - ファイルを開いたプロセスがバッファリングされたデータを読み取ることを許可します。
シリアルポートを介した転送とIPC(プロセス間通信)パイプを介した転送の主な違い
これはmakeパイプラインを使用するパイプラインの例とは異なりますmkfifo
。先ほど確認しました。パイプラインの例では、まず端末でリスニングを開始できます。またはまず、他の端末のパイプに送ります。それは問題ではありません。どちらの場合でも動作します。
最初に書き込む場合は、書き込みがブロックされ、プロセスがパイプからデータを読み取るのを待ちます。最初に読み取ると、読み取りがブロックされ、プロセスがパイプに書き込まれるのを待ちます。これは、bashの基盤がほぼ確実なmkfifo()
Linux C関数のドキュメントで確認されています。mkfifo
バラよりman 3 mkfifo
(強調する):
このようにして FIFO 特殊ファイルが作成されると、すべてのプロセスで通常のファイルのように読み書きするためにそのファイルを開くことができます。 ただし、両端が開いている必要があります。 同時に入力または出力を進める前に それに取り組んでください。 読み取り用にFIFOを開く通常ブロック 他のプロセスが書き込みのために同じFIFOを開くまで、またはその逆も同様です。
ただし、これはシリアルポートの動作とは異なります。おそらく、各端のシリアルポートを次のように表示する必要があるからです。その他マシンはFIFOパイプとは異なり、読み書きの終わりを制御する必要があります。同じマシンで。後者の場合、カーネルは、あるプロセスが読み取りを試みている間に別のプロセスが書き込みを試みた時点を簡単に追跡できます。これは、パイプの両端を制御して2つのプロセスのうちの1つをブロックできるためです。
ただし、シリアルポートの場合、カーネルは他のデバイスがリッスンしているのか送信したいのかを知ることができないため、転送をブロックしません。明確に言うと、パイプに書き込むときにリスナーが表示されるまで書き込みがブロックされます。ただし、シリアルポートに書き込むと、コンピュータはリスナーが存在することを確認できません。だから、絶対にブロックしないでください!ただ送ります。受信機がないとブロックは発生しませんが、誰も聞いていない間、シリアルを介して送信されたデータは失われます。繰り返しますが、これはパイプと同じではありません。私の実験と上記のドキュメントは、プロセスがパイプを介してデータを転送しようとしていることを確認します。データを読み取ることができるリスナーが存在するまでブロックされます。
参照と「参照」:
- この記事を書いて、私は私のさまざまな発見を積極的に研究して記録しています。eRCAGuy_dotファイル興味がある場合は、ここでもう一度購入してください。シリアル端末_README.md。
- 私のデモコード:eRCaGuy_hello_world/bash/ipc_pipe_fifo.sh
man 3 mkfifo
:https://man7.org/linux/man-pages/man3/mkfifo.3.html