PTRACE(2)

이름
ptrace - 프로세스 추적

사용법
#include <sys/ptrace.h>

long int ptrace(enum __ptrace_request request, pid_t pid, void * addr, void * data)

설명
ptrace 시스템 콜은 부모 프로세스가 다른 프로세스의 실행을 관찰하거나 제어하는 수단을 제공한다. 그리고 해당 프로세스의 코어 이미지와 레지스터들을 검사하고 바꿀수 있는 수단도 제공한다. 이 함수는 우선 breakpoint 디버깅과 시스템 콜 추적을 수행하는데 사용된다.

부모 프로세스는 fork(2)를 호출하여 추적을 시작할수 있으며 exec(2)다음에 (전형적으로) PTRACE_TRACEME를 하는 결과 자식 프로세스를 가질수 있다. 선택적으로, 부모 프로세스는 PTRACE_ATTACH를 사용하여 존재하는 프로세스의 추적을 시작할수 있다.

프로세스를 추적하는 동안, 비록 신호가 무시된다 할지라도 신호가 도착할때마다 자식 프로세스는 멈출것이다. (예외는 SIGKILL이며 이 신호는 자신의 원래 효과를 가진다.) 부모 프로세스는 다음 wait(2)에서 인식되며 멈추는 동안 자식 프로세스를 수정 한다. 그리고 부모 프로세스는 자식 프로세스가 계속하도록 야기하며 옵션으로 전달된 신호를 무시할수 있다.(또는 다른 신호가 대신 전달된다 할지라도)

부모 프로세스가 추적을 마칠때, 부모는 PTRACE_KILL를 가진 자식 프로세스를 종료시키거나 PTRACE_DETACH를 통해 추적을 안하는 모드에서 보통 실행을 계속하도록 한다.

request의 값은 행해져야할 행동을 결정한다:

PTRACE_TRACEME
    이 프로세스는 이 프로세스의 부모에 의해 추적되어 진다는것을 가리킨다. 이 프로세스에 전달된 신호(SIGKILL을 제외)는 프로세스 추적을 멈추고 부모 프로세스가 wait를 인식하도록 야기한다. 또한, 이 프로세스에 의해 exec되는 모든 연속적인 콜들은 새로운 프로그램이 실행하기 전에 제어를 얻을 기회를 보모 프로세스에게 주기 위해 SIGTRAP을 야기한다. 프로세스는 부모 프로세스가 추적을 기대하지 않는다면 이런 요구(request)를 만들어서는 안된다.(pid, addr, 그리고 data 는 무시된다.)

위의 request는 자식 프로세스에 의해서만 사용된다.; 나머지는 부모에 의해서만 사용된다. 다음 request 들중에, pid는 작동하는 자식 프로세스를 가리킨다. PTRACE_KILL외 다른 request들을 위해서 자식 프로세스는 멈추어져야 한다.

PTRACE_PEEKTEXT, PTRACE_PEEKDATA
    자식 프로세스 메모리의 addr위치의 워드(word)를 읽고 ptrace 콜의 결과로써 워드를 반환한다. 리눅스는 text와 data 주소 공간을 분리하지 않는다. 그래서 두개의 request는 현재 같다.(data는 무시된다.)

PTRACE_PEEKUSER
    레지스터와 프로세스에 관한 다른 정보를 가지고 있는 자식 프로세스의 USER 공간에 있는 변위 addr 의 워드를 읽는다. (<linux/user.h> 와 <sys/user.h> 를 참조해라.) word는 ptrace 시스템 콜의 결과로써 반환된다. 전형적으로 변위는 아키텍쳐에 의해 바뀐다 할지라도, 워드-정렬이어야 한다. (data는 무시된다.)

PTRACE_POKETEXT, PTRACE_POKEDATA
    부모 프로세스 메모리에 있는 위치 data에서 자식 프로세스 메모리에 있는 위치 addr으로 word를 복사한다. 위처럼, 두 request는 현재 같다.

PTRACE_POKEUSER
    부모 프로세스 메모리에 있는 위치 data에서 자식 프로세스의 addr USER 영역으로 word를 복사한다. 위처럼, 변위는 전형적으로 워드-정렬이어야 한다. 커널의 무결성을 유지하기 위해, USER 영역에 대한 수정은 허용되지 않는다.

PTRACE_GETREGS, PTRACE_GETFPREGS
    자식 프로세스의 범용 또는 부동 소수점 레지스터들을 각각 부모 프로세스의 data 위치로 복사한다. 이 데이터의 포맷에 관한 정보를 위해서는 <linux/user.h>를 참조해라. (addr는 무시된다.)

PTRACE_SETREGS, PTRACE_SETFPREGS
    부모 프로세스의 data 위치에서 자식 프로세스의 범용 또는 부동 소수점 레지스터들을 각각 복사한다. PTRACE_POKEUSER 처럼, 몇몇 범용 레지스터 수정은 허용되지 않는다.(addr는 무시된다.)

PTRACE_CONT
    중지된 자식 프로세스를 다시 시작한다. 만일 data가 0도 SIGSTOP도 아니라면, 이것은 자식에게 전달되어야 하는 신호로써 해석된다.; 그렇지 않으면, 어떤 신호도 전달되지 않는다. 예를 들어, 부모 프로세스는 자식에게 보내진 신호가 전달될지 아닐지를 제어할수 있다.(addr는 무시된다.)

PTRACE_SYSCALL, PTRACE_SINGLESTEP
    PTRACE_CONT처럼 중지된 자식의 프로세스를 다시 시작한다. 그러나 자식을 다음 엔트리에서 중지하도록 재배열 하거나 시스템 콜에서 종료하거나 단일 명령어 실행후에 종료한다.(보통처럼, 자식은 또한 신호를 받는 즉시 종료한다.) 부모 프로세스의 관점에서, 자식 프로세스는 SIGTRAP을 받아 종료된것처럼 보일 것이다. 예를 들어 PTRACE_SYSCALL를 위한 생각은 처음 중지시 시스템 콜을 위한 인자들을 조사하고 그리고 다른 PTRACE_SYSCALL을 하고 두번째 중지시에 시스템 콜의 반환값을 조사하는 것이다.(addr는 무시된다.)

PTRACE_KILL
    종료하도록 하기 위해 SIGKILL을 자식에게 보낸다.(addr와 data는 무시된다.)

PTRACE_ATTACH
    pid로 지정된 프로세스에 부착시키고, 현재 프로세스의 "child"를 추적하도록 만든다.; 자식 프로세스의 행동은 마치 PTRACE_TRACEME가 하는것처럼 이다. 실제로 현재 프로세스는 대부분의 목적을 위해 자식 프로세스의 부모가 된다. (즉, 자식 이벤트의 통지를 받으며 자식의 부모처럼 ps(1)에 나타난다.) 그러나 자식에 의한 getpid(2)는 여전히 원래 부모의 pid를 반환한다. 자식은 SIGSTOP을 보내지만, 이 시스템 콜에 의해 중지될 필요는 없다.; 자식이 중지되는것을 기다리기 위해 wait를 사용해라. (addr 그리고 data 는 무시된다.)

PTRACE_DETACH
    PTRACE_CONT처럼 중지된 자식을 다시 시작한다. 그러나 우선 프로세스에서 분리하고 PTRACE_ATTACH의 재 부모화 효과와 PTRACE_TRACEME의 결과를 원래대로 되돌린다. 비록 의도하지 않았다 할지라도, 리눅스에서 추적되는 자식은 추적을 시작할때 사용된 방법에 관계없이 이 방식으로 분리될수 있다.(addr는 무시된다.)

주의
ptrace 의 인자가 주어진 원형에 따라 해석됨에도 불구하고, GNU libc는 현재 request인자만이 고정된 가변 함수로써 ptrace 를 선언한다. 이것은 비록 문서화되지 않은 gcc(1) 행동을 사용한다 할지라도 불필요한 추적 인자들은 생략했다는 것을 의미한다.

pid 1인 프로세스 init(8)는 추적되지 않는다.

메모리 내용과 USER 영역의 배치는 OS와 아키텍쳐에 따라 쾌 다르다.

"word"의 크기는 OS에 의해 결정된다.(즉, 32-bit 리눅스에서 word는 32 bits이다.)

추적은 추적되는 프로세스의 의미론에서 약간의 미묘한 차이를 야기한다. 예를 들어, 만일 프로세스가 PTRACE_ATTACH로 부착된다면 이 프로세스의 원래 부모 프로세스는 더이상 이 프로세스가 멈출때 wait를 통해 지시를 받을수 없다. 그리고 새 부모 프로세스가 효과적으로 이 지시를 받을 방법도 없다.

이 페이지는 ptrace 콜이 현재 리눅스에서 작동되는 방법을 문서화 했다. 이 함수의 행동은 Unix의 성향에 따라 상당히 다르다. 어떤 경우, ptrace 의 사용은 OS와 아키텍쳐에 매우 의존적이다.

SunOS 맨 페이지는 "유일하고 애매한"이라고 ptrace 를 기술한다. Solaris 2에 있는 proc-기반 디버깅 인터페이스는 더 강력하고 일반적인 ptrace 기능을 수행한다.

반환값
성공시, PTRACE_PEEK* request들은 다른 request들이 0을 반환하는 동안에 요구된 데이터를 반환한다. 실패시, 모든 request들은 -1을 반환하며 errno(3)는 적당한 값으로 설정된다. PTRACE_PEEK* request가 성공시 반환되는 값이 -1일수도 있기 때문에, 호출자는 request 후에 에러가 발생했는지 아닌지를 결정하기 위해 errno 를 검사해야 한다.

에러

EPERM
    지정된 프로세스는 추적될수 없다. 이것은 부모 프로세스가 충분한 우선권을 가지고 있지 않기 때문이다; 비-루트 프로세스들은 신호를 보낼수 없거나 setuid/setgid로 돌아가는 프로그램의 프로세스들을 추적할수 없다. 선택적으로, 프로세스가 이미 추적되고 있거나 init이다. (pid 1).

ESRCH
    지정된 프로세스가 존재하지 않거나, 현재 호출자에 의해 추적되지 않거나 충지되지 않았다. (추적을 요구하는 request들 때문에)

EIO request 가 무효하거나 부모나 자식의 메모리에 있는 유효하지 않는 영역에서 읽거나 영역으로 쓸려고 하는 시도가 발생했다. 또는 워드-정렬 위반이 있거나 유효하지 않는 신호가 reqeust 재시작동안 지정됐다.

EFAULT
    부모나 자식의 메모리에 있는 유효하지 않은 영역에서 읽거나 쓸려고 하는 시도가 있었다. 아마도 영역이 대응되지 않았거나 접근할수 없기 때문이다. 불행하게도, 리눅스에서 이 에러에 관한 다른 변형은 약간 애매하게 EIO 나 EFAULT를 반환한다.

호환
SVr4, SVID EXT, AT&T, X/OPEN, BSD 4.3

관련 항목
exec(3), wait(2), signal(2), fork(2), gdb(1), strace(1)

역자
정강훈 <skyeyes@soback.kornet.net>, 2000년 9월 21일