現在の制御端末と `/dev/tty` の間にはどのような関係がありますか?

現在の制御端末と `/dev/tty` の間にはどのような関係がありますか?

Lubuntu 18.04 では、lxterminal でシェルを実行します。その制御端末は、現在の疑似端末スレーブです。

$ tty
/dev/pts/2

/dev/pts/2現在の制御端末と との間にどのような関係があるのか​​を知りたいです/dev/tty

  1. /dev/tty現在の制御端末のように動作します/dev/pts/2:

    $ echo hello > /dev/tty
    hello
    
    $ cat < /dev/tty
    world
    world
    ^C
    
  2. しかし、これらは、一方が他方へのシンボリックリンクまたはハードリンクではなく、無関係なファイルであるようです。

    $ ls -lai /dev/tty /dev/pts/2
     5 crw--w---- 1 t    tty 136, 2 May 31 16:38 /dev/pts/2
    13 crw-rw-rw- 1 root tty   5, 0 May 31 16:36 /dev/tty
    

異なる制御端末を持つ異なるセッションの場合、 は /dev/ttyそれらの制御端末であることが保証されます。 シンボリックリンクやハードリンクにせずに、異なる制御端末にするにはどうすればよいでしょうか。

では、それらの関係と違いは何でしょうか? どのような助言でも大歓迎です!

この投稿は以前の投稿から派生したものですコマンド `tty` の出力とファイル `/dev/tty` はどちらも、現在の bash プロセスの制御端末を参照していますか?

答え1

ttyセクション4のmanページ次のように主張している。

ファイル/dev/ttyメジャー番号 5、マイナー番号 0 の文字ファイルで、通常はモード 0666、所有者.グループ root.tty です。プロセスの制御端末 (存在する場合) の同義語です。

に加えてioctl(2)ttyが参照するデバイスがサポートする要求、ioctl(2)リクエストTIOCNOTTYはサポートされています。

TIOCNOTTY

呼び出しプロセスを制御端末から切り離します。

プロセスがセッション リーダーである場合、シグナルSIGHUPSIGCONTフォアグラウンド プロセス グループに送信され、現在のセッション内のすべてのプロセスは制御 tty を失います。

これioctl(2)呼び出しは、接続されたファイル記述子に対してのみ機能します。 /dev/ttyデーモンプロセスは、端末でユーザーによって呼び出されたときに使用されます。プロセスは、/dev/ttyオープンが成功した場合は、 を使用してターミナルからデタッチされます TIOCNOTTYが、オープンが失敗した場合は、明らかにターミナルに接続されていないため、デタッチする必要はありません。

/dev/ttyこれは、 が制御端末へのシンボリックリンクではない理由を部分的に説明します。 は追加の をサポートしioctl、制御端末がない可能性があります(ただし、プロセスは常に にアクセスしようとすることができます)/dev/tty。ただし、ドキュメントは間違っています。追加の は、ioctl経由 /dev/tty(見るmosvyの回答、これは の性質についてもより合理的な説明を与えます/dev/tty

/dev/ttyリンクにならずに、異なる制御端末を表すことができます。これは、それを実装するドライバが、呼び出しプロセスの制御端末 (存在する場合) を決定するためです。

/dev/ttyこれは制御端末であると考えることができます。したがって、制御端末にのみ意味のある機能を提供しますが、/dev/pts/2などは単純な端末であり、そのうちの 1 つが特定のプロセスの制御端末になる場合があります。

答え2

/dev/ttyは、「魔法の」文字デバイスであり、開くと現在の端末へのハンドルを返します。

制御端末が であると仮定すると/dev/pts/1、 を介して開かれたファイル記述子/dev/pts/1と を介して開かれたファイル/dev/tty記述子は同じデバイスを参照します。書き込み、読み取り、またはその他のファイル操作はどちらに対しても同じように動作します。

特に、それらは同じioctlのセットを受け入れ、そしてTIOCNOTTY経由でのみ利用可能な追加のioctlではありません。/dev/tty

ioctl(fd, TIOCNOTTY)呼び出し元のプロセスの制御端末であれば、端末を参照する任意のファイル記述子に対して同じように動作します。

/dev/tty記述子が、/dev/pts/1、を開いて取得されたかどうかは関係ありません/dev/ptmx(その場合、ioctlは対応する奴隷)、または最近では の呼び出しによっても行われますioctl(master, TIOCGPTPEER, flags)

例:

$ cat <<'EOT' >tiocnotty.c
#include <sys/ioctl.h>
#include <unistd.h>
#include <err.h>

int main(int ac, char **av){
        if(ioctl(0, TIOCNOTTY)) err(1, "io(TIOCNOTTY)");
        if(ac < 2) return 0;
        execvp(av[1], av + 1);
        err(1, "execvp %s", av[1]);
}
EOT
$ cc -W -Wall tiocnotty.c -o tiocnotty
$ ./tiocnotty
$ ./tiocnotty </dev/tty
$ tty
/dev/pts/0
$ ./tiocnotty </dev/pts/0

また、これは実際には現在のプロセスを tty から「切り離す」わけではありません。プロセスは引き続きそこから読み取ることができ、^C端末上で を実行するとプロセスが強制終了されるなどです。セッション リーダーではないプロセスに対する唯一の影響は、 経由で tty にアクセスできなくなることと/dev/tty、 で制御 tty としてリストされなくなることです/proc/PID/stat

$ ./tiocnotty cat
^C
$ ./tiocnotty cat
^Z
[2]+  Stopped                 ./tiocnotty cat
$ ./tiocnotty cat
foo
foo
^D
$ ./tiocnotty cat /dev/tty
cat: /dev/tty: No such device or address
$ ./tiocnotty awk '{print$7}' /proc/self/stat
0

[の7番目のフィールドは/proc/<pid>/stat制御ttyのデバイスIDです。 を参照してくださいproc(5)。]

呼び出したプロセスがセッションリーダーである場合、セッションは実際にttyから切り離され、セッションからフォアグラウンドプロセスグループにSIGHUP/SIGCONTペアが送信されます。しかし、それでもターミナルはない閉じられ、プロセスは、次の状態を生き延びれば、引き続きそこから読み取ることができますSIGHUP

$ script /dev/null -c 'trap "" HUP; exec ./tiocnotty cat'
Script started, file is /dev/null
lol
lol
^C^C^C^C^C  # no controlling tty anymore

wtf  
wtf
^D   # but still reading fine from it
Script done, file is /dev/null

/dev/tty/dev/stdinは、procfs のような仮想動的ファイルシステムよりずっと前に (そして一般的なシンボリックリンクよりずっと前に) 発明されたため、 => /dev/fd/0=> /proc/self/fd/0=>のようなシンボリックリンクではありません/dev/pts/0。そして、多くのプログラムがその特定のセマンティクスに依存するようになりました (例:制御端末にアクセスできない場合/dev/ttyに失敗するENODEV)。

関連情報