背景
有些进程需要频繁调用系统时间接口
导致大量的时间卡顿在时间获取的API上
Linux VDSO 提供了用户态的获取时间方法
VDSO 编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| chunli@ubuntu:~/source$ vim vdso_time.c #include <stdio.h> #include <sys/auxv.h> #include <string.h> #include <elf.h>
void* vdso_sym(const char* name) { char* vdso_addr = (char*)getauxval(AT_SYSINFO_EHDR); Elf64_Ehdr* elf_header = (Elf64_Ehdr*)vdso_addr; Elf64_Shdr* section_header = (Elf64_Shdr*)(vdso_addr + elf_header->e_shoff);
char* dynstr = NULL; for (int i=0; i<elf_header->e_shnum; i++) { Elf64_Shdr *ss_ = section_header + elf_header->e_shstrndx; Elf64_Shdr *s = section_header + i; char *name = vdso_addr + ss_->sh_offset + s->sh_name; if (strcmp(name, ".dynstr") == 0) { dynstr = (char*)(vdso_addr + s->sh_offset); break; } }
void *ret = NULL; for (int i=0; i< elf_header->e_shnum; i++) { Elf64_Shdr * s = section_header + i; Elf64_Shdr *ss_ = section_header + elf_header->e_shstrndx; char* name =vdso_addr + ss_->sh_offset + s->sh_name; if (strcmp(name, ".dynsym") == 0) { for (int si=0; si<(s->sh_size/s->sh_entsize); si++) { Elf64_Sym * sym = ((Elf64_Sym*)(vdso_addr + s->sh_offset)) + si; char *name = dynstr + sym->st_name; if (strcmp(name, "clock_gettime") == 0) { return vdso_addr + sym->st_value; } } } } return ret; }
#include <time.h> // for clockid_t, timespec, and CLOCK_REALTIME typedef int (clock_gettime_t)(clockid_t clk_id, struct timespec *tp); int main(int argc, char *argv[]) { struct timespec now; clock_gettime_t *my_clock_gettime = (clock_gettime_t*)vdso_sym("clock_gettime"); my_clock_gettime(CLOCK_REALTIME, &now); printf("now: %ld\n", now.tv_sec); return 0; }
|
演示
1 2 3 4 5 6 7
| chunli@ubuntu:~/source$ chunli@ubuntu:~/source$ gcc vdso_time.c && ./a.out now: 1633682308 chunli@ubuntu:~/source$ gcc vdso_time.c && ./a.out now: 1633682309 chunli@ubuntu:~/source$ `
|