10 장 시스템 콜

26
10 장 장장장 장 장 장 장

Upload: aleron

Post on 15-Jan-2016

83 views

Category:

Documents


0 download

DESCRIPTION

10 장 시스템 콜. 김 태 훈. API 와 시스템 콜의 차이. API 는 특정 서비스를 어떻게 받을지를 지정하는 함수 시스템 콜은 소프트웨어 인터럽트를 통해 커널에 하는 직접적인 요청 libc 에서 정의하는 API 중 일부는 시스템 콜을 호출하는 목적으로만 사용하는 “ 래퍼루틴 ” 이 있다 . 보통 각 시스템 콜마다 대응하는 래퍼 루틴이 하나씩 있으며 , 래퍼 루틴은 애플리케이션에서 사용할 API 를 정의한다 . - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 10 장 시스템  콜

10 장 시스템 콜

김 태 훈

Page 2: 10 장 시스템  콜

API 와 시스템 콜의 차이• API 는 특정 서비스를 어떻게 받을지를 지정하는 함수• 시스템 콜은 소프트웨어 인터럽트를 통해 커널에 하는

직접적인 요청• libc 에서 정의하는 API 중 일부는 시스템 콜을

호출하는 목적으로만 사용하는 “래퍼루틴”이 있다 .• 보통 각 시스템 콜마다 대응하는 래퍼 루틴이 하나씩

있으며 , 래퍼 루틴은 애플리케이션에서 사용할 API를 정의한다 .– 그러나 그 역은 성립하지 않는다 . 즉 , API 는 특정 시스템

콜에 대응할 필요가 없다 .

Page 3: 10 장 시스템  콜

시스템 콜 핸들러와 서비스 루틴• 사용자 모드 프로세스가 시스템 콜을 호출하면 , CPU 는 커널

모드로 전환해서 커널함수를 실행하기 시작한다 .• 커널은 서로 다른 많은 시스템 콜을 구현하므로 사용자 모드

프로세스는 자신이 원하는 시스템 콜을 구별하는 ‘ System Call Number’ 를 매개 변수로 전달해야 한다 .– eax 레지스터를 사용한다 .

• 모든 시스템 콜은 정수 값을 반환한다 .– 양수나 0 은 성공 , 실패는 음수이다 .– 에러가 발생한 경우 errno 변수를 통해 에러 코드는 나타낸다 .– 커널은 error 변수를 사용하지 않음 .– 대신 래퍼 루틴이 시스템 콜에서 돌아온 후 변수를 설정하는 일을

한다 .

Page 4: 10 장 시스템  콜

시스템 콜 핸들러와 서비스 루틴• 시스템 콜 핸들러의 역할

– 레지스터 내용을 커널 모드 스택에 저장한다 . – ‘System Call Service Routine’ 이라는 C 함수를 호출하여

시스템 콜을 처리한다 .– 핸들러에서 빠져 나온다 .

• 커널 모드 스택에 저장된 값을 레지스터들에 로딩하고 , CPU 는 커널모드에서 사용자 모드로 다시 돌아온다 .

Page 5: 10 장 시스템  콜

시스템 콜 핸들러와 서비스 루틴• 그림 10-1

– SYSCALL, SYSEXIT 는 사용자 모드에서 커널 모드로 , 커널 모드에서 사용자 모드로 CPU 를 전환하는 실제 어셈블리 명령어가 있는 곳을 나타낸다 .

• 커널은 시스템 콜 번호와 해당 시스템 콜 서비스 루틴을 서로 연계하기 위해 ‘ System Call Dispatch Table’ 을 사용한다 .– 이 테이블은 sys_call_table 배열에 들어 있다 .– NR_syscalls 개의 엔트리를 포함한다 .– __NR_syscall_max 개 (279 개 )– 이 값은 구현 가능한 시스템 콜의 최대 개수로 , 실제 구현하고

있는 시스템 콜 수를 의미하지는 않는다 .

Page 6: 10 장 시스템  콜

시스템 콜로의 진입과 복귀• 시스템 콜을 호출하는 두 가지 방법– int $0x80 어셈블리 명령을 수행한다 .

• 과거 버전에서 사용자 모드에서 커널 모드로 전환하는 유일한 방법

– sysenter 어셈블리어를 수행한다 .• 2.6 커널 이상 부터 지원함 .• 펜티엄 2 프로세서 부터 지원함 .

• 사용자 모드로 전환하는 두 가지 방법– iret 어셈블리 명령어를 수행한다 .– sysexit 어셈블리 명령어를 수행한다 .

Page 7: 10 장 시스템  콜

시스템 콜로의 진입과 복귀• 두 가지 방법이 혼재함으로 인해 고려해야 할 사항 .– 커널은 int $x80 명령어만 사용하는 라이브러리와

sysenter 를 사용하는 라이브러리를 동시에 지원해야 한다 .

– sysenter 를 사용하는 라이브러리는 int $x80 만 지원하는 커널에 대처해야 한다 .

– 커널과 표준 라이브러리는 sysenter 명령어를 포함하지 않는 구식 프로세서와 sysenter 명령어를 포함하는 최신 프로세서에서 모두 실행될 수 있어야 한다 .

Page 8: 10 장 시스템  콜

int $0x80 명령어를 통한 시스템 콜 발생

• trap_init 함수는 128 번 (0x80) 에 해당하는 ‘ Interrupt Descrip-tor Table’ 엔트리를 다음과 같이 설정한다 .– set_system_gate(0x80, &system_call);– 이는 gate descriptor의 필드를 다음 값으로 지정한다 .

• 세그먼트 셀렉터– __KERNEL_CS

• 오프셋– system_call() 예외 핸들러에 대한 포인터이다 .

• 종류 (Type)– 15 로 설정 .– 예외 종류가 Trap 이고 , 해당 핸들러는 마스크 가능한 인터럽트를 금지하지 않음 .

• DPL(Descriptor Privilege Level)– 3 으로 설정– 사용자 모드의 프로세스가 예외 핸들러를 호출할 수 있게 한다 .

• 사용자 모드 프로세스가 int $0x80 명령어를 발생시킬 때 CPU 는 커널 모드로 전환하고 system_call 주소에서 명령어를 수행한다 .

Page 9: 10 장 시스템  콜

system_call() 함수• 시스템 콜 번호와 함께 예외 핸들러가 사용할

수 있도록 eflags 와 cs, eip, ss, esp 레지스터를 제외한 모든 CPU 레지스터를 스택에 저장한다 .

• SAVE_ALL 매크로는 ds 와 es 를 각각 커널 데이터 세그먼트의 세그먼트 셀렉터로 설정한다 .

Page 10: 10 장 시스템  콜

system_call() 함수• ebx 레지스터에 현재 프로세스의 thread_info 자료

구조의 주소를 저장한다 .• thread_info 자료 구조의 flags 필드를 검사하여 디버거의

추적이 있는지 확인한다 .– TIF_SYSCALL_TRACE, TIF_SYSCALL_AUDIT– 이 경우 , do_syscall_trace() 함수를 시스템 콜 실행 전후에

호출한다 .

• 사용자 모드 프로세스가 전달한 시스템 콜 번호가 올바른지 검사한다 .

• 시스템 콜 번호가 잘못된 것이면 eax 를 저장했던 스택위치에 – ENOSYS 값을 저장한 후 resume+userspace() 를 호출한다 .

Page 11: 10 장 시스템  콜

sysenter 명령어를 통한 시스템 콜 발생• low-latency system call

• 다음의 세 가지 레지스터를 사용한다 .– SYSENTER_CS_MSR

• 커널 코드 세그먼트의 세그먼트 셀렉터– SYSENTER_EIP_MSR

• 커널 진입점의 선형 주소– SYSENTER_ESP_MSR

• 커널 스택 포인터

• sysenter 명령어 수행시 ,– SYSENTER_CS_MSR의 내용을 cs 레지스터에 복사한다 .

• 커널 영역의 코드 세그먼트 정보– SYSENTER_EIP_MSR의 내용을 eip 레지스터에 복사한다 .

• sysenter_entry() 함수의 주소– SYSENTER_ESP_MSR의 내용을 esp 레지스터에 복사한다 .

• TSS(Task State Segment)의 주소– SYSENTER_CS_MSR의 값에 8을 더한 후 ss 레지스터에 값을 로딩한다 .

Page 12: 10 장 시스템  콜

vsyscall

• 커널이 정의한 실행 코드를 사용자 공간에서 참조할 수 있도록 하기 위해 library 에서 실행시키는 것 .– 실행하고 있는 CPU 에 따라 int 0x80 을 사용하거나 ,

sysenter 명령을 사용한다 .– 장점 : 커널 호출이 불필요한 경우 , 실제 시스템 콜을

발생하지 않고 처리를 끝낸다 .• 예 ) gettimeofday

• 표준 라이브러리의 래퍼 루틴이 시스템 콜을 호출할 때는 항상 __kernel_vsyscall() 함수를 호출한다 .

Page 13: 10 장 시스템  콜

vsyscall

• CPU 가 sysenter 를 지원하지 않는 경우

• CPU 가 sysenter 를 지원하는 경우

__kernel_vsyscall: int $0x80 ret

__kernel_vsyscall: pushl %ecx pushl %edx pushl %ebp movl %esp, %ebp sysenter

Page 14: 10 장 시스템  콜

시스템 콜로의 진입1. eax 레지스터에 시스템 콜 번호를 로딩한다 .2. __kernel_vsyscall() 함수는 ecx, edx, ebp 레지스터 값을 스택에 push

한다 .3. esp(사용자 스택 포인터 )를 ebp에 복사한다 .4. sysenter 명령어를 수행한다 .5. CPU는 현재 모드를 커널 모드로 전환한다 .6. SYSENTER_EIP_MSR 레지스터가 가리키는 sysenter_entry()함수를

호출한다 .1. 커널 스택 포인터를 설정한다 .2. 지역 인터럽트를 허용한다 .3. 다음을 스택에 push한다 .

1. 사용자 데이터 세그먼트 (__USER_DS)2. 사용자 스택 포인터3. 사용자 코드 세그먼트의 세그먼트 셀렉터4. 리턴시 수행될 명령어 주소

4. 래퍼 루틴이 전달한 레스터 초기 값을 ebp 레지스터에 복구한다 .

Page 15: 10 장 시스템  콜

시스템 콜에서 빠져 나오기• eax 레지스터에 시스템 콜의 반환 값을 저장한다 .• 지역 인터럽트를 금지한다 .• 현재 thread_info 의 flag 를 검사한다 .

ebp

edx

ecx

sysen-ter

sy-sexit

Page 16: 10 장 시스템  콜

sysexit 명령어• 커널 모드에서 사용자 모드로 전환한다 .• sysexit 명령어 수행시 CPU 는 ,– SYSENTER_CS_MSR 레지스터 값에 16 을

더해서 cs 레지스터에 로딩한다 .• 사용자 코드의 세그멘트 셀렉터 로딩

– edx 레지스터 값을 eip 레지스터에 복사한다 .– SYSENTER_CS_MSR 레지스터에 24 를 더해서

ss 레지스터에 로딩한다 .• 사용자 데이터 세그먼트의 세그먼트 셀렉터 로딩

– ecx 레지스터 값을 esp 레지스터에 복사한다 .

Page 17: 10 장 시스템  콜

SYSENTER_RETURN 코드• sysenter 로 시작된 시스템 콜이 iret 이나 sy-

sexit 명령에 의해 종료중일때 수행된다 .• vsyscall 페이지에 저장되어 있다 .

• 스택에 저장된 레지스터의 값을 복구한 후 , 표준 라이브러리의 래퍼 루틴으로 제어권을 반환한다 .

SYSENTER_RETURN: popl %ebp popl %edx popl %ecx ret

Page 18: 10 장 시스템  콜

매개 변수 전달• 매개변수의 형식

– 실제 값 (숫자 ), 사용자 주소 공간의 변수 , 사용자 공간의 자료구조 주소

• 필수 매개 변수– eax 레지스터로 전달하는 시스템 콜 번호– 예 ) fork()를 호출하면 , int $0x80 or sysenter 를 실행하기 전에 eax

레지스터를 __NR_fork로 설정한다 .

• 일반적인 C프로그램에서 매개변수는 스택을 통하여 전달된다 .– 그러나 시스템 콜은 모드가 변경되는 특별한 함수이기 때문에 양쪽 어느 곳의 스택도

사용할 수 없다 .

• 시스템 콜 서비스 루틴 수행 전 매개 변수를 CPU 레지스터에 저장한다 .• 저장된 매개 변수를 커널 모드의 스택으로 복사한다 .

– 사용자 모드의 스택을 커널 모드의 스택으로 직접 복사는 난해하므로 사용하지 않는다 .

Page 19: 10 장 시스템  콜

매개 변수 전달• 매개 변수를 레지스터로 전달하기 위한 두가지 조건

– 각 매개 변수는 레지스터 크기인 32 비트를 넘을 수 없다 . : 32비트 CPU 의 경우에만 해당

– 레지스터의 수는 제한적이므로 6 개를 넘길 수 없다 .(eax 포함 )

• 6 개를 초과하는 매개 변수 전달법– 하나의 레지스터를 매개 변수 값을 저장하고 있는 프로세스 주소

공간 내의 메모리 영역을 가르키게 한다 .

• 사용되는 레지스터의 종류– eax, ebx, ecx, edx, esi, edi, ebp

Page 20: 10 장 시스템  콜

매개변수 확인• 공통적인 검사 유형–매개 변수가 주소인 경우 커널은 이 주소가

프로세스 주소 공간 내에 있는지 검사해야 한다 .• 선형 주소가 프로세스 주소 공간에 속하는지 , 속하면

주소를 포함하는 메모리영역이 해당 접근 권한에 있는지 확인한다 .– 초기 커널의 형태 : 전달된 매개 변수를 모두 검사하므로

시간이 오래 걸림 .

• 선형 주소가 PAGE_OFFSET 보다 낮은지 확인한다 .– 즉 , 커널용으로 예약한 주소 범위에 들어가지 않는지 ..– 커널 2.2 이후 부터 사용

Page 21: 10 장 시스템  콜

프로세스 주소 공간 접근• 프로세스 주소 공간에 접근을 위한 매크로 제공

함수명 / 매크로명 읽기 / 쓰기 읽기 / 쓰기 사이즈 접근 범위 체크

get_user 읽기 1, 2, 4 바이트 정수값 있음

__get_user 1, 2, 4 바이트 정수값 없음

put_user 쓰기 1, 2, 4 바이트 정수값 있음

__put_user 1, 2, 4 바이트 정수값 없음

copy_from_user 읽기 임의 사이즈 있음

__copy_from_user 읽기 임의 사이즈 없음

copy_to_user 쓰기 임의 사이즈 있음

__copy_to_user 쓰기 임의 사이즈 없음

strncpy_from_user 읽기 임의사이즈 (NULL 종료 ) 있음

__strncpy_from_user 읽기 임의사이즈 (NULL 종료 ) 없음

strlen_user 읽기 문자열 길이 리턴 . 복사하지 않음

있음

strnlen_user 읽기 문자열 길이 리턴 . 복사하지 않음 . n까지 카운트

있음

clear_user 쓰기 (zero clear) 임의 사이즈 있음

__clear_user 쓰기 (zero clear) 임의 사이즈 없음

Page 22: 10 장 시스템  콜

동적 주소 검사 – 수선 코드 (The Fix-up Code)

• 커널 모드에서 페이지 폴트가 일어날 수 있는 네 가지 경우–해당 페이지 프레임이 존재하지 않거나 읽기 전용

페이지에 쓰려고 한 경우 .–해당 페이지 테이블 엔트리가 초기화되지 않은 경우–커널함수의 버그 또는 일시적인 H/W 에러–시스템 콜에 전달한 주소의 메모리 영역에 읽기 /쓰기를 할 때 해당 주소가 프로세스 주소 공간에 들어 있지 않은 경우

Page 23: 10 장 시스템  콜

예외 테이블• 사용자 공간에 접근 하는 명령어 개수는 매우 적다 .• 프로세스 주소 공간에 접근하는 각 커널 명령의 주소를 ‘예외 테이블

(Exception Table)’ 구조체로 모은다 .– 만일 페이지 폴트 예외가 발생하면 , do_page_fault 핸들러를 이용해 예외 테이블을

검사한다 .– 예외를 일으킨 명령의 주소를 찾으면 시스템 콜 매개 변수가 원인이 되고 , 그렇지

않으면 다른 버그 때문이다 .

• 예외 테이블의 엔트리– insn : 프로세스 주소 공간에 접근하는 명령어의 주소– fixup : 예외가 발생할 때 호출할 어셈블리 코드 주소

struct exception_table_entry{ unsigned long insn, fizup};

Page 24: 10 장 시스템  콜

예외 테이블• 예외 테이블은 커널을 컴파일할 때 링크된다 .– 예외 테이블 항목은 __ex_table 섹션에 저장된다 .– 예외가 발생했을 때 처리 코드는 커널 코드 세그먼트 중

.fixup 섹션에 저장된다 .

• search_exception_table() 함수– 예외 테이블에 지정한 주소가 있는지 찾는다 .– 주소가 테이블에 있으면 해당

exception_table_entry 의 포인트를 ,– 없으면 0 을 반환한다 .

Page 25: 10 장 시스템  콜

커널 래퍼 루틴• 시스템 콜은 주로 사용자 모드 프로세스에서

사용한다 .–하지만 커널스레드에서도 사용할 수 있도록 래퍼

루틴 (Wrapper Routine) 선언을 위해 _syscall0 ~ _syscall6 까지 매크로를 선언하고 있다 .

–매크로의 숫자는 시스템 콜이 사용하는 매개변수의 개수이다 .• 여기서는 6 개가 넘는 매개변수는 사용할 수 없다 .

Page 26: 10 장 시스템  콜

커널 래퍼 루틴• 각 매크로는 (2 + 2xn) 개의 매개변수를 받는다 .– 예

• _syscall0(int, fork)– int : 반환타입– fork : 시스템 콜 이름

• _syscall3(int, write, int, fd, const char*, buf, un-signed int, count)– int : 반환타입– write : 시스템 콜 이름– int : type, fd : 첫번째 매개 변수– const char * : type, buf : 두번째 매개 변수– unsigned int : type, count : 세번째 매개 변수– int write(int fd, const char *buf, unsigned int count)