どのようにglibcがclone()を処理するのですか?
アーチ特有のアセンブリラッパーを介して。 i386については、glibcソースのsysdeps/unix/sysv/linux/i386/clone.Sを参照してください。 x86-64の場合はsysdeps/unix/sysv/linux/x86-64/clone.Sなどを参照してください。
通常のsyscallラッパーは、スタックを切り替えるためのユーザースペースまでであるため、十分ではありません。上記のアセンブリファイルには、システムコールに加えて実際にユーザー空間で何が行われる必要があるかに関するかなり有益なコメントがあります。
カーネルでシステムコールのすべてのパラメータは、glibcの中でクローンとまったく同じではありませんので、どのようにこれらの変化が処理されますか?
システムコールにマップするCライブラリ関数は、ラッパー関数です。
たとえば、POSIX.1 write()
CライブラリのローレベルI/O機能と、Linux write()
システムコールを考えてみましょう。パラメータは基本的に同じで、エラー条件と同じですが、エラーの戻り値は異なります。エラーが発生した場合、を設定した-1
が返されますが、Linuxのシステムコールは負のエラーコード(基本的にerrno
の値に一致)を返します。
sysdeps/unix/sysv/linux/x86_64/sysdep.hシステムコールの戻り値がエラーを示した場合、あなたがするx86-64上のLinuxのための基本的なシステムコールのラッパーが
だけで、実際のシステムコールを呼び出す
# define INLINE_SYSCALL(name, nr, args...) \
({ \
unsigned long int resultvar = INTERNAL_SYSCALL (name, , nr, args); \
if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (resultvar,))) \
{ \
__set_errno (INTERNAL_SYSCALL_ERRNO (resultvar,)); \
resultvar = (unsigned long int) -1; \
} \
(long int) resultvar; })
に沸くことを確認することができ、その後、チェックします。存在する場合は、結果を-1
に変更し、それに応じてerrno
を設定します。 GCCの拡張機能を利用して1つのステートメントとして動作するため、面白そうです。
のは、あなたがLinuxへの新しいシステムコールを追加したとしましょう、どんな理由のために、あなたは
int splork(void *arg2, unsigned long arg1);
として問題をユーザ空間ないし、それを公開したい、
SYSCALL_DEFINE2(splork, unsigned long, arg1, void *, arg2);
言うと!必要なのは
#ifndef _SPLORK_H
#define _SPLORK_H
#define _GNU_SOURCE
#include <sys/syscall.h>
#include <errno.h>
#ifndef __NR_splork
#if defined(__x86_64__)
#define __NR_splork /* syscall number on x86-64 */
#else
#if defined(__i386)
#define __NR_splork /* syscall number on i386 */
#endif
#endif
#ifdef __NR_splork
#ifndef SYS_splork
#define SYS_splork __NR_splork
#endif
int splork(void *arg2, unsigned long arg1)
{
long retval;
retval = syscall(__NR_splork, (long)arg1, (void *)arg2);
if (retval < 0) {
/* Note: For backward compatibility, we might wish to use
*(__errno_location()) = -retval;
here. */
errno = -retval;
return -1;
} else
return (int)retval;
}
#else
#undef SYS_splork
int splork(void *arg2, unsigned long arg1)
{
/* Note: For backward compatibility, we might wish to use
*(__errno_location()) = ENOTSUP;
here. */
errno = ENOTSUP;
return -1;
}
#endif
#endif /* _SPLORK_H */
SYS_splork
と__NR_splork
は新しいシステムコールのためのシステムコール番号を定義するプリプロセッサマクロで、最小限のヘッダファイルを提供することです。 syscallの番号は公式のカーネルのソースとヘッダーには含まれていない可能性が高いため、上記のヘッダーファイルはサポートされているアーキテクチャごとに明示的に宣言しています。サポートされていないアーキテクチャの場合、splork()
関数は常に-1
をerrno == ENOTSUP
と返します。
ただし、Linuxのシステムコールは6つのパラメータに制限されています。カーネル関数がもっと必要な場合は、構造体にパラメータをパックし、その構造体のアドレスをカーネルに渡し、copy_from_user()
を使用してカーネル内の同じ構造体に値をコピーする必要があります。すべてのLinuxアーキテクチャ、ポインタとlong
は、同じ大きさ(int
はポインタよりも小さくてもよい)で
ので、私はあなたに/カーネルからデータを渡すために、このような構造にlong
または固定サイズのタイプのいずれかを使用することを推奨します。