Software_Linux_syscall

◆システムコール

_◇システムコールのI/F

※x86
①128番(0x80)が使われている。
②EAXに識別番号を置く。引数もレジスタにコピーする
⇒識別番号 sys/syscall.hを見よ
⇒引数順に ebx, ecx, edx, esi, edi, ebp

③ユーザープロセスからシステムコールされると、entry.S 内の system_call サブルーチンがカーネル空間で呼び出される(システムコールハンドラ)
※これが分岐テーブルを参照し、システムコールのサービスルーチンを呼び出す

⇒システムコール呼び出しは遅いので、まとめて呼び出さないと効率があがらない

※エラーが発生した場合、errnoを負数にした値がeaxに入る。

例)
#include
#include <sys/syscall.h>
#include

int main(void)
{
_  int ret;
_  asm volatile (“int $0x80” : “=a” (ret) : “0” (__NR_getpid));
_  printf(“ret = %d pid = %d\n”, ret, getpid());
_  return 0;
}

※sysenter
vsyscall … カーネル内部でシステムコールの発行メカニズムを変えられるが、ユーザーには影響がでない仕組み

[vdso]領域がvsyscall呼び出しのページ

_◇strace
引数にトレースしたい実行プログラムを指定することで引数のプログラムが発行するシステムコールをダンプできる。openシステムコールを探せばどのようなファイルを開いているかわかる。

例)
$strace ./hello

◆Linux システムコール

※戻り値
多くは、成功なら0以上の整数を返す
失敗なら-1を返す

※errno
失敗したら失敗した原因を表す定数がグローバル変数errnoにセットされる
_  ENOENT  ファイルが存在しない
_  EINVAL  引数の値がおかしい

_◇_exit(2)
#include

void _exit(int status);

statusを終了ステータスとしてプロセスを終了する。絶対に失敗しないので、決して戻らない。

_◇brk(), sbrk()
物理アドレスが割り当てられていないページに物理アドレスを対応させる

_◇close(2)
#include
int close(int fd);

問題なければ0、エラーなら-1

_◇dup(2), dup2(2)
#include
int dup(int oldfd);
int dup2(int oldfd, int newfd);

ファイルディスクリプタ oldfd を複製する

_◇dlopen()
ダイナミックリンクを行うためのAPI

_◇exec(2)

自プロセスを新しいプログラムで上書きする
⇒fork()してexec()すれば、新しいプログラムを起動できたことになる
⇒呼び出しが失敗したときだけ戻る。-1が返る(errnoがセットされる)

execは引数により多くの種類がありexec族と言われる
※execve()だけがシステムコール
⇒語尾lは、コマンドライン引数を引数リストとしてわたす
⇒リストの末尾はNULL
⇒語尾vは、コマンドライン引数を文字列配列で渡す、末尾はNULL
⇒引数リストの第1要素はプログラムの名前
⇒語尾eは最後の引数として環境変数envpを渡す
⇒e無し版では現プロセスの環境変数が引き継がれる
⇒語尾pならば第1引数のプログラムをPATHから探す。

#include

int execl(const char *path, const char *arg,
_  …/*NULL*/ );

例)
execl((“/bin/cat”, “cat”, “hello.c”, NULL);

char *argv[3] = { “cat”, “hello.c”, NULL };
execv(“/bin/cat”, argv);

_◇fcntl(2)
#include
#include
int fcntl(int fd, int cmd, …);

ファイルディスクリプタ関連の操作をおこなう
第2引数のcmdで操作を指定し、第3引数以降で操作特有の引数を与える

_◇fork(2)
#include <sys/types.h>
#include

pid_t fork(void);

カーネルが呼び出したプロセスを複製、2つのプロセスに分裂させる。分裂直後は、どちらもfork()の呼び出しから戻った状態であり、fork()以降のコードが実行される

⇒元から存在:親プロセス parent process
⇒複製されたもの:子プロセス child process

※子プロセスのfork()戻り値は0、親プロセス側ではPID値(正の整数)が戻る。fork()失敗時には-1

_◇getpid(2), getppid(2)
#include <sys/types.h>
#include

pid_t getpid(void);
pid_t getppid(void);

getpid()  自分のプロセスのPIDを返す。
getppid()  親プロセスのPIDを返す。

_◇ioctl(2)
#include <sys/ioctl.h>
int ioctl(int fd, int request, …);

fdに接続したデバイスに特化した操作を行う
第2引数 requestにどのような操作か定数で指定
第3引数以降に該当request特有の引数を指定する

※requestのリストは man ioctl_list に記述さてる

_◇lseek(2)
#include <sys/types.h>
#include

off_t lseek(int fd, off_t offset, int whence);

ファイルディスクリプタfdのファイルオフセットを指定位置offsetに移動する。whenceにより起点がきまる
SEEK_SET  ファイル先頭起点
SEEK_CUR  現在ファイルオフセット起点
SEEK_END  ファイル末尾起点

※端末やプロセスにつながっているストリームに対してlseekを実行するとエラーとなる

_◇mmap()
仮想メモリ機構全体のインタフェース
これを応用してメモリマップトファイルも扱う事ができる

_◇open(2)
#include <sys/types.h>
#include <sys/stat.h>
#include

int open(const char *path, int flags);
int open(const char *path, int flags, mode_t mode);

pathであらわされるファイルにつながるストリームを作成し、そのストリームを指すファイルディスクリプタを返す

※第2引数 定数ビットのORを指定する

O_RDONLY  |  O_CREAT    |   O_EXCL
O_WRONLY    O_APPEND    O_TRUNC
O_RDWR

※第3引数はCREATEの場合だけ有効。umaskとくみあわせてパーミッションを生成する

_◇pipe(2)
#include

int pipe(int fds[2]);

両端とも自プロセスにつながったストリームを作成し、その両端のファイルディスクリプタ2つを返す。
fds[0]が読み込み専用
fds[1]が書き込み専用

※fork()でプロセスを複製するとストリームもすべて複製される。
⇒親が読み込み側をclose、子が書き込み側をcloseすれば
親のfds[1]から、子のfds[0]にパイプが通る

※狙った番号のディスクリプタにパイプをつなぐにはdup2()を使用してつけかえる

close(繋ぎたいディスクリプタ)
dup2(元パイプディスクリプタ、繋ぎたいディスクリプタ)
close(元パイプディスクリプタ)

_◇read (2)
#include
ssize_t read(int fd, void *buf, size_t bufsize);

fdから最大 bufsizeだけ、bufにバイト列を読み込む

読み込み成功。。。読み込んだバイト数を返す
ファイル終端。。。0
エラー。。。-1
※bufsizeより少ないバイト数であることもあるので、必ず戻り値はチェックすること

※ssize_t, size_t
sys/types.hで定義されている
ハードウエアやOSの差を吸収するための型

※read()の扱う文字列は、\0で終端されていないので、通常の文字列関数にそのまま渡すのには適さない。

※あまり細かい単位でシステムコールするととても遅くなる
⇒おおむね1KB以上のバッファサイズを確保すること

_◇rmdir(2)
#include

ディレクトリを削除する。エラーの場合-1を返す。

例)
rmdir(“foo”);

_◇setsid(2)
#include

pid_t setsid(void);

新しいセッションを作成し、自分がセッションリーダになる
(同時にプロセスグループリーダにもなる)

成功すればSIDを返し、失敗すれば-1を返す。

※自プロセスがプロセスグループリーダになっている場合(シェルから起動された場合など)、予め一回余分にfork()して、プロセスグループリーダでなくなっている必要がある。

⇒あらたなセッションなので制御端末を持たない
⇒deamonとなる

_◇wait(2)
#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

fork()した子プロセスの終了を待つ。
wait()は子プロセスのうちどれか一つ、waitpidは特定のpidのプロセスの終了を待つ。

statusにNULL以外を指定すると、そのアドレスに子プロセスの終了ステータスが格納される。

※終了の仕方を調べるマクロ
WIFEXITED(status)    exitで終了していたら非0、それ以外で0
WEXISTATUS(status)    exitで終了していたら終了コードを返す
WIFSIGNALED(status)    シグナルで終了していたら非0、それ以外なら0
WTERMSIG(status)    シグナルで終了したときに、そのシグナル番号を返す

_◇write(2)
#include
ssize_t write(int fd, const void *buf, size_t bufsize);

bufsizeバイト分をbufからfdに書きこむ

書き込み成功。。。書き込んだバイト数
エラー。。。-1

◆Unix

_◇execve システムコール
起動するプログラムのパス名、プログラムに渡す引数のリスト、プログラムに渡す環境変数を指定してプログラムを起動する。

※プログラムに渡す引数リストの中の「プログラム名」は必ずしもそのプログラムの本当のプログラム名で無くても良い。この「引数プログラム名」を見て、処理を変更することができる。

_◇kvm ライブラリ
カーネルメモリインタフェース。作動中のカーネル仮想メモリイメージをアクセスするためのライブラリ。スペシャルファイル/dev/mem, /dev/kmemによりアクセスされる。