vsyscall相较于普通的通过INT 80中断完成的系统调用要更快,因此在对于精度要求很高或者时延要求极低的系统调用适合用vsyscall来实现,但是有一个缺点是vsyscall的系统调用不一定有libc的包裹函数,因此使用上比较复杂,但是内核已经为我们映射好了这些vsyscall到线程的地址空间中。废话少说,看代码:
/*
*Copyright (c) 2013 GuZheng
* Author: GuZheng <cengku@gmail.com>
* Demo of how to use vsyscall func
*
* */
/* vsyscall.h header info
#ifndef _ASM_X86_VSYSCALL_H
#define _ASM_X86_VSYSCALL_H
enum vsyscall_num {
__NR_vgettimeofday,
__NR_vtime,
__NR_vgetcpu,
};
#define VSYSCALL_START (-10UL << 20)
#define VSYSCALL_SIZE 1024
#define VSYSCALL_END (-2UL << 20)
#define VSYSCALL_MAPPED_PAGES 1
#define VSYSCALL_ADDR(vsyscall_nr) (VSYSCALL_START+VSYSCALL_SIZE*(vsyscall_nr))
#endif
*/
#include <asm/vsyscall.h> /*the vsyscall header*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
struct getcpu_cache {
unsigned long blob[128 / sizeof(long)];
};
int main(int argc, char **argv)
{
/*这个内核版本有三个vsyscall实现的系统调用
*gettimeofday,time和getcpu
*因此先定义三个函数原型的指针,用来接受后面找到的函数的映射地址
* */
int (*my_getcpu)(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache);
int (*my_gettimeofday)(struct timeval *tv, struct timezone *tz);
time_t (*my_time)(time_t *calptr);
int node, cpu;
time_t tm;
struct tm *gm;
struct timeval tv;
struct timezone tz;
struct getcpu_cache cache;
/*根据VSYSCALL_ADDR宏,以及三个函数的偏移就可以找到映射的地址了*/
char *addr1 = (char *)VSYSCALL_ADDR(__NR_vgettimeofday);
printf("vgettimeofday addr is%p \n", addr1);
char *addr2 = (char *)VSYSCALL_ADDR(__NR_vtime);
printf("vtime addr is %p \n", addr2);
char *addr3 = (char *)VSYSCALL_ADDR(__NR_vgetcpu);
printf("vgetcpu addr is %p \n", addr3);
my_gettimeofday = (int (*)(struct timeval *, struct timezone *))addr1;
my_gettimeofday(&tv, &tz);
printf("tv_sec:%d\n", tv.tv_sec);
printf("tv_usec:%d\n", tv.tv_usec);
printf("tz_minuteswest:%d\n", tz.tz_minuteswest);
printf("tz_dsttime:%d\n", tz.tz_dsttime);
my_time = (time_t (*)(time_t *))addr2;
time(&tm);
gm = gmtime(&tm);
printf("Now is %d-%d-%d %d:%d:%d\n",
gm->tm_year + 1900,
gm->tm_mon,
gm->tm_mday,
gm->tm_hour,
gm->tm_min,
gm->tm_sec);
my_getcpu = (int (*)(unsigned *, unsigned *, struct getcpu_cache *))addr3;
my_getcpu(&cpu, &node, &cache);
printf("numa node:%d, run on cpu:%d\n", node, cpu);
return 0;
}
方法有点hack,后面补上用内联汇编的正规版本
补上gcc inline asm版本的(你妹的内联asm真心不好写啊!):
/*
*Copyright (c) 2013 GuZheng
* Author: GuZheng <cengku@gmail.com>
* Demo of how to use vsyscall func
*
* */
/* vsyscall.h header info
#ifndef _ASM_X86_VSYSCALL_H
#define _ASM_X86_VSYSCALL_H
enum vsyscall_num {
__NR_vgettimeofday,
__NR_vtime,
__NR_vgetcpu,
};
#define VSYSCALL_START (-10UL << 20)
#define VSYSCALL_SIZE 1024
#define VSYSCALL_END (-2UL << 20)
#define VSYSCALL_MAPPED_PAGES 1
#define VSYSCALL_ADDR(vsyscall_nr) (VSYSCALL_START+VSYSCALL_SIZE*(vsyscall_nr))
#endif
*/
#include <asm/vsyscall.h> /*the vsyscall header*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
struct getcpu_cache {
unsigned long blob[128 / sizeof(long)];
};
int main(int argc, char **argv)
{
/*这个内核版本有三个vsyscall实现的系统调用
*gettimeofday,time和getcpu
*因此先定义三个函数原型的指针,用来接受后面找到的函数的映射地址
* */
int (*my_getcpu)(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache);
int (*my_gettimeofday)(struct timeval *tv, struct timezone *tz);
time_t (*my_time)(time_t *calptr);
int node, cpu;
time_t tm;
struct tm *gm;
struct timeval tv;
struct timezone tz;
struct getcpu_cache cache;
/*根据VSYSCALL_ADDR宏,以及三个函数的偏移就可以找到映射的地址了*/
char *addr1 = (char *)VSYSCALL_ADDR(__NR_vgettimeofday);
printf("vgettimeofday addr is%p \n", addr1);
char *addr2 = (char *)VSYSCALL_ADDR(__NR_vtime);
printf("vtime addr is %p \n", addr2);
char *addr3 = (char *)VSYSCALL_ADDR(__NR_vgetcpu);
printf("vgetcpu addr is %p \n", addr3);
my_gettimeofday = (int (*)(struct timeval *, struct timezone *))addr1;
//my_gettimeofday(&tv, &tz);
__asm__(
"movq %1, %%rdx;"
"movq %0, %%rax;"
"movq %2, %%rcx;"
"movq %%rdx, %%rsi;"
"movq %%rax, %%rdi;"
"call *%%rcx;"
:
: "r"(&tv), "r"(&tz), "r"(addr1)
: "rdx", "rax", "rcx", "rsi", "rdi"
);
printf("tv_sec:%d\n", tv.tv_sec);
printf("tv_usec:%d\n", tv.tv_usec);
printf("tz_minuteswest:%d\n", tz.tz_minuteswest);
printf("tz_dsttime:%d\n", tz.tz_dsttime);
my_time = (time_t (*)(time_t *))addr2;
//time(&tm);
__asm__(
"movq %0, %%rax;"
"movq %1, %%rcx;"
"movq %%rax, %%rdi;"
"call *%%rcx"
:
: "r"(&tm), "r"(addr2)
: "rax", "rcx", "rdi"
);
gm = gmtime(&tm);
printf("Now is %d-%d-%d %d:%d:%d\n",
gm->tm_year + 1900,
gm->tm_mon,
gm->tm_mday,
gm->tm_hour,
gm->tm_min,
gm->tm_sec);
my_getcpu = (int (*)(unsigned *, unsigned *, struct getcpu_cache *))addr3;
//my_getcpu(&cpu, &node, &cache);
__asm__(
"movq %2, %%rdx;"
"movq %0, %%rcx;"
"movq %1, %%rax;"
"movq %3, %%r8;"
"movq %%rcx, %%rsi;"
"movq %%rax, %%rdi;"
"call *%%r8;"
:
: "r"(&cpu), "r"(&node),"r"(NULL),"r"(addr3)
);
printf("numa node:%d, run on cpu:%d\n", node, cpu);
return 0;
}