YouTube에서 모든 레지스터에 대해 시스템 호출이 있는 비디오를 봤습니다. 그렇다면 이것은 시스템 호출이 레지스터에 저장된다는 의미입니까? 그렇다면 이것이 어떻게 가능합니까? 운영 체제를 제어하는 커널에 의해 액세스된다는 의미입니다. 그렇다면 커널 액세스 레지스터는 어떻게 이루어지며, 어떤 시스템 호출이 어떤 레지스터에 존재하는지 커널이 어떻게 알 수 있을까요?
답변1
"레지스터당 시스템 호출..."은 없습니다.
시스템 호출 인터페이스는 하드웨어 아키텍처에 따라 다릅니다. 일반적으로(거의 항상?) 프로세스는 원하는 시스템 호출에 해당하는 정수를 미리 결정된 레지스터에 저장한 다음 CPU가 실행을 커널의 시스템 호출 처리 코드로 전송하도록 트리거하는 명령을 실행합니다. 커널은 미리 결정된 레지스터를 참조하여 프로세스가 요청하는 시스템 호출을 식별한 다음 적절한 시스템 호출 처리기를 실행합니다.
예를 들어 Linux x86_64 어셈블리의 "hello world" 프로그램을 생각해 보세요.
$ cat hello.s
.text
.global
main:
movq $1, %rax # write() system call number
movq $1, %rdi # first parameter -- standard output file descriptor
movq $msg, %rsi # second parameter -- buffer
movq $14, %rdx # third parameter -- buffer length
syscall # invoke system call
movq $60, %rax # exit() system call number
movq $0, %rdi # first parameter -- exit status
syscall # invoke system call; this will never return
.section .rodata
msg:
.ascii "Hello, world!\n"
$ gcc hello.s
$ ./a.out
Hello, world!
$
x86_64의 경우 시스템 호출 번호는 레지스터에 저장되고 rax
, 시스템 호출의 첫 번째 매개변수는 에 저장되며 rdi
, 시스템 호출의 두 번째 매개변수는 에 저장되고 rsi
, 세 번째 매개변수는 에 저장됩니다 rdx
.
이 예에서는 먼저 값을 1
레지스터에 넣습니다 rax
. 1
시스템 호출의 시스템 호출 번호입니다 write()
. 다음으로, 표준 출력에 대한 파일 설명자이자 첫 번째 인수에 대한 레지스터인 레지스터를 넣 1
습니다 . 그런 다음 두 번째 매개변수인 의 주소를 레지스터에 저장 합니다 . 다음으로 ; 14 에 저장하는 것은 세 번째 인수에 대한 레지스터인 문자열의 길이입니다 . 그런 다음 이 명령어는 CPU가 제어권을 커널 코드로 전송하도록 트리거합니다. 커널은 를 확인하고 이 레지스터(1)의 값을 사용하여 수행(쓰기)할 시스템 호출을 결정한 다음 적절한 핸들러를 호출합니다.rdi
1
rdi
msg
rsi
rsi
14
rdx
rdx
syscall
rax
커널이 시스템 호출을 완료하면 제어권이 사용자 공간 프로세스로 반환됩니다. 이것은 60
레지스터 에 기록합니다 rax
. 다시 말해 rax
이것은 시스템 호출 번호에 대한 레지스터입니다. 여기서는 60
시스템 호출 번호입니다 exit()
. 종료 시스템 호출의 첫 번째(유일한) 매개변수인 0
레지스터 에 기록합니다 . rdi
이 명령어는 다시 syscall
CPU가 제어권을 커널 코드로 전송하도록 트리거합니다. 커널은 를 확인하고 rax
이 레지스터(60)의 값을 사용하여 실행할 시스템 호출(종료)을 결정한 다음 적절한 핸들러를 호출합니다. 핸들러는 exit()
프로세스를 파괴하므로 명령이 반환되지 않습니다.
동일한 작업을 수행하는 예는 32비트 Intel의 경우에도 하드웨어 아키텍처에 따라 다릅니다.