exec()
関数とそのファミリーは何ですか? なぜこの関数が使用され、どのように機能するのですか?
誰かこれらの機能を説明してください。
簡単に言うと、UNIX では、プロセスとプログラムの概念があります。プロセスは、プログラムが実行される環境です。
UNIX の「実行モデル」の背後にある単純な考え方は、実行できる操作が 2 つあります。
1 つ目は tofork()
で、現在のプログラムの (ほとんど) 複製 (状態を含む) を含むまったく新しいプロセスを作成します。2 つのプロセスには、どちらが親でどちらが子であるかを判断できるいくつかの違いがあります。
2 つ目はexec()
、現在のプロセスのプログラムを新しいプログラムに置き換える to です。
これら 2 つの単純な操作から、UNIX 実行モデル全体を構築できます。
上記にさらに詳細を追加するには:
fork()
andの使用は、exec()
新しいプロセスを開始するための非常に簡単な方法を提供するという点で、UNIX の精神を実証しています。
このfork()
呼び出しにより、現在のプロセスのほぼ複製が作成され、ほぼすべての点で同一になります (一部の実装ではリソース制限など、すべてがコピーされるわけではありませんが、可能な限り近いコピーを作成することが目的です)。プロセス呼び出しは fork()
1 つだけですが、その呼び出しから2 つのプロセスが返されます。奇妙に聞こえますが、非常にエレガントです。
新しいプロセス (子と呼ばれる) は別のプロセス ID (PID) を取得し、古いプロセス (親) の PID を親 PID (PPID) として持ちます。
2 つのプロセスは現在まったく同じコードを実行しているため、どちらがどちらであるかを判別できる必要があります - の戻りコードがfork()
この情報を提供します - 子は 0 を取得し、親は子の PID を取得します (fork()
失敗した場合は、いいえ子が作成され、親はエラー コードを受け取ります)。
そうすれば、親は子の PID を認識し、子と通信したり、強制終了したり、待機したりできます (子は を呼び出すことで、親プロセスをいつでも見つけることができますgetppid()
)。
このexec()
呼び出しは、プロセスの現在の内容全体を新しいプログラムに置き換えます。プログラムを現在のプロセス空間にロードし、エントリ ポイントから実行します。
そのためfork()
、exec()
多くの場合、現在のプロセスの子として実行される新しいプログラムを取得するために順番に使用されます。シェルは通常、次のようなプログラムを実行しようとするたびにこれを行いますfind
-シェルがフォークし、子がfind
プログラムをメモリにロードし、すべてのコマンドライン引数、標準 I/O などを設定します。
ただし、それらを一緒に使用する必要はありません。fork()
たとえば、プログラムに親コードと子コードの両方が含まれている場合、プログラムがフォローなしで呼び出すことはまったくexec()
問題ありません (実行には注意が必要です。各実装には制限がある場合があります)。
これは、単に TCP ポートをリッスンし、親がリッスンに戻っている間に特定の要求を処理するために自身のコピーをフォークするデーモンに対して、かなり頻繁に使用されていました (今でも使用されています)。この場合、プログラムには親コードと子コードの両方が含まれます。
同様に、終了したことを知っていて、別のプログラムを実行したいだけのプログラムは、必要はありfork()
ませexec()
んwait()/waitpid()
。を使用して、子を現在のプロセス空間に直接ロードするだけexec()
です。
一部の UNIX 実装には、fork()
copy-on-write と呼ばれるものを使用する最適化があります。fork()
これは、プログラムがその空間で何かを変更しようとするまで、プロセス空間のコピーを遅らせるトリックです。これは、プロセス空間全体をコピーする必要がないという点でfork()
のみ使用し、使用しないプログラムに役立ちます。exec()
Linux ではfork()
、ページ テーブルと新しいタスク構造のコピーのみを作成しexec()
、2 つのプロセスのメモリを「分離」するという単調な作業を行います。
exec
が次のように呼び出されるとfork
(これがほとんどの場合に発生します)、プロセス空間への書き込みが発生し、変更が許可される前に子プロセスにコピーされます。
Linux には、2 つのプロセス間でほぼすべてvfork()
を共有する、さらに最適化された もあります。そのため、子ができることには一定の制限があり、子がまたはを呼び出すまで、親は停止します。exec()
_exit()
2 つのプロセスが同じスタックを共有しているため、親を停止する必要があります (そして、子は現在の関数から戻ることを許可されません)。これは、fork()
直後にexec()
.
exec
一連の呼び出し ( execl
、execle
などexecve
) が存在することに注意してください。ただしexec
、ここでは文脈上、それらのいずれかを意味します。
次の図は、シェルを使用してコマンドでディレクトリを一覧表示する典型的なfork/exec
操作を示しています。bash
ls
+--------+
| pid=7 |
| ppid=4 |
| bash |
+--------+
|
| calls fork
V
+--------+ +--------+
| pid=7 | forks | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash | | bash |
+--------+ +--------+
| |
| waits for pid 22 | calls exec to run ls
| V
| +--------+
| | pid=22 |
| | ppid=7 |
| | ls |
V +--------+
+--------+ |
| pid=7 | | exits
| ppid=4 | <---------------+
| bash |
+--------+
|
| continues
V
関数ファミリーは、exec
プロセスが実行していた古いプログラムを置き換えて、プロセスに別のプログラムを実行させます。つまり、あなたが呼び出す場合
execl("/bin/ls", "ls", NULL);
次に、ls
プロセスID、現在の作業ディレクトリ、および呼び出したプロセスのユーザー/グループ(アクセス権)を使用してプログラムが実行されますexecl
。その後、元のプログラムはもう実行されていません。
新しいプロセスを開始するには、fork
システム コールが使用されます。元のプログラムを置き換えずにプログラムを実行するにはfork
、 、次にexec
.
exec 関数とそのファミリとは何ですか。
exec
関数ファミリは、、、、、などのファイルを実行するために使用されるすべての関数です。これらはすべてフロントエンドでありexecl
、execlp
それを呼び出すさまざまな方法を提供します。execle
execv
execvp
execve
なぜこの機能が使われるのか
ファイル(プログラム)を実行(起動)したいときはexec関数を使います。
そしてそれはどのように機能しますか。
それらは、現在のプロセス イメージを起動したイメージで上書きすることによって機能します。それらは、現在実行中のプロセス (exec コマンドを呼び出したプロセス) を、起動された新しいプロセスに (終了することによって) 置き換えます。
詳細については、このリンクを参照してください。
exec
は と一緒fork
に使われることが多いのですが、こちらも聞かれましたので、その点を踏まえてお話しします。
exec
現在のプロセスを別のプログラムに変えます。ドクター・フーを見たことがあるなら、これは彼が再生するときのようなものです-彼の古い体は新しい体に置き換えられます.
これがプログラムで発生する方法は、OS カーネルがプログラム引数 (最初の引数) としてexec
渡すファイルが現在のユーザー (プロセスのユーザー ID) によって実行可能かどうかを確認するためにチェックする多くのリソースです。exec
そのexec
場合、現在のプロセスの仮想メモリ マッピングを新しいプロセスの仮想メモリに置き換え、呼び出しで渡されたデータをこの新しい仮想メモリ マップの領域にコピーargv
します。ここで他にもいくつかのことが起こる可能性がありますが、呼び出されたプログラム用に開いていたファイルは新しいプログラム用に開いたままになり、同じプロセス ID を共有しますが、呼び出したプログラムは停止します (exec が失敗しない限り)。envp
exec
exec
exec
これがこのように行われる理由は、新しいプログラムの実行 をこのように 2 つのステップに分けることで、2 つのステップの間でいくつかのことを実行できるからです。最も一般的なのは、新しいプログラムで特定のファイルが特定のファイル記述子として開かれていることを確認することです。(ここで、ファイル記述子は と同じではなく、カーネルが認識している値であることを思い出してください)。これを行うと、次のことができます。 FILE *
int
int X = open("./output_file.txt", O_WRONLY);
pid_t fk = fork();
if (!fk) { /* in child */
dup2(X, 1); /* fd 1 is standard output,
so this makes standard out refer to the same file as X */
close(X);
/* I'm using execl here rather than exec because
it's easier to type the arguments. */
execl("/bin/echo", "/bin/echo", "hello world");
_exit(127); /* should not get here */
} else if (fk == -1) {
/* An error happened and you should do something about it. */
perror("fork"); /* print an error message */
}
close(X); /* The parent doesn't need this anymore */
これにより、次の実行が達成されます。
/bin/echo "hello world" > ./output_file.txt
コマンドシェルから。
exec(3,3p)
関数は、現在のプロセスを別のプロセスに置き換えます。つまり、現在のプロセスが停止し、代わりに別のプロセスが実行され、元のプログラムが持っていたリソースの一部が引き継がれます。