简体   繁体   English

glibc中的getpid工作过程是什么?

[英]what's the getpid work procedure in glibc?

The situation is as below: 情况如下:

I am trying to do the project which hacks the kernel in github. 我正在尝试做一个在github中破解内核的项目 Kernel version is linux-3.18.6. 内核版本是linux-3.18.6。

QEMU is used to simulate the environment. QEMU用于模拟环境。

In my application, I try to understand the syscall procedure by follow them.The way to complete my aim is just like the shell program. 在我的应用程序中,我尝试通过遵循它们来理解syscall过程。完成目标的方法就像shell程序一样。 I just create some commands to run the relative syscall. 我只是创建一些命令来运行相对的系统调用。 Maybe it's simple through the picture. 也许通过图片很简单。 some commands 一些命令

Code is simple as follows: 代码很简单,如下所示:

1 Use API getpid. 1使用API​​ getpid。

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

2 Use int $0x80 directly. 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;
}

Because my application just run in the process with pid 1, so every time I type command getpid, it returns 1. Of course that's true. 因为我的应用程序仅在进程号为pid的进程中运行,所以每次我键入命令getpid时,它都会返回1。当然,这是正确的。

The weird thing is that when I use gdb to debug the syscall process, it stops at the berakpoint sys_getpid only once when I type getpid to execute. 奇怪的是,当我使用gdb调试syscall进程时,当我键入getpid来执行时,它仅在berakpoint sys_getpid处停止一次。 When I do it again and again, it just outputs without stopping. 当我一遍又一遍地执行时,它只是输出而不会停止。

Obviously, the use of int $0x80 is absolutely correct as I understand. 显然,据我所知,使用int $ 0x80是绝对正确的。

To fix the problem, I did some research. 为了解决这个问题,我做了一些研究。 I download the glibc source(glibc-2.25) code to see how the api getpid wraps int $0x80. 我下载了glibc源代码(glibc-2.25),以查看api getpid如何包装int $ 0x80。 Unfortunately, it wasn't there or I just didn't find the right position. 不幸的是,那里不存在,或者我只是找不到合适的位置。

some code in glibc. 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;
}

If I got the wrong code, please tell me,tks. 如果我输入的代码错误,请告诉我。

As the code indicates, the definition of getpid is not contained in glibc. 如代码所示,getpid的定义未包含在glibc中。 After read some data, someone said the VDSO... . 读取一些数据后,有人说VDSO...。

Notice that, AFAIK, a significant part of the cost of simple syscalls is going from user-space to kernel and back. 注意,AFAIK是简单系统调用成本的很大一部分,它是从用户空间到内核再到内核。 Hence, for some syscalls (probably gettimeofday, getpid ...) the VDSO might avoid even that (and technically might avoid doing a real syscall). 因此,对于某些系统调用(可能是gettimeofday,getpid ...),VDSO甚至可以避免(并且从技术上讲,可以避免进行真正的系统调用)。

In the man getpid pgae: 在男人getpid pgae中:

C library/kernel differences Since glibc version 2.3.4, the glibc wrapper function for getpid() caches PIDs, so as to avoid additional system calls when a process calls getpid() repeatedly. C库/内核的差异自glibc 2.3.4版以来,用于getpid()的glibc包装器功能会缓存PID,以便在进程重复调用getpid()时避免其他系统调用。 Normally this caching is invisible, but its correct operation relies on support in the wrapper functions for fork(2), vfork(2), and clone(2): if an application bypasses the glibc wrappers for these system calls by using syscall(2), then a call to getpid() in the child will return the wrong value (to be precise: it will return the PID of the parent process). 通常,此缓存是不可见的,但是其正确操作取决于对fork(2),vfork(2)和clone(2)的包装函数的支持:如果应用程序使用syscall(2)绕过了这些系统调用的glibc包装器),那么在子级中对getpid()的调用将返回错误的值(准确地说:它将返回父级进程的PID)。 See also clone(2) for dis‐ cussion of a case where getpid() may return the wrong value even when invoking clone(2) via the glibc wrapper function. 另请参见clone(2),以了解即使通过glibc包装函数调用clone(2)时getpid()可能返回错误值的情况。

Though there exit so many explain, I can't figure out the work procedure of API getpid. 尽管有太多的解释,但我仍无法弄清楚API getpid的工作过程。

As a contrast, API time is easy to understand. 相比之下,API时间很容易理解。 The definition of time: 时间的定义:

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;
}

then, 然后,

#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)

Finally, it's embedded asm, the normal way to use kernel source. 最后,它是嵌入式asm,这是使用内核源代码的常规方法。

#define internal_syscall1(v0_init, input, number, err, arg1)        \
({                                  \
    long _sys_result;                       \
                                    \
    {                               \
    register long __s0 asm ("$16") __attribute__ ((unused))     \
      = (number);                           \
    register long __v0 asm ("$2");                  \
    register long __a0 asm ("$4") = (long) (arg1);          \
    register long __a3 asm ("$7");                  \
    __asm__ volatile (                      \
    ".set\tnoreorder\n\t"                       \
    v0_init                             \
    "syscall\n\t"                           \
    ".set reorder"                          \
    : "=r" (__v0), "=r" (__a3)                  \
    : input, "r" (__a0)                     \
    : __SYSCALL_CLOBBERS);                      \
    err = __a3;                         \
    _sys_result = __v0;                     \
    }                               \
    _sys_result;                            \
})

Can somebody explain clearly how the API getpid works? 有人可以清楚说明API getpid的工作原理吗? Why the getpid just trap into the syscall sys_getpid only once? 为什么getpid只捕获一次syscall sys_getpid? Some references is admired if possible. 如果可能,请赞赏一些参考资料。

Thanks for your help. 谢谢你的帮助。

First of all note that that the glibc source code is nigh impossible to navigate. 首先请注意,glibc源代码几乎无法导航。

The documentation states that getpid() caches its result, as you have noticed. 正如您所注意到的,文档指出getpid()缓存其结果。 The code you have found that looks like 您找到的代码看起来像

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;
}

is merely a wrapper. 只是一个包装。 It looks up a getpid symbol, and calls that function. 它查找一个getpid符号,并调用该函数。 That function is what you need to find. 您需要找到该功能。 It is aliased to the __getpid() function which you will find in the sysdeps/unix/sysv/linux/getpid.c file, and is also shown in the bottom of this post. 它是__getpid()函数的别名,您可以在sysdeps/unix/sysv/linux/getpid.c文件中找到该函数,该函数也显示在本文的底部。

Now - you might be looking at glibc source code that does not match your current glibc - there was a big change regarding exactly the getpid() caching in November 2016 in this commit , as far as I can tell that change would be part of glibc-2.25 released in February 2017 现在-您可能正在查看与当前glibc不匹配的glibc源代码-在2016年11月的一次提交中 ,关于getpid()缓存的确有很大的变化,据我所知,该变化将成为glibc的一部分-2.25已于2017年2月发布

The older getpid() implementation that cached its value to avoid calling the getpid() syscall more than once, can be seen here: http://repo.or.cz/glibc.git/blob/93eb85ceb25ee7aff432ddea0abf559f53d7a5fc:/sysdeps/unix/sysv/linux/getpid.c and looks like 可以在此处看到较早的getpid()实现,该实现将其值缓存起来以避免避免多次调用getpid()系统调用: 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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM