web-dev-qa-db-ja.com

Shell / initはどのようにstdioストリームを作成しますか?

私はMITの xv6 OS のソースを読んでいます。このスニペットはsh.cの先頭にあります。

// Ensure that three file descriptors are open.
while((fd = open("console", O_RDWR)) >= 0){
    if(fd >= 3){
      close(fd);
      break;
    }
}

これは、新しく割り当てられたファイル記述子が上(または同じ)であるかどうかをチェックすることによって、少なくとも3つのファイル記述子が開いているかどうか(おそらくstdin、stdout、およびstderr)をチェックすることを理解しています3.3。

1)同じプロセスから同じデバイスを複数回openして、異なるファイル記述子を期待することはどのように可能ですか?

2)これを理解するために、ホストマシン(x86_64 Linux 4.6.0.1)で同様のスニペットを実行しました。テストプログラムは、ループ内でテキストファイルを繰り返しopenして、異なるfdを期待できるかどうかを確認しましたが、常に同じファイル記述子を生成しました。このことから、xv6のスニペットが明らかに機能するため(Qemuでテスト済み)、実際のファイルとデバイス(/dev/consoleなど)をopen-することはどういうわけか異なると結論付けました。違いは正確には何ですか?

#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>

int main(void)
{
    int fd;
    int cnt = 0;

    while ((fd = open("sample.txt", O_RDWR) > 0)) {
        if (cnt != 10) {
            cnt++;
            printf("File descriptor opened: %d\n", fd);
        } else {
            break;
        }
    }

    return 0;
}

これを実行したときの出力は次のとおりです。

$ ./a.out
File descriptor opened: 1
File descriptor opened: 1
[snip]
File descriptor opened: 1
File descriptor opened: 1

[〜#〜] edit [〜#〜]回答の1つに基づいて、実行可能ファイルでstraceを実行したところ、 open実際 複数のファイル記述子を返しますが、何らかの理由ですべてが出力されません。なぜそうなるのでしょうか?

3)やや無関係ですが、fds 0-2でstdioストリームを使用するという慣習は、それだけではありません-慣習ですか?たとえば、初期化シーケンスが入力/出力ファイル記述子を他の何かに割り当てた場合、それはその子がI/Oを行う方法に何らかの影響を及ぼしますか?

2

それは実際には3つの質問です。プログラムが正しくないため、#2をすぐに廃棄してください。

    while ((fd = open("sample.txt", O_RDWR) > 0)) {

あなたはおそらく意味しました

    while ((fd = open("sample.txt", O_RDWR)) > 0) {

不適切に配置された括弧を使用すると、fdがゼロより大きいかどうかのみをテストします(ファイル記述子0、1、および2が開いているため、これはおそらく適切な仮定です)。

#1の場合: open 呼び出し(成功した場合)は、個別のファイル記述子を返すように定義されています。デバイスを再度開くことができない場合、open-1を返します。

#3の場合:確かに、これはconventionですが、 POSIXstandard にもあります。他のシステムでは、すべてのプログラムに4番目のオープンストリームがあるなど、他の規則が使用されています。

さらに読む: イージス環境の使用(1988年7月)
6-9ページを参照してください。これは、Apollo Domain/OSにエラーinputおよび** output *があったことを示しています。

1
Thomas Dickey

いいえ、コードはcheck記述子ではなく、実際に記述子を開きます。記述子はまだ与えていません。開くたびに、新しいファイル記述子、つまり0、1、2、3が提供されます。 fd 3に達するとコードが壊れ、0から2が開いたままになります。

各ファイル記述子は、単にあるファイル内のある場所へのポインタです。したがって、同じファイルに複数の記述子があることは問題ありません。

テストプログラムが異なるオープンコールに対して同じfdを与える場合、それにバグがあります。コードを見せてください。

はい、fd 0から2には強い規則があります。あるコードがstdoutに出力したい場合、実際にはfd1に出力します。stdoutを他のものに「マップ」する方法はありません。

1
michas

1)システムが複数のプロセスから同時に同じファイルを開くことをサポートしている場合は、1つのプロセスを複数回開くことを許可しないのはなぜですか?ファイル記述子は継承されるため、とにかく同じプロセスで同じファイルが2回発生する可能性があります。つまり、プロセス自体によって一度だけ継承された場合です。プロセスが開いているファイルを確認し、以前のファイルへの参照を返すことは、余分な作業になります。

また、プロセスの異なる部分が同じファイルを同時に使用するかどうかという問題もあります。ライブラリがメインプログラムがそれを使用するのと同時にいくつかの構成ファイルを使用するとします。ファイル記述子は、アクセスモードとファイルポインタの位置に関連付けられています。それらのコピーが1つしかない場合、奇妙なことが起こります。また、ファイルが2回(同じfdに対して)開かれた場合、ファイルが閉じられるとどうなりますか?ファイルをいつ本当に閉じるかを決定するために、参照カウントの別のレイヤーが存在する可能性がありますが、それは他の問題には役立ちません。


2)コードによって異なります。スマート言語(つまりCではない)は、開いているファイルを保持している変数を再カウントし、ファイルを再度開く直前に変数を閉じる場合があります。コードを見ずに言うのは難しい。

しかし、ちょっとしたテストで、Perlの同じ変数に対して同じファイルを2回開くと、同じFD番号になります。

Perl -e 'open F, "test.txt"; printf "%d ", fileno(F); open F, "test.txt"; printf "%d\n", fileno(F)'
3 3

straceで実行すると、ファイルが再度開く直前に閉じられていることがわかります。

2つの異なる変数を使用して、2つのFD番号を取得します。

Perl -e 'open F, "test.txt"; printf "%d ", fileno(F); open G, "test.txt"; printf "%d\n", fileno(G)'
3 4

3)技術的には、標準のファイル番号は慣例であると言えます。 POSIXおよびISO C標準 で成文化された規則:

プログラムの起動時に、3つのストリームが事前定義されている必要があり、明示的に開く必要はありません。標準入力(従来の入力の読み取り用)、標準出力(従来の出力の書き込み用)、および標準エラー(診断出力の書き込み用)です。

しかし、とにかく、カーネルを気にせずに、それらなしでプログラムを実行できる可能性があるという慣習。またはそうではないかもしれません:exec呼び出しの仕様を読むと、実装があなたのために何かを開くことが 許可されているようです

関数のexecファミリのいずれかが正常に呼び出された後、ファイル記述子0、1、または2が閉じられる場合、実装は新しいプロセスイメージ内のファイル記述子の未指定のファイルを開く可能性があります。

(もちろん、プログラム自体でそれらを閉じることができます。)

起動時にそれらを持たないように手配すると、互換性はドアの外に投げ出されます。

標準ユーティリティまたは適合アプリケーションが、ファイル記述子0が読み取り用に開かれていない、またはファイル記述子1または2が書き込み用に開かれていない状態で実行された場合、ユーティリティまたはアプリケーションが実行される環境は不適合と見なされ、その結果、ユーティリティまたはアプリケーションは、この標準で説明されているように動作しない場合があります。

Linuxのマニュアルページはそれをいくらか実用的にしています

一般原則として、特権の有無にかかわらず、これら3つのファイル記述子がexecve()全体で閉じられたままであると想定できるポータブルプログラムはありません。

1
ilkkachu