プログラムを開発している間、プログラマーは、コードが複雑であってはならない、つまり保守可能であるべきである、移植性も念頭に置いておくべき別の分野など、いくつかのことを心に留めておく必要があります。したがって、優れたコードを生成するためにプログラマーが従うべきいくつかの優れたプラクティスがあることがわかります。この記事では、プログラマーが Linux でシステム コールを操作する際に従うべきいくつかの優れた方法に焦点を当てます。
システムコールとは?
システム コールは、カーネルから何らかのサービスを要求するために行われる特別な関数呼び出しです。要求されたサービスは、新しいプロセスの作成、ハードディスクなどのハードウェアへのアクセスなどです。システム コールが実行されると、実行がユーザー モードからカーネル モードに切り替わり、必要なサービスがカーネルによって提供されると、実行はユーザーモードに戻ります。システム コールの例としては、fork()、read()、write() などがあります。
システム コールの処理
システム コールを処理する際は、次の点に注意してください。
- プログラマーは、システム コールの内外の知識を持っている必要があります。たとえば、正確に何をするか、使用するシステム リソース、期待する引数の種類、特にどのような場合に失敗するかなどです。
- ほとんどの Linux システム コールは、失敗するとエラー コードを返します。これらのエラー コードは、障害の原因となったエラーの種類によって異なる場合があります。そのため、適切なエラー処理を実施して、各種類のエラーを適切に処理し、(ユーザーまたは親モジュールのいずれかに) 明確にエスカレートする必要があります。
- システム コールとそれが返すエラー コードの完全な知識については、その特定のシステム コールのマニュアル ページを読むことを強くお勧めします。マニュアル ページは、Linux のシステム コールに関する基本的な理解を深めるための最良のリファレンスです。
一般的なシステム コールの失敗
システム コールの失敗は、システム コールの実行中に発生したエラーの種類によって異なりますが、システム コールの失敗の主な原因となる理由のリストを以下に示します。
- システム コールがシステム ハードウェアにアクセスしようとしたときに、何らかの理由でハードウェアが利用できない場合、またはハードウェアが故障していると思われる場合、システム コールは失敗します。
- システム コールの実行中に優先度の高い信号が発生すると、システム コールの実行が失敗する可能性もあります。
- システム コールを介して、プログラムが特別な権限またはルート権限を必要とする特定のタスクを実行しようとする場合があります。プログラムにそのような権限がない場合、システム コールも失敗します。
- システム コールが失敗するもう 1 つの非常に一般的な理由は、無効な引数を渡すことです。
- ヒープからメモリを要求するシステム コールが行われ、システム コールを行った要求側プロセスに何らかの理由でシステムがメモリを割り当てることができないとします。この場合、システム コールも失敗します。リ>
システム コールが失敗する理由は他にも多数あるため、上記のリストは網羅的なものではありません。
エラー コードの処理
既に説明したように、各システム コールは、発生した (システム コールの失敗の原因となった) エラーの種類ごとに特定のエラー コードを返します。そのため、エラー情報を特定して伝達することは、プログラミングの非常に重要なタスクです。一般に、ほとんどのシステム コールは成功すると '0' を返し、失敗すると 0 以外を返しますが、メモリへのポインタを返すシステム コール ( malloc() など) は、失敗すると '0' または NULL を返し、成功すると 0 以外のポインタ値を返します。 .
注:上記の観察は、すべてのシステム コールに当てはまらない場合があります。いくつかの例外があるかもしれません.
したがって、エラー コードに戻ると、説明したように、システム コールの失敗の原因に関する重要な情報を提供できます。現在、各エラー コードは特定の理由に関連付けられているため、プログラムはエラー コードのマップとエラーの原因を説明するテキストを持つことができます。しかし、これは非常に非効率的で実用的ではありません。これは、プログラムで使用されるシステム コールごとに大量のマッピングが必要になるためです。では、問題は、これを達成するためのより効率的な方法は何でしょうか?
「errno」変数
この変数のマニュアルページから:
<ブロック引用>
有効なエラー番号はすべてゼロ以外です。 errno は、システム コールまたはライブラリ関数によってゼロに設定されることはありません。一部のシステム コールとライブラリ関数(getpriority など)では、-1 は成功時の有効な戻り値です。このような場合、呼び出しの前に errno をゼロに設定することで、成功したリターンとエラー リターンを区別できます。次に、エラーが発生した可能性があることを示すステータスが呼び出しから返された場合は、errno に非 -ゼロ値。 errno は、ISO C 標準によって、int 型の変更可能な左辺値であると定義されており、明示的に宣言してはなりません。 errno はマクロの場合があります。 errno はスレッドローカルです。あるスレッドで設定しても、他のスレッドの値には影響しません。
そう。上記の説明から、Linux でのシステム コールのエラー処理に関しては非常に便利なツールであり、多くの労力を節約できることは明らかです。ただし、この変数はスレッドに対してローカルであるため、マルチスレッド プログラムでこの変数を使用する場合は注意してください。したがって、1 つのスレッドで errno の値を変更しても、他のスレッドではアクセスできません。
strerror() API
errno のみを使用する場合の問題の 1 つは、依然として整数値のみであることです。ログを記録するとき、またはエラーの原因をユーザーに渡すときに、説明は常により役立ちます。そのため、エラー コードのマップとそれらがマップされる原因が必要です。これが「strerror()」API です。この関数は、errno 変数を引数として取り、エラー コードがマップされる原因の説明を含む文字列へのポインターを返します。
#include <string.h> char *strerror(int errnum);
この関数の他のバリアントも利用できます。詳細については、この API のマニュアル ページを参照してください。
注 :興味のある読者は、perror() API を参照することもできます。これは、システム コールの失敗のエラー メッセージを標準エラーに出力するために使用されます。
例
errno と strerror() の使用法を示す例を見てみましょう
#include<stdio.h> #include<errno.h> #include<string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(void) { int fd = -1; // Always Reset errno before use. errno = 0; // Make sure you are opening a file that does not exist fd = open("abcd",O_RDONLY); if(fd == -1) { // Seems like some error occured. Use strerror to print it printf("\nStrerror() says -> [%s]\n",(char*)strerror(errno)); return 1; } return 0; }
上記のコード:
- errno は、最初にゼロであるとは限らないため、「0」に初期化されます。
- システム コール open() が失敗するように、存在しないファイルを開きます。
- 現在、strerror() API を使用して、errno コードに基づいてエラー メッセージを出力しています。
上記のプログラムを実行すると:
$ ./strerror Strerror() says -> [No such file or directory]
そのため、エラー コードではなく意味のあるエラー メッセージが出力に表示されることがわかります。