2016-12-06 4 views
2

私はいくつかの追加のパラメータでユーザープロセスのクローンを作成するために、クローン(2)のsyscall(カーネルスペース内)を作成しようとしています。このシステムコールは、clone私は、私が見たときにuser_space.Howeverからカーネルに一つの追加パラメータを渡したいのglibcのcode すべてのパラメータがクローン()Linuxカーネルでclone(2)syscallの別のバリエーションを実装する方法は?

int clone(int (*fn)(void *), void *child_stack, 
      int flags, void *arg, ... 
      /* pid_t *ptid, void *newtls, pid_t *ctid */); 

のユーザーの呼び出しではなく、いくつかのと同じ順序で渡されていないようですglibcのコード自体によって処理されています。私はインターネットを検索してglibのクローン()がどのように動作するのかを調べましたが、より良い文書は見つかりませんでした。 誰でも説明できます

  1. glibcがclone()をどのように処理するのですか?
  2. また、カーネル内のsyscallのすべてのパラメータは、glibcのクローンとまったく同じではないため、これらのバラエティはどのように処理されますか?

答えて

3

どのように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()関数は常に-1errno == ENOTSUPと返します。

ただし、Linuxのシステムコールは6つのパラメータに制限されています。カーネル関数がもっと必要な場合は、構造体にパラメータをパックし、その構造体のアドレスをカーネルに渡し、copy_from_user()を使用してカーネル内の同じ構造体に値をコピーする必要があります。すべてのLinuxアーキテクチャ、ポインタとlongは、同じ大きさ(intはポインタよりも小さくてもよい)で

ので、私はあなたに/カーネルからデータを渡すために、このような構造にlongまたは固定サイズのタイプのいずれかを使用することを推奨します。

関連する問題