打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
getpid浅析

getpid实验

elf和elftoolchain中提到,通过ltrace浅析查看ps是如何通过调用getpid得到进程id的。
对此,可以发现有多处错误:
1、ltrace可能好像应该也许并然是跟不进syscall的(我们就把ltrace -S当成是strace好吧),这是strace的工作。
2、strace没有发现ps调用了getpid。

但是getpid的确如所想那样位于glibc中,glibc再调用对应的syscall。来看一个例子:

#include <unistd.h>#include <stdlib.h>#include <stdio.h>int main(){    int pid;    pid = getpid();    pid = getpid();    return 0;}

编译后使用strace跟踪,是可以看到getpid syscall的:

...munmap(0xb77d1000, 61281)               = 0getpid()                                = 4827fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0...

这证明了有getpid这么个syscall。同时也发现一个现象(其实是很废话的现象):syscall是由glibc做代理调入内核的。
更重要的,我看到getpid在strace的结果中只出现了一次,但是我在程序中明显地调用了两次,这是为什么呢?看这两份资料:“被glibc 忽悠了"、"...getpid captured only once..."。简言之就是glibc cache了结果,第二次调用就径直用第一次的结果了。
有时候我们不想glibc使用cache的结果,怎么办?
答案就是不用glibc的接口,以获取pid为例,就是不调用getpid。后面再看来如何实现。

再来看这段代码编出来的程序:

$ ldd ./test    linux-gate.so.1 =>  (0xb7726000)    libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb75ce000)    /lib/ld-linux.so.2 (0xb7727000)

可以看到,它的确用到glibc(就是这里的libc)。
然而遗憾的是,上面ldd的结果是不能拿来当证据的。因为:
1、不负责任地说,用户态程序用到glibc的地方太多了,你避无可避,所以证据无效。
2、稍微负责任一点地,可以说程序的入口函数极有可能就是定义在glibc里面的,所以你逃不脱使用glibc啊,所以证据无效。
3、负责任地说,用ltrace -l“验明正身”吧。见下面的操作结果。

去除程序的getpid调用,ldd仍然得到libc。
去除/未去除getpid的ltrace 结果:

// 没有getpid$ ltrace -l /lib/i686/cmov/libc.so.6 ./test                                                                  __libc_start_main(0x8048394, 1, 0xbff8f834, 0x80483c0, 0x80483b0 <unfinished ...>+++ exited (status 0) +++// 有getpid$ ltrace -l /lib/i686/cmov/libc.so.6 ./test__libc_start_main(0x80483c4, 1, 0xbff9b524, 0x8048400, 0x80483f0 <unfinished ...>getpid(                                                                      = 5076getpid(                                                                      = 5076+++ exited (status 0) +++))

发现ltrace飙出两个getpid,而strace只出现一个,这说明getpid是会去判断是否已经有结果在内存中,如有就不进syscall,没有才进syscall。
差点忘记:ldd可以看出glibc是个动态库,我是否可以直接使用工具(如nm、objdump等)去查看其中是否包含getpid呢?这实际上和动态连接库、elf文件的组成相关了。

getpid syscall细节

事实上在了解系统调用的大致流程之后,getpid的流程也不言自明,只不过现在仍不清楚细节。此处也不列出详细的细节,又以时间不足为理由略过吧。
从glibc 2.11的代码看出部分细节,可以发现,这里getpid最终是直接通过汇编代码去触发系统调用的。类似地,我们也可以绕过 libc,直接使用汇编去触发系统调用,内核代码中的syscallN对需要的汇编代码做了一些封装:

// nptl/sysdeps/unix/sysv/linux/getpid.cpid_t__getpid (void){#ifdef NOT_IN_libc  INTERNAL_SYSCALL_DECL (err);  pid_t result = INTERNAL_SYSCALL (getpid, err, 0);#else  pid_t result = THREAD_GETMEM (THREAD_SELF, pid);  if (__builtin_expect (result <= 0, 0))    result = really_getpid (result);#endif  return result;}// INTERNAL_SYSCALL 的一个定义位于 sysdeps/unix/sysv/linux/i386#  define INTERNAL_SYSCALL(name, err, nr, args...)      ({                                                     register unsigned int resultvar;                     EXTRAVAR_##nr                                        asm volatile (                                       LOADARGS_##nr                                        "movl %1, %%eax\n\t"                               "call *%%gs:%P2\n\t"                               RESTOREARGS_##nr                                     : "=a" (resultvar)                                     : "i" (__NR_##name), "i" (offsetof (tcbhead_t, sysinfo))          ASMFMT_##nr(args) : "memory", "cc");                         (int) resultvar; })
--EOF--     
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Linux系统调用全过程详解
Linux中获取某个进程的系统调用以及参数(故障排查案例)
Android执行系统调用的过程
linux系统调用的来龙去脉(下)
Linux 下 strace 命令用法总结
添加一个系统调用,遍历所有进程 - 涌泉*闪闪
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服