C の fork() と exec() をマスターする:プロセスの作成と置換の説明
この Linuxhint の記事では、fork() 関数と exec() 関数を使用してプロセスを作成、実行、または別のプロセスに置き換える方法を学びます。これら 2 つの関数の説明を見て、その構文と呼び出し方法を説明します。 2 つの関数それぞれの簡単な実践例も見ていきます。次に、fork() と execv() を組み合わせて使用してプロセスを作成し、それを別のプロセスに置き換える方法を説明します。
C 言語の Fork() 関数
fork() 関数は、呼び出しプロセスの複製を作成します。子プロセスは親プロセスの複製ですが、割り当てられたメモリ領域、PID などの特定のプロパティを共有しません。 次に、fork() 関数の構文を見てみましょう。
fork() 関数は、親プロセスの結果として子プロセスの PID を返しますが、同じ呼び出しは子プロセスには影響せず、結果として 0 を返します。このメカニズムにより、fork() の戻り値である「if」条件を通じて 2 つのプロセスで異なるコードを実行できます。次の概念を見てみましょう。
if (fork() ==0)
{
子プロセスのコード
}
それ以外の場合
{
親プロセスのコード
}
このようにして、親プロセスは「else」ステートメントの中括弧の間にあるコードを実行し、子プロセスは「if」ステートメント内のコードを実行します。
簡単な例でこれを見てみましょう。次のコードは 2 つの無限ループで構成されています。親プロセスでは、プログラムは「親プロセスに書き込みます」メッセージを表示する「else」ステートメントに対応するループに入りますが、fork() によって作成された子プロセスでは、「子プロセスに書き込みます」メッセージを表示する「if」ステートメントに入ります。
#include
#include
void main(){
if (fork() ==0)
{
一方 (1){
printf ("子プロセスによる書き込み\n");
スリープ(5);}
}
それ以外の場合
{
一方 (1){
printf ("親プロセスによる書き込み\n");
スリープ(2);}
}
}
次の図は、このコードのコンパイルと実行を示しています。コマンド コンソールに見られるように、各プロセスは異なるコードを実行します。

C 言語の ExecXXX() 関数
execXXX() ファミリの関数は、実行中のプロセスを新しいプロセスに置き換えます。新しいプロセスのイメージは、置き換えられるプロセスに割り当てられたメモリ領域にコピーされ、特にその PID と割り当てられたリソースが保持されます。
「unistd.h」ヘッダーで定義されているこのグループの関数は、入力に応じて異なる呼び出しメソッドを使用し、「可変個数」タイプであるため、未指定の引数リストまたはポインタを古いプロセスから新しいプロセスに渡すことができます。次に、各関数の構文を見てみましょう。
int execl(const char *path, const char *arg, ...(char *) NULL );
int execlp(const char *file, const char *arg, ... (char *) NULL );
int execle(const char *path, const char *arg, ... , (char *) NULL, char * const envp[] );
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
execl()、execle()、および execv() 関数は、新しいプロセスの実行可能ファイルの絶対パスを含む文字列へのポインタを最初の入力引数として使用しますが、execlp()、execvp()、および execvpe() は、現在のディレクトリ内のファイルの名前を使用します。 2 番目の入力は、新しいプロセスに渡す引数です。これらは、const char *arg 文字列、または char *const argv[] 文字列へのポインタのリストのいずれかでなければなりません。
次に、execv() 関数を使用してプロセスを置き換え、あるプログラムから別のプログラムに入力引数を渡す例を見てみましょう。
これを行うために、2 つの非常に単純なコードを作成します。 1 つは、execv() 関数を呼び出して子プロセスを実行する親プロセスです。 execv() 関数は、子プロセスを開始するときに、子プロセスが取得してシェルに表示する文字列の形式で 2 つの入力引数を渡します。
子プロセス
子プロセスは、「私は子プロセスです」というメッセージを出力し、親プロセスによって送信された入力引数を取得してシェルに表示する単純なコードです。子プロセスのコードは次のとおりです。
#include
#include
#include
int main(int argc, char *argv[])
{
printf ("私は子プロセスです\n\n");
printf ("引数 1:%s\n", argv[1]);
printf ("引数 2:%s\n", argv[2]);
}
このコードをコンパイルし、次に示すように、その生成物を「子」名で「.bin」拡張子を付けて「ドキュメント」に保存します。
~$ gcc ドキュメント/child.c -o ドキュメント/child.bin
このようにして、子実行ファイルを「ドキュメント」に保存します。この実行可能ファイルのパスは、親プロセスで execv() を呼び出すときの入力引数のパスです。
親プロセス
親プロセスは、execv() 関数を呼び出して子プロセスに置き換えるプロセスです。このコードでは、execv() 関数が開くプロセスへの入力引数を表す文字列へのポインタの配列を定義します。
次の図では、文字列へのポインターの配列を正しく作成する方法を示しています。この場合、これは 4 つのポインターで構成され、「arg_Ptr[]」と呼ばれます。
ポインター配列を定義したら、各ポインターに、子プロセスに送信する入力引数を含む文字列を割り当てる必要があります。 execxx() 関数を使用する場合の経験則として、最初の引数は実行可能ファイルの名前と拡張子を含む文字列にする必要があり、最後のポインタは NULL にする必要があります。
したがって、対応する引数を文字列形式で各ポインタに割り当てます。
arg_Ptr[0] ="子.bin";
arg_Ptr[1] =" こんにちは ";
arg_Ptr[2] =" プロセス 2 ";
arg_Ptr[3] =NULL;
次のステップでは、execv() 関数を呼び出し、実行可能ファイルの絶対パスを含む文字列を最初の引数として渡し、文字列の配列 arg_Ptr[] を 2 番目の引数として渡します。親プロセスの完全なコードは以下で確認できます。
#include
#include
#include
#include
#include
int メイン (){
printf (「私は親プロセスです」);
char *arg_Ptr[4];
arg_Ptr[0] ="子.c";
arg_Ptr[1] =" こんにちは ";
arg_Ptr[2] =" プロセス 2 ";
arg_Ptr[3] =NULL;
execv("/home/linuxhint/Documents/child.bin", arg_Ptr);
}
「.c」ファイルのパスと出力の名前を指定するこのコードをコンパイルします。
~$ gcc ドキュメント/parent.c -o パターン
次に、出力を実行します。
親プロセスは、「私は親プロセスです」というメッセージを表示し、次のプロセスに渡される各入力引数に文字列を割り当てることで文字列配列を作成し、execv() 関数を呼び出します。
execv() 関数が正常に実行されると、「child.bin」実行可能ファイルが親プロセスを置き換え、その ID と割り当てられたメモリを引き継ぎます。したがって、このアクションは元に戻すことができません。
子プロセスは、「私は子プロセスです」というメッセージを表示し、コマンド コンソールに表示するために親プロセスによって渡された各入力引数を取得します。

Linux で新しいプロセスを作成するための Fork() 関数と Execve() 関数の組み合わせ
これまで見てきたように、fork() 関数はプロセスを複製し、execve() 関数はプロセスを置き換えます。この例では、これら 2 つの関数を組み合わせて使用して、複製から新しいプロセスを開いて置き換える方法を見ていきます。これを行うには、前に見た 2 つの関数のコードを結合して、fork() が親プロセスを複製し、execve() がそれを実行可能ファイルに置き換えます。この場合、前の「child.bin」の例で使用したものと同じです。
ここで、「.c」拡張子の付いた空のファイルを取得し、fork() 関数の例で見たプログラム コードを挿入します。
このプログラムでは、子プロセスのコードのみを変更して、execv() 関数が子プロセスを「child.bin」実行可能ファイルに置き換えますが、メイン タスクは fork() 関数の例と同じです。
これを行うには、execv() 関数の例から main() 関数の内容をコピーし、「if」ステートメントの内容をこのコードに置き換えます。それでは、完全なプログラムがどのようなものかを見てみましょう:
#include
#include
void main(){
if (fork() ==0)
{
printf ("私は子プロセスです\n");
char *arg_Ptr[5];
arg_Ptr[0] ="子.c";
arg_Ptr[1] =" こんにちは ";
arg_Ptr[2] =" プロセス 2 ";
arg_Ptr[3] =NULL;
execv(/home/linuxhint/Documents/child.bin", arg_Ptr);
}
それ以外の場合
{
一方 (1){
printf ("
スリープ(3);}
}
}
このようにして、システム上に新しいプロセスを作成することでプロセスが複製され、その後 execv() 関数によって「child.bin」実行可能ファイルのプロセスと置き換えられます。次に、このコードをコンパイルした画像が表示されます。

画像でわかるように、fork() 関数はプロセスを複製して新しいプロセスを作成し、その後 execv() 関数が「child.bin」実行可能ファイルに置き換えます。
結論
この Linuxhint の記事では、fork() 関数と execv() 関数を使用して Linux で新しいプロセスを開く方法を説明しました。これらの各関数、その構文、入力引数と出力引数について簡単に説明しました。仕組みをよりよく理解できるように、これらの各関数の呼び出し方法と各関数が実行するタスクを学習した例も含めました。これら 2 つの関数を個別に検討した後、これらを組み合わせて使用し、Linux で新しいプロセスを作成するために C 言語が提供するメソッドを実装する方法を説明しました。
-
異なるポインター操作とC言語のポインターの問題は何ですか?
ポインタは、値が別の変数のアドレス、つまりメモリ位置の直接アドレスである変数です。他の変数や定数と同様に、変数アドレスを格納するために使用する前に、ポインターを宣言する必要があります。 次のステートメントを検討してください- int qty = 179; メモリ内の変数の表現は次のとおりです- 次のようにポインタを宣言できます- Int *p; これは、「p」が別の整数変数のアドレスを保持するポインタ変数であることを意味します。 アドレス演算子(&)は、ポインタ変数を初期化するために使用されます。 例- int qty = 175; int *p; p= &qty;
-
ビット演算を使用した2による加算と乗算のCプログラム。
ビット演算子はビットを操作します(つまり、onオペランドのバイナリ値を操作します) オペレーター 説明 & ビットごとのAND | ビットごとのOR ^ ビット単位のXOR < 左シフト 右シフト - 1の補数 ビットごとのAND a b a&b 0 0 0 0 1 0 1 0 0 1 1 1 ビットごとのOR a b a | b 0 0 0 0 1 1 1 0