fork()
C では、最初に呼び出して新しいプロセス (最終的なサブプロセス) を作成し、次にexec*()
関数ファミリの 1 つを呼び出してサブプロセスを実行することで、最大数のオプションを取得します。元のプロセスと新しいプロセスの両方が同時に実行されるため、パイプまたはソケットのペアを介して交換 (データの読み取りおよび/または書き込み) を行うことができます。最後にwaitpid()
、ループ内で例を使用して、新しいプロセスが終了するのを待ち、その終了ステータスを「取得」します。例えば:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
int main(void)
{
pid_t child, p;
int status;
/*
* Prepare pipes et cetera first.
*/
/* Fork to create the subprocess. */
child = fork();
if (child == (pid_t)-1) {
/* Cannot fork(); usually out of resources (user limits).
* see errno for details. With <string.h>, you can use
* strerror(errno) to obtain the error string itself. */
return 1;
} else
if (!child) {
/* This is the child process itself.
* Do whatever cleanup is necessary, then
* execute the subprocess command. */
execlp("/bin/ls", "ls", "-lA", NULL);
/* This is only reached if the exec failed;
* again, see errno for reason.
* Always have the child process exit! */
return 127;
}
/* This is only run by the parent process
* (because the child always exits within the
* else if body above).
*
* The subprocess PID is 'child'.
*/
/* Wait for the child process to exit. */
do {
status = 0;
p = waitpid(child, &status, 0);
if (p == (pid_t)-1 && errno != EINTR)
break; /* Error */
} while (p != child);
if (p != child) {
/* Child process was lost.
* If (p == (pid_t)-1), errno describes the error.
*/
} else
if (WIFEXITED(status)) {
/* Child process exited with WEXITSTATUS(status) status.
* A status of 0 (or EXIT_SUCCESS) means success,
* no errors occurred. Nonzero usually means an error,
* but codes vary from binary to binary.
*/
} else
if (WIFSIGNALED(status)) {
/* Child process died from WTERMSIG(status) signal.
* If you include <string.h>, you can use
* strsignal(WTERMSIG(status))
* to obtain the name (string) of the terminating signal.
*/
} else {
/* Child process died from unknown causes.
*/
}
/* All done. */
return 0;
}
個人的には、私socketpair()
が制御するプロセス間で Unix ドメイン ストリームまたはデータグラム ソケットを作成するために使用することを好みpipe()
ます。すべての場合において、関数を使用して、標準入力 (STDIN_FILENO
記述子)、標準出力 (STDOUT_FILENO
記述子)、および標準エラー (STDERR_FILENO
記述子) をソケットまたはパイプに置き換えることができdup2()
ます。必要に応じて、親プロセスの下にある疑似ファイルにアクセスして/proc/[child]/
、子プロセスの状態を観察することもできます。
サブプロセスと通信する必要がある方法に応じて-ファイルからの/への入力/出力?メモリ内の文字列?出力用に動的に割り当てられたバッファー -- 多くのバリエーションがあります。通常、上記と同様のコードは、正確な制御や全二重 (読み取りと書き込みの両方) および/または非同期通信が必要な場合に使用されます。
お気に入りの検索エンジンで「linux」「fork」「exec」を検索して、さまざまな品質の例を見つけることができます。
より簡単な解決策が必要で、コマンドの出力をキャプチャするだけでよい場合 (コマンドに入力を提供しないか、ファイルから入力を提供する場合)、次の変形を使用できます。
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
FILE *sub;
pid_t subpid;
int status;
sub = popen("setsid /bin/sh -c 'echo $$ ; exec command args' </dev/null", "rb");
if (!sub) {
/* popen() failed. */
return 1;
}
/* Read the first line from sub. It contains the PID for the command. */
{
char buffer[32], *line, dummy;
int value;
line = fgets(buffer, sizeof buffer, sub);
if (!line) {
/* setsid failed, or non-POSIXy system (Windows?) */
pclose(sub);
return 1;
}
if (sscanf(line, "%d%c", &value, &dummy) != 1 || value < 2) {
/* Internal bug, or extra output? */
pclose(sub);
return 1;
}
/* subpid is also the session ID and the process group ID,
* because it is the session leader. */
subpid = value;
}
/* Read from sub using standard I/O, to capture command output. */
/* After no more output to read from sub, reap the subprocess. */
errno = 0;
do {
status = pclose(sub);
} while (status == -1 && errno == EINTR);
if (status) {
/* Problem: sub exited with nonzero exit status 'status',
* or if status == -1, some other error occurred. */
} else {
/* Sub exited with success (zero exit status). */
}
/* Done. */
return 0;
}
Linux では、popen()
( /bin/sh
POSIX.1 仕様に従って) シェルを使用し、setsid
コマンドライン ユーティリティを使用して新しいセッションを作成できます。コマンド内で、はシェル PID を出力echo $$
するコマンドであり、シェルをコマンドに置き換えます。したがって、コマンドが実行される前であっても、コマンドの PID を取得します。sh
exec CMD...