Linuxアプリケーションの開発エンジニアとしてLinuxシステムの機能は、シェルスクリプトを呼び出すために非常に便利な方法ですが、システム関数自体の呼び出しメカニズムを理解していない、対応するエラー処理ではなく、それはプログラムの例外やバグを引き起こすことは容易です。
I. System() の理解
機能:system()関数は、特定のコマンドを実行するために"/bin/sh -c command "を呼び出し、コマンドが実行されるまで現在のプロセスをブロックします。
プロトタイプ:
int system(const char *command).
戻り値:
コマンドを実行するためにシェルを起動できない場合、systemは127を返します。systemが正常に実行できた場合、そのコマンドの終了コードが返されます。
説明
助けてください:
#インクルード <stdlib.h>
int system(const char *command).
説明
system() は、/bin/sh -c を呼び出して command で指定されたコマンドを実行します。
コマンドを実行し、コマンド完了後に戻ります。
コマンドの実行中、SIGCHLDはブロックされ、SIGINTとSIGQUITはブロックされます。
は無視されます。
戻り値
エラーに失敗した場合に返される値は -1 です)。
それ以外の場合は、コマンドのステータスを返します。
を wait(2) で指定されたフォーマットで返します。 したがって、コマンド
は WEXITSTATUS(status) になります。 /bin/sh が実行できなかった場合は
を実行すると、exit(127) を実行したコマンドの終了ステータスになります。
command の値が NULL の場合、system() はシェル
が利用可能なら0。
system() は、他の子プロセスの待機状態には影響しません。
第二に、システム()機能の原理
システム関数が実行されると、fork、execve、waitpidなどの関数が呼び出されます。
Linux版システム機能のソースコード:
int system(const char * cmdstring)
{
pid_t pid;
int status;
if(cmdstring == NULL){
return (1);
}
if((pid = fork())<0){
status = -1;
}
else if(pid == 0){
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127); //子プロセスが正常に実行されていれば、この文は実行されない。
}
else{
while(waitpid(pid, &status, 0) < 0){
if(errno != EINTER){
status = -1;
break;
}
}
}
return status;
}
機能説明
system() は fork() を呼び出して子プロセスを生成し、子プロセスは /bin/sh-c string を呼び出して文字列引数で表されるコマンドを実行し、コマンドの実行後に元のプロセスに戻ります。
SIGCHLD シグナルは、system() 呼び出し中に一時的に保留され、SIGINT および SIGQUIT シグナルは無視されます。
戻り値
=-1: エラーが発生しました
=0:呼び出しは成功しましたが、子プロセスは現れませんでした。
>0: 正常終了した子プロセスのID
system()が/bin/shの呼び出しに失敗した場合、127が返され、その他の失敗の理由 では-1が返されます。引数文字列がヌル・ポインタの場合、0以外の値が返されます >。system() が /bin/sh への呼び出しに成功すると、最後に
シェルコマンド実行後の戻り値ですが、これは /bin/sh への system() 呼び出しが失敗したときに返される 127 かもしれません。
追記
system()は環境変数を継承し、環境変数を渡すとシステム・セキュ リティの問題を引き起こす可能性があります。
システム関数による戻り値の処理には3つの段階があります:
ステージ1:子プロセスの作成などの準備作業。失敗した場合は -1 を返します。
ステージ2:シェルスクリプトをプルアップするために/bin/shを呼び出します。プルアップに失敗した場合、またはシェルが正しく実行を終了しなかった場合、その理由がステータスの下位8~15ビットに書き込まれます。システムのマニュアルでは127という値が書き込まれると説明されているだけですが、実際にテストしてみると126という値も書き込まれることがわかりました。
フェーズ3:シェルスクリプトが正常に実行を終了した場合、シェルの戻り値をSTATUSの下位8~15ビットに入力します。
備考 1:
bin/shを呼び出すことができ、シェルの実行が他のシグナルによって異常に中断されない限り、シェルは正常に終了したとみなされます。
例えば、シェルスクリプトでどのような値が返されたとしても、それが0であろうと0以外であろうと、正常な実行の終了とみなされます。シェルスクリプトが存在しなかったり、実行権限がなかったりしても、通常の実行終了としてカウントされます。
シェルスクリプトの実行中に強制終了された場合などは、異常終了としてカウントされます。
フェーズ 2 でシェル・スクリプトのサブプロセスが正しく実行を終了したかどうかを判断するにはどうすればよいですか?システムはWIFEXITED(status)というマクロを提供しています。WIFEXITED(status)が真なら正常終了です。
ステージ3でシェルの戻り値を得るには?直接8ビット右シフトすることもできますが、システムが提供するマクロ:WEXITSTATUS(status)を使うのが安全です。
シェルスクリプトでは、一般的にスクリプトが正常な実行であるかどうかを判断するために戻り値を介してされるので、リターン0の成功は、正の数を返すように失敗した場合。
つまり、要約すると、シェルスクリプトへのシステム関数呼び出しが正しく終了するかどうかを判断する方法は、以下の3つの条件が同時に成立することです:
-1 != 状態
WIFEXITED(状態)が真
0 == WEXITSTATUS(status)
注目してください:
上記の分析によると、シェルスクリプトが存在しない場合、実行権限がない場合、およびその他のシナリオでは、上記の最初の2つの条件はまだ保持され、この時点でWEXITSTATUS(status)は127、126およびその他の値です。
そのため、シェルスクリプトの戻り値として127や126などを定義してしまうと、それがシェルの戻り値なのか、シェルスクリプトの例外の原因なのか区別がつかなくなってしまうので、シェルスクリプトでは戻り値を1ずつインクリメントするようにした方が良いでしょう。
サンプルの手順
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define EXIT_ERR(m) \
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}\
while (0);\
int main(void)
{
int status ;
status = system("ls -l|wc -l");
if(status == -1){
EXIT_ERR("system error");
}
else{
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
printf("run command successful
");
else
printf("run command fail and exit code is %d
",WEXITSTATUS(status));
}
else
printf("exit status = %d
",WEXITSTATUS(status));
}
return 0;
}
走行結果





