【发布时间】:2017-09-01 03:49:56
【问题描述】:
情况如下:
我正在尝试执行project 来破解 github 中的内核。内核版本为linux-3.18.6。
QEMU用于模拟环境。
在我的应用程序中,我尝试通过跟随它们来理解系统调用过程。完成我的目标的方式就像shell程序一样。我只是创建一些命令来运行相对系统调用。也许通过图片很简单。 some commands
代码简单如下:
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 直接使用 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。当然是这样的。
奇怪的是,当我使用 gdb 调试 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;
}
如果我得到了错误的代码,请告诉我,tks。
如代码所示,glibc 中不包含 getpid 的定义。看了一些资料,有人说the VDSO...。
请注意,AFAIK 是简单系统调用成本的重要组成部分 正在从用户空间到内核并返回。因此,对于一些系统调用 (可能是 gettimeofday、getpid ...)VDSO 甚至可以避免这种情况 (并且在技术上可能会避免进行真正的系统调用)。
在 man getpid pgae 中:
C 库/内核差异 从 glibc 版本 2.3.4 开始,glibc 的 getpid() 包装函数 缓存 PID,以避免在进程时额外的系统调用 反复调用 getpid()。通常这种缓存是不可见的,但它的 正确的操作依赖于包装函数的支持 fork(2)、vfork(2) 和 clone(2):如果应用程序绕过 glibc 使用 syscall(2) 封装这些系统调用,然后调用 孩子中的 getpid() 将返回错误的值(准确地说:它 将返回父进程的PID)。另请参阅 clone(2) 以了解 解决 getpid() 可能返回错误值的情况,即使 通过 glibc 包装函数调用 clone(2)。
虽然exit这么多解释,但我搞不懂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,正常使用内核源码的方式。
#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; \
})
有人可以清楚地解释 API getpid 的工作原理吗?为什么 getpid 只陷入系统调用 sys_getpid 一次?如果可能,推荐一些参考资料。
感谢您的帮助。
【问题讨论】:
-
您的问题到底是什么?您已阅读手册:glibc 确实缓存了 getpid-syscall 返回的值。显然这个缓存必须在子进程的 fork(2) 之后重新设置。
-
感谢您的回答。使用 dlsym 的 getpid 机制是什么?为什么getpid的实现与其他的不同?这就是我想知道的。谢谢。
标签: linux linux-kernel system-calls glibc vdso