VDSO

7492 단어
VDSO(Virtual Dynamically-lined Shared Object)는 커널에서 제공하는 가상입니다.이 파일은 디스크에 있지 않고, 메모리에 있습니다. 메모리는 그것을 하나의 주소 공간에 비추고, 모든 프로그램에 의해 공유되며, 본문 크기는 한 페이지입니다.
$ ldd /bin/lsmod
        linux-vdso.so.1 =>  (0x00007ffff7fdf000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7c42000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ffff7fe0000)

여기 linux-vdso.so.1은 vdso 파일입니다. 현재의 내장 매핑은 프로세스의 무작위 주소화 기능, vdso 파일 및 기타를 제공합니다.so 파일의 맵 주소는 서로 다른offset이 있습니다.
$ echo "0">/proc/sys/kernel/randomize_va_space(root 환경) 또는
$ sudo sysctl -w kernel.randomize_va_space=0
랜덤화된 주소 기능을 닫을 수 있습니다. 다음에 vdso의 내용을/proc/self/mem에서 dd로 반집성하여 연구해 보십시오
$ cat /proc/self/maps
00400000-0040c000 r-xp 00000000 08:01 1430947                            /bin/cat
0060c000-0060d000 rw-p 0000c000 08:01 1430947                            /bin/cat
0060d000-0062e000 rw-p 00000000 00:00 0                                  [heap]
7ffff7a59000-7ffff7bd3000 r-xp 00000000 08:01 801437                     /lib/x86_64-linux-gnu/libc-2.13.so
7ffff7bd3000-7ffff7dd3000 ---p 0017a000 08:01 801437                     /lib/x86_64-linux-gnu/libc-2.13.so
7ffff7dd3000-7ffff7dd7000 r--p 0017a000 08:01 801437                     /lib/x86_64-linux-gnu/libc-2.13.so
7ffff7dd7000-7ffff7dd8000 rw-p 0017e000 08:01 801437                     /lib/x86_64-linux-gnu/libc-2.13.so
7ffff7dd8000-7ffff7ddd000 rw-p 00000000 00:00 0
7ffff7ddd000-7ffff7dfc000 r-xp 00000000 08:01 801438                     /lib/x86_64-linux-gnu/ld-2.13.so
7ffff7e68000-7ffff7fdf000 r--p 00000000 08:01 1832159                    /usr/lib/locale/locale-archive
7ffff7fdf000-7ffff7fe2000 rw-p 00000000 00:00 0
7ffff7ff9000-7ffff7ffb000 rw-p 00000000 00:00 0
7ffff7ffb000-7ffff7ffc000 r-xp 00000000 00:00 0                          [vdso]
7ffff7ffc000-7ffff7ffd000 r--p 0001f000 08:01 801438                     /lib/x86_64-linux-gnu/ld-2.13.so
7ffff7ffd000-7ffff7ffe000 rw-p 00020000 08:01 801438                     /lib/x86_64-linux-gnu/ld-2.13.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

 
$ dd if=/proc/self/mem of=vdso.so bs=4096 skip=$[0x7ffff7ffb] count=1
dd: `/proc/self/mem': cannot skip to specified offset
1+0 records in
1+0 records out
4096 bytes (4.1 kB) copied, 0.00105878 s, 3.9 MB/s
$ file vdso.so
vdso.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, stripped

vdso는 내부 핵 상태의 호출을 사용자 상태의 주소 공간에 비추어 호출 비용을 더욱 줄이고 경로를 더욱 좋게 한다.전통적인 시스템 호출 방식은 소프트 인터럽트 명령 int 0x80을 통해 이루어진 것이다. x86 보호 모드에서INT 인터럽트 명령을 처리할 때 CPU는 먼저 인터럽트 설명자 테이블 IDT에서 대응하는 설명자를 꺼내서 설명자의 종류를 판단한 다음에 설명자의 레벨 DPL과 INT 명령 호출자의 레벨 CPL을 검사한다. CPL<=DPL일 때 INT 호출자의 레벨이 설명자가 지정한 레벨보다 높아야 성공적으로 호출할 수 있다.마지막으로 설명자의 내용에 따라 창고 압축, 점프, 권한 등급 향상을 진행한다.커널 코드가 실행된 후 IRET 명령을 호출하여 반환하고 IRET 명령은 사용자 스택을 복원하고 저급 코드로 돌아갑니다.
사실 시스템 호출이 발생하면 사용자 층에서 내부 핵층으로 들어가는 이 과정은 적지 않은 CPU 주기를 낭비한다. 예를 들어 시스템 호출은 반드시 링3에서 링0으로 들어가야 한다. (내부 핵에서INT 명령을 호출하는 방식을 제외하고 이것은 대부분이 Hacker의 내부 핵 모듈에 속한다) 권한이 향상되기 전과 이후의 등급은 고정적이다. CPL은 3이고 INT 0x80의 DPL도 3이다.이렇게 CPU 검사문 설명자의 DPL과 호출자의 CPL은 전혀 필요하지 않다. 그래서 Intel x86 CPU는 PII 300(Family 6, 모델 3, Stepping 3) 이후 새로운 시스템 호출 명령인sysenter/sysexit를 지원하기 시작했다.sysenter 명령은 링3에서 링0(sysenter 명령은 3, 2, 1 특권 레벨에 사용 가능), sysexit 명령은 링0에서 링3(sysexit는 0 레벨 특권에서만 사용 가능)으로 되돌아오는 데 사용되며, 특권 레벨 검사 처리가 없기 때문에 창고 압축 작업도 없기 때문에 실행 속도가 INT n/IRET보다 훨씬 빠르다.
시스템 호출은 주로 라이브러리 함수로 봉인되어 응용 프로그램에 사용된다. 응용 프로그램이 라이브러리 함수를 호출한 후에glibc 라이브러리에서 내부 호출 시스템 호출 함수를 담당한다. 구형glibc에서 라이브러리 함수는 int 명령을 통해 시스템 호출을 완성하는 것이다. 2.6 이전의 내부 호출이 제공한 시스템 호출 인터페이스도 간단하다. IDT에서INT 0x80의 입구를 제공하면 라이브러리는 중단 호출을 완성할 수 있다.
2.6에서 내장 코드는 int 0x80 인터럽트 방식과sysenter 명령 호출에 대한 지원을 포함하기 때문에 내장 코드는 사용자 공간에 입구 코드를 제공한다. 내장 코드가 시작될 때 CPU 유형에 따라 이 코드가 어떤 시스템 호출 방식을 취할지 결정한다. glibc에 있어 시스템 호출 방식을 고려하지 않고 이 입구 코드를 직접 호출하면 시스템 호출을 완성할 수 있다. 이렇게 하면glibc에 대한 변경을 최대한 줄일 수 있다.glibc의 원본 코드에서 "int $0x80"명령을 "call 입구 주소"로 바꾸면 됩니다. 이 주소가 vsyscall의 첫 번째 주소입니다.
새 버전의 내부 핵에서 관찰 시스템의 호출은syscall() 함수를 이용하여 이루어진 것이다
SYSCALL_DEFINE0(getpid)
{ return task_tgid_vnr(current); }

#define SYSCALL_DEFINE0(name)    asmlinkage long sys_##name(void)
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

#define __SYSCALL_DEFINEx(x, name, ...) asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))

static inline int sys_perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags)
{
 attr->size = sizeof(*attr);
 return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
}

vdso를 보십시오.ld.S 파일, 볼 수 있음
/* Linker script for 64-bit vDSO. We #include the file to define the layout details. Here we only choose the prelinked virtual address. This file defines the version script giving the user-exported symbols in the DSO. We can define local symbols here called VDSO* to make their values visible using the asm-x86/vdso.h macros from the kernel proper. */
#define VDSO_PRELINK 0xffffffffff700000
#include "vdso-layout.lds.S"
/* This controls what userland symbols we export from the vDSO. */
VERSION {
	LINUX_2.6 {
	global:
		clock_gettime;
		__vdso_clock_gettime;
		gettimeofday;
		__vdso_gettimeofday;
		getcpu;
		__vdso_getcpu;
		time;
		__vdso_time;
	local: *;
	};
}
VDSO64_PRELINK = VDSO_PRELINK;
$ cat vdso.dump | grep ">:"
ffffffffff700700 <__vdso_clock_gettime-0x220>:
ffffffffff700920 <__vdso_clock_gettime>:
ffffffffff7009a0 <__vdso_gettimeofday>:
ffffffffff700a30 <__vdso_time>:
ffffffffff700a60 <__vdso_getcpu>:

이렇게 보면 clock 만 호출됩니다gettime, gettimeofday, getcpu,time 이런 시스템이 호출될 때 vdso를 사용한다. 다른 시스템 호출은syscall을 통해 이루어진다. 이유는 빠른 시스템 호출 명령은 중단 명령보다 소모 시간이 적기 때문이다. 그러나 CPU 디자인이 발전함에 따라 앞으로 Intel pentium4와 같은 현격한 차이가 나타나지 않을 것이다. 빠른 시스템 호출 명령은 중단 방식의 시스템 호출보다또한 일정한 한계가 존재한다. 예를 들어 하나의 시스템 호출 처리 과정에서 빠른 시스템 호출 명령을 통해 다른 시스템 호출을 호출할 수 없기 때문에 모든 시스템 호출이 빠른 시스템 호출 명령을 통해 이루어져야 하는 것은 아니다. 예를 들어 복잡한 시스템 호출, 예를 들어 fork, 두 가지 시스템 호출 방식의 시간차와 시스템 호출 자체의 운행 소모 시간을 비교하면 무시할 수 있다.여기서 빠른 시스템 호출 지령 방식을 채택하면 필요없다. 진정으로 빠른 시스템 호출 지령 방식을 사용해야 하는 것은 자체의 운행 시간이 매우 짧고 시간의 정확성에 대한 요구가 높은 시스템 호출이다. 예를 들어 getcpu, gettimeofday 등이다. 따라서 유연한 수단을 취하고 서로 다른 시스템 호출에 대해 서로 다른 방식을 취해야 최적화된 성능과 가장 완벽한 기능을 실현할 수 있다.
References:
1. Linux 2.6 새로운 CPU 고속 시스템 호출 지원http://www.ibm.com/developerworks/cn/linux/kernel/l-k26ncpu/index.html
2. linux에서 vdso와 vsyscall
3. Linux의 VDSO

좋은 웹페이지 즐겨찾기