2017-04-06 6 views
0

状況は次のとおりです。glibcのgetpid作業手順は何ですか?

私は012bを実行して、githubでカーネルをハックしようとしています。カーネルのバージョンはlinux-3.18.6です。

環境をシミュレートするためにQEMUが使用されます。

私のアプリケーションでは、私は自分のアプリケーションに従うことによってシステムコールの手順を理解しようとしています。私の目的を達成する方法は、シェルプログラムのようなものです。相対的なシステムコールを実行するコマンドをいくつか作成します。たぶんそれは写真を通して簡単です。

1を使用API​​のGETPIDを次のように some commands

コードは簡単です。

int Getpid(int argc, char **argv) 
{ 
    pid_t pid; 
    pid = getpid(); 
    printf("current process's pid:%d\n",pid); 
    return 0; 
} 

2 int $ 0x80を直接使用してください。

int GetpidAsm(int argc, char **argv) 
{ 
    pid_t pid; 
    asm volatile(
    "mov $20, %%eax\n\t" 
    "int $0x80\n\t" 
    "mov %%eax, %0\n\t" 
    :"=m"(pid) 
    ); 
    printf("current process's pid(ASM):%d\n",pid); 
    return 0; 
} 

私のアプリケーションだけで、pidを1とプロセスで、私は、コマンドを入力しgetpidをそうするたびに実行されるので、それは本当だ。もちろん、1を返します。

gyscを使用してsyscallプロセスをデバッグすると、実行するgetpidとタイプすると、berakpoint sys_getpidで一度だけ停止します。何度も何度もやり直すと、止まることなく出力されます。

明らかに、int $ 0x80の使用は私が理解するように絶対に正しいです。

問題を解決するために、私はいくつかの研究を行いました。 glibcソース(glibc-2.25)コードをダウンロードして、api getpidがint $ 0x80をどのようにラップするかを確認します。残念ながら、それはそこになかったか、私はちょうど正しい位置を見つけられませんでした。

glibcのコード。

pid_t getpid(void) 
{ 
    pid_t (*f)(void); 
    f = (pid_t (*)(void)) dlsym (RTLD_NEXT, "getpid"); 
    if (f == NULL) 
    error (EXIT_FAILURE, 0, "dlsym (RTLD_NEXT, \"getpid\"): %s", dlerror()); 
    return (pid2 = f()) + 26; 
} 

私に間違ったコードがある場合は、教えてください。

コードに示すとおり、getpidの定義はglibcには含まれていません。いくつかのデータを読んだ後、誰かがthe VDSO...と言った。

単純なシステムコール のコストの重要な部分であるAFAIKは、ユーザー空間からカーネルに戻ってきていることに注意してください。したがって、いくつかのシステムコール (おそらくgettimeofday、getpid ...)では、VDSOは でさえ回避することができます(そして、技術的には実際のシステムコールを避けるかもしれません)。男GETPID pgaeで

C glibcのバージョン2.3.4以降のライブラリ/カーネルの違い 、GETPID用glibcのラッパー関数() キャッシュのPIDを、追加のシステムコールを避けるためにプロセス がgetpid()を繰り返し呼び出すとき。通常、このキャッシュは見えませんが、 の正しい操作は、 fork(2)、vfork(2)、およびclone(2)のラッパー関数のサポートに依存します。アプリケーションがglibc ラッパーをバイパスしてsyscall(2)を呼び出すと、子の getpid()を呼び出すと間違った値が返されます(正確には、 は親プロセスのPIDを返します)。 clone(2)も参照してください。 glibcラッパー関数を使用して を呼び出しても、getpid()が間違った値を返す場合があります。

私はAPI getpidの作業手順を分かりません。

対照的に、API時間はわかりやすいです。時間の 定義:その後、

time_t 
time (time_t *t) 
{ 
    INTERNAL_SYSCALL_DECL (err); 
    time_t res = INTERNAL_SYSCALL (time, err, 1, NULL); 
    /* There cannot be any error. */ 
    if (t != NULL) 
    *t = res; 
    return res; 
} 

#define INTERNAL_SYSCALL(name, err, nr, args...)   \ 
    internal_syscall##nr ("li\t%0, %2\t\t\t# " #name "\n\t", \ 
        "IK" (SYS_ify (name)),   \ 
        0, err, args) 

最後に、それが埋め込まれていASM、カーネルソースを使用する通常の方法。

API getpidの仕組みを誰かが明確に説明できますか? getpidがシステムコールsys_getpidに1回だけトラップするのはなぜですか?可能であれば、いくつかの参考文献を賞賛する。

ありがとうございました。

+1

あなたの質問は何ですか?あなたはマニュアルを読んでいます:glibcはgetpid-syscallによって返された値をキャッシュします。明らかに、このキャッシュは、子プロセスでfork(2)の後に再設定する必要があります。 –

+0

あなたの答えをありがとう。 dlsymを使ったgetpidのメカニズムは何ですか? getpidの実現が他のものと異なるのはなぜですか?それは私が知りたいことです。ありがとう。 –

答えて

1

まず、glibcソースコードはナビゲートすることがほとんど不可能であることに注意してください。

getpid()が気づいたように結果をキャッシュしていると書かれています。 あなたが見つけたコード

pid_t getpid(void) 
{ 
    pid_t (*f)(void); 
    f = (pid_t (*)(void)) dlsym (RTLD_NEXT, "getpid"); 
    if (f == NULL) 
    error (EXIT_FAILURE, 0, "dlsym (RTLD_NEXT, \"getpid\"): %s", dlerror()); 
    return (pid2 = f()) + 26; 
} 

は単なるラッパーです。 getpidシンボルを検索し、その関数を呼び出します。 その機能が必要です。これはsysdeps/unix/sysv/linux/getpid.cファイルにある__getpid()関数のエイリアスであり、このポストの下部にも表示されています。

現在、glibcのソースコードが現在のglibcと一致していない可能性があります.2019年11月の正確なgetpid()キャッシングに関する大きな変更がthis commitにありました。 2017年2月にリリースされたglibc-2.25の一部

(GETPIDの呼び出しを避けるために、その値をキャッシュされた古いGETPID()の実装が)複数回SYSCALL、ここで見ることができます: http://repo.or.cz/glibc.git/blob/93eb85ceb25ee7aff432ddea0abf559f53d7a5fc:/sysdeps/unix/sysv/linux/getpid.c

static inline __attribute__((always_inline)) pid_t 
really_getpid (pid_t oldval) 
{ 
    if (__glibc_likely (oldval == 0)) 
    { 
     pid_t selftid = THREAD_GETMEM (THREAD_SELF, tid); 
     if (__glibc_likely (selftid != 0)) 
    return selftid; 
    } 

    INTERNAL_SYSCALL_DECL (err); 
    pid_t result = INTERNAL_SYSCALL (getpid, err, 0); 

    /* We do not set the PID field in the TID here since we might be 
    called from a signal handler while the thread executes fork. */ 
    if (oldval == 0) 
    THREAD_SETMEM (THREAD_SELF, tid, result); 
    return result; 
} 
#endif 

pid_t 
__getpid (void) 
{ 
#if !IS_IN (libc) 
    INTERNAL_SYSCALL_DECL (err); 
    pid_t result = INTERNAL_SYSCALL (getpid, err, 0); 
#else 
    pid_t result = THREAD_GETMEM (THREAD_SELF, pid); 
    if (__glibc_unlikely (result <= 0)) 
    result = really_getpid (result); 
#endif 
    return result; 
} 

libc_hidden_def (__getpid) 
weak_alias (__getpid, getpid) 
libc_hidden_def (getpid) 
のように見えます
+0

詳細な回答ありがとうございます。多分私はあなたの意味を理解しています。あなたのメッセージを得た後、私はちょうど私がカーネルをコンパイルするlddで私のubuntu glibcバージョンのバージョンをチェックしました。あなたが指摘したように、それはglibc-2.23です。glibc-2.23で指摘したコードが見つかりました。だから、ピズルを解決する必要があります。しかし、私はまだgetpidの実現がカーネルモードに入る必要のある時間や他のapiと異なるのはなぜか分かりません。また、私はgetpidで使用されているdlsymのメカニズムを理解していません。可能であれば、詳細への参照をいくつか提供できますか?私は深く掘り下げたい、Tks。 –

+0

@ a-thornプロセスのPIDは決して変更されません。したがって、glibcはカーネルから一度pidを取得し、別のシステムコールを実行することなく将来キャッシュすることができます。 time()の呼び出しを同じ方法でキャッシュすることができない理由は明らかです。他のほとんどのシステムコールでも同様です。結果をキャッシュするのは意味がありません。あなたが投稿したコードは、dlsym()関数を使ってgetpidを検索しますが、 '。/ elf/restest2.c'から来ているようですが、このファイルはgetpid()実装ではなくglibcのtestsuiteの一部です。 – nos

+0

あなたの答えをありがとう、私はあなたの効率に感心します。はい、今私は最終的にその違いを知っています。私の愚かさを許してください。今はgetpidのためのテストスイートを知っていますが、実際にgetpid()の実装を見つけることはできません。私のためにそれを指摘できますか?私はgetpidを使用しますが、__getpidの実装を見つけるだけです。また、time()がint $ 0x80を使用してカーネルモードに入ったので、カーネルは実装sys_timeを見つけたので、時間を理解しています。 getpid()については、私は混乱しています。あなたの注意のためのThans。 –

関連する問題