目次
プログラミング教育者。ScratchやPythonを教えています。
C 言語から Linux OS の各システムコールを利用する場合を想定して、関連情報をまとめます。
システムコールについて
プログラムを実行するとき、CPU のレジスタの一つであるプログラムカウンタは、実行するプログラムが展開されたメモリ上のアドレスを指します。CPU が実行するプログラムには、OS のカーネルと、カーネルが管理するユーザプログラムがあります。CPU のプログラムカウンタは、これら複数のプログラムのメモリアドレスを次々と切り換えながら指すことで、見かけ上、複数のプログラムを同時に実行します。これをプリエンプティブ・マルチタスク (preemptive multitask) とよびます。プロセスとして動作するユーザプログラム間の切り換えはコンテキストスイッチという仕組みを利用します。
カーネルとユーザプログラムは異なる CPU モードで動作します。それぞれ、特権モード (スーパーバイザモード、カーネルモード) とユーザモードで動作しており、例えばユーザモードではハードウェアを操作する CPU 命令を出すことができません。ユーザプログラムからハードウェアを操作したい場合は、カーネルが提供するシステムコールというインタフェースを用いて間接的に操作します。このインタフェースを介することにより、ハードウェアを操作する CPU 命令を、不具合を含む可能性のあるユーザプログラムからでも「安全に」出すことができます。ハードウェアを操作するような CPU 命令を出すためには、ユーザプログラムの動作権限を一時的に特権モードに切り換える必要があり、このときもコンテキストスイッチの仕組みを利用します。
libc とシステムコールの関係
カーネルが提供するシステムコールは、アセンブラで記述して呼び出すこともできますが、例えば C 言語の場合、より簡単に利用できるライブラリ関数が各システムコールに対応するように libc 内で提供されています。libc を利用すると、OS の違いによって存在したりしなかったりするシステムコールの差異を吸収することもできます。
マニュアルの参照方法
CentOS の場合は man-pages をインストールしておきます。
sudo yum install man
sudo yum install man-pages
man コマンド実行時にはセクション番号を指定できます。例えば chmod はコマンドとシステムコールでそれぞれ別物であるため、システムコールのマニュアルを読みたい場合は man 2 chmod
とします。
- 1: ユーザコマンド (ls など)
- 2: システムコール (fork など)
- システムコールを除く C ライブラリ関数 (printf など)
上述のとおり、OS によってはシステムコールが存在せず、互換性のためにシステムコールを間接的に利用するライブラリ関数として実装されていることにも注意します。その場合はセクション番号 3 を指定する必要があります。
man 2 fork
man 2 chmod
エラー処理について
システムコールの返り値で条件分岐して perror()
でエラー内容を出力します。
main.c
#include <sys/stat.h> // open()
#include <fcntl.h> // open()
#include <stdio.h> // perror()
#include <errno.h> // errno
#include <string.h> // strerror()
#include <locale.h> // setlocale()
int main() {
int fd_r;
// 英語以外のエラー表示をしたい場合は `LC_ALL` を空にして $LANG 環境変数を見るようにします。
setlocale(LC_ALL, "");
// 基本的には、システムコールを内部的に利用する C ライブラリ関数の返り値を利用して
// エラーが発生したかどうかを判別できます。0 以上であれば成功、失敗の場合は -1 です。
// ただし、システムコールによっては戻り値なしでリターンするものや
// `exit()` のようにそもそもリターンしないものがあります。
if ((fd_r = open("/path/to/nofile", O_RDONLY)) < 0) {
// `任意のメッセージ: strerror(errno)` という形式で出力できます。
perror("/path/to/nofile");
// システムコールの実行でエラーが発生した場合は、外部変数 `errno` にエラー番号が格納されます。
// エラーが発生していない場合は以前の値が格納されたままになります。
// 戻り値なしでリターンするシステムコールを利用する場合は、
// errno に 0 を代入してから実行してエラー判定する必要があります。
printf("errno %d: %s\n", errno, strerror(errno));
return 1;
}
// fd_r を利用した処理
// ...
return 0;
}
実行例
gcc -Wall -O2 main.c
unset LC_ALL
unset LC_MESSAGES
unset LC_CTYPE
$ LANG=ja_JP.UTF-8 ./a.out
/path/to/nofile: そのようなファイルやディレクトリはありません
errno 2: そのようなファイルやディレクトリはありません
$ LANG=C ./a.out
/path/to/nofile: No such file or directory
errno 2: No such file or directory
記事の執筆者にステッカーを贈る
有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。
さらに詳しく →Feedbacks
ログインするとコメントを投稿できます。
関連記事
- cmake で ccache を有効化するための設定YOCTO Linux で開発している場合など、ビルド速度が開発効率にそのまま影響する際は ccache (compiler cache) で C/C++ ビルドを高速化することを考えます。cmakeと併用する場合の設定およびコマンド例を記載します。 インストール sudo apt install ccache 以下のバイナリファイルに加えて $ w
- Python から C ライブラリを利用 (ctypes)FFI (Foreign Function Interface) の一つである ctypes を利用すると、C 言語のライブラリを Python から利用できます。サンプルコードを記載します。 適宜参照するための公式ドキュメント libm の sqrt を利用する例 main.py ``
- 低レイヤーネットワークプログラミングに関する雑多な知識TCP/IP モデルのうちトランスポート層ではなく、インターネット層およびネットワークインターフェイス層のパケット (正確には PDU) を扱う低レイヤープログラミングの雑多なテクニックをまとめます。『ルーター自作でわかるパケットの流れ』などを参考にしています。バックアップ目的で書籍のサンプルコードをホスティングしました。 検証環境
- ファイルディスクリプタ関連のシステムコールのサンプルコード (C 言語)ファイル記述子 (File Descriptor) に関連するシステムコールを利用した C 言語のサンプルコードを記載します。 ファイルの読み書き open/close main.c #include <unistd.h> #include <fcntl.h> #include <stdio.h> int main() { int fd_r, fd_w;...
- C言語の資産を利用 (C++をもう一度)サンプルコード メルセンヌ・ツイスタなど、C言語で記述されたライブラリをC++から利用するためには extern "C" を利用します。その際、組み込みマクロ __cplusplus を利用するとC言語からもC++からも利用できるヘッダファイルを作成できます。 sub.h #ifndef SUB_H_ #define SUB_H_ #ifdef __cplu