- 서버 만들기 (TCP/SOCK_STREAM)
- bind()할 때 "address already in use" 메시지가 나왔는데 무슨의미인가?
- 왜 소켓을 닫을 수 없는가?
- 내 서버를 데몬으로 만들려면?
- 한번에 여러 포트로부터 listen할 수 있는가?
- SO_REUSEADDR 가 정확히 뭐하는 것인가?
- SO_LINGER 가 정확히 뭐하는 것인가?
- SO_KEEPALIVE 가 정확히 뭐하는 것인가?
- 포트 번호를 1024 이하로 bind()할 수 있는 방법은?
- 클라이언트의 주소와 호스트 이름을 알아내는 방법은?
- 나의 서버를 위한 포트번호를 어떻게 선택 해야 하는가?
- SO_REUSEADDR 와 SO_REUSEPORT 의 차이는 무엇인가?
- multi-homed server를 만드는 방법은?
- 한 번에 한 글짜식 읽는 방법은?
- 서버에서 exec()를 실행하고 그것에 소켓 I/O를 attach시키려고 하고 있다. 그런데 그것
을 통하여 모든 그 데이터를 얻지 못하고 있다. 왜 그런가?
4.1. bind()할 때 "address already in use" 메시지가 나왔는데 무슨의미인가?
이건 그 주소가 이미 사용중일 때 발생한다. 가장 일반적인 이유는 서버를 중지시킨 후에 그것 을 곧바로 다시 실행했을 경우에 그런일이 발생하는 경우가 있다. 첫 번째 만들어진 서버에 의해 사용된 소켓이 여전히 active상태이기 때문이다. 이것은 2.7에 더 깊이 설명되고 있는 TIME_WAIT와 2.5."어떻게 소켓을 제대로 닫을 수 있는가"를 참조하라.
close() 시스템 콜을 발생시키면, 여러분은 소켓 자체가 아니라 소켓에 대한 인터페이스를 닫게 되는 것이다. 소켓을 닫는 것은 커널이 하는 일이다. 때때로 기술적인 이유로 소켓은 여러분이 그것을 닫은 후에도 계속 살아 있는 경우가 있다. 소켓이 서버쪽에서 TIME_WAIT상태로 들어가 보통 몇분간 살아 있는건 보통이다. 표준에 의하면 보통 4분이다. 리눅스에서는 약 2분정도이다. 이것은 2.7 "TIME_WAIT상태에 대해 설명해 달라"를 참고하기 바란다.
두가지 방법이 있는데, 첫 번째는 inetd로 하여금 어려운일을 맏기는 것이고. 두 번째는 힘들일 까지 모두 여러분 자신이 하는 것이다.
만약 inetd를 사용한다면. 여러분은 단지 stdin, stdout, 또는 stderr을 여러분의 소켓을 위해 사용 하고, 이것을 여러분의 코드에서 이것들을 프로그램에서 소켓처럼 사용하면 된다.( stdin을 비롯한 이 세가지는 실제 소켓으로부터 dup()를 호출함으로써 만들 수 있다.) inetd프로세스는 프로그램이 다 끝났을 때 소켓을 닫아주는 일까지 해준다.
만약 자신의 서버를 만들기 원한다면, "Unix Network Programming"을 보라. 그리고 만약 SIGPIPE를 무시하는 코드를 추가하길 원할 수도 있다. 왜냐하면 만약 이 시그널을 그냥 두면 프로그램이 exit될 수도 있기 때문이다.(이것에 대해 알려준 ingo@milan2.snafu.de 에게 감사한다)
나는 이 모든 것들을 GNU C library 매뉴얼로 부터 얻었다. 여기에 몇몇 코드가 있다. 필요하면 이것을 여러분 코드에 삽입할 수도 있을 것이다.
#include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <sys/wait.h> /* 전역변수 */ volatile sig_atomic_t keep_going = 1; /* 제어 프로그램 종료 */ /* 함수 프로토타입 */ void termination_handler (int signum); /* 종료전에 clean up 한다. */ int main (void) { ... if (chdir (HOME_DIR)) /* 데이터파일을 포함하고 있는 디렉토리로 이동 */ { fprintf (stderr, "´%s': ", HOME_DIR); perror (NULL); exit (1); } /* 데몬이 된다. */ switch (fork ()) { case -1: /* fork 실패*/ perror ("fork()"); exit (3); case 0: /* 자식, 프로세스가 데몬이 된다: */ close (STDIN_FILENO); close (STDOUT_FILENO); close (STDERR_FILENO); if (setsid () == -1) /* 새로운 세션 요청 (job control) */ { exit (4); } break; default: /* 호출 프로세스로 부모는 리턴한다 */ return 0; } /* 종료전에 clean up하기 위해 시그널 핸들러를 설립한다. */ if (signal (SIGTERM, termination_handler) == SIG_IGN) signal (SIGTERM, SIG_IGN); signal (SIGINT, SIG_IGN); signal (SIGHUP, SIG_IGN); /* 주 프로그램 루프 */ while (keep_going) { ... } return 0; } void termination_handler (int signum) { keep_going = 0; signal (signum, termination_handler); }
4.4. 한번에 여러 포트로부터 listen할 수 있는가?
이것을 위한 가장 좋은 방법은 select()를 사용하는 것이다. 이것을 이용하면 커널은 언제 소켓이 사용가능한지를 알려 준다. 이 호출을 이용하면 다중 소켓 에서 하나의 프로세스를 통하여 i/o를 하는게 가능하다. 만약 소켓 4,6,10로부터의 연결을 기다리기를 원한다면 다음의 코드를 실행해야 한다.
fd_set socklist; FD_ZERO(&socklist); /* 처음에 구조체를 clear시킨다 */ FD_SET(4, &socklist); FD_SET(6, &socklist); FD_SET(10, &socklist); if (select(11, NULL, &socklist, NULL, NULL) < 0) perror("select");
커널은 (select()의 첫 번째 파라미터인) 11보다 작은 파일 디스크립터가 우리의 쓰기 가능 상태가 되는 socklist의 구성원이 되자마자 우리에게 그것을 알려 준다. 자세한 것은 selec()의 매뉴얼 페이지를 보라.
4.5. SO_REUSEADDR 가 정확히 뭐하는 것인가?
이 소켓 옵션은 커널에게 비록 이 포트가 busy상태일 지라도 그것을 계속해서 사용하도록 하게 한다. 이것은 서버가 셧다운 되고 소켓이 그것의 포트에서 아직 active상태인 동안에 바로 재 구동 되었을 경우에 유용하다. 하지만 만약 소켓이 active된 상태에서 어떤 예기치 않은 데이터가 들어오면 이것이 서버를 혼란스럽게 할 수 있다라는 점을 알아 둬야 한다.
"소켓은 5개의 튜플(proto, local addr, local port, remote addr, remote port)이고, SO_REUSEADDR는 단지 로컬 주소를 다시 사용할 수 있으며. 이 5튜플은 계속해서 유일해야 한다"라는 것을 Michael Hunter (mphunter@qnx.com)이 지적했다.
이것은 사실이고, 이것은 매우 예기치 않은 데이터가 서버에 거의 들어오지 않을 것이란 것의 이유가 된다. 위험한 것은 이 5 튜플이 네트웍에 여전히 떠다니고, 여기저기 튀어 다니는 동안 새로운 연결이 같은 기계의 클라이언트로 부터 들어오게 되면 똑같은 리모트의 포트를 얻게된다. 이것은 2.7의 "TIME_WAIT 상태에 대해 설명해 달라" 부분에 설명되어 있다.
몇몇 유닉스에서 이것은 아무 의미도 없다. 또 다른 유닉스에서 이것은 커널로 하여금 tcp연결을 정확하게 닫게 하는 것 대신에 tcp연결을 취소(abort)하게 명령한다. 이것은 위험한 일이다. 만약 여러분이 이것에 대해 잘 이해가 안가면 2.7장을 보라.
4.7. SO_KEEPALIVE 이 정확히 뭐하는 것인가?
Andrew Gierth (andrew@erlenstar.demon.co.uk):
SO_KEEPALIVE옵션은 만약 오랜시간동안(default로 2시간 이상) 패킷이 수신 또는 송신되지 않을 때, 패킷으로 하여금 리모트 시스템으로 전달되도록 한다. 이 패킷은 peer가 ACK 응답을 만들도록 설계 되었다. 이것은 unreachable된 peer를 알아 낼 수 있도록 해준다.(예를 들어 파워가 꺼져 버렸거나 네트웍이 끊겼을 때). 2.8 에 이것에 대한 더 상세한 내용이 있다.
2시간이라는 수치는 RFC1122의 "Requirements for Internet Hosts"에 근거한 숫자이다. 설정되어야 할 중요한 숫자이지만 자주 난 이것이 어려운 일임을 발견하게 된다. 계속 살아 있어야 할 시간이 각 연결마다 설정되는 것이 가능 한 유일한 것이 SVR4.2이다.
4.8. 포트 번호를 1024 이하로 bind()할 수 있는 방법은?
Andrew Gierth (andrew@erlenstar.demon.co.uk):
1024보다 작은 수치의 포트 번호에 접근하도록 제한 하는 것은 유닉스의 보안 체계에 안좋은 영향을 주는 아주 않좋은 방법이다. 이 의도는 서버가(예를 들어 rlogind, rshd) 클라이언트의 포트번호를 체크할 수 있도록 하고, 만약 그것이 1024보다 작으면 그 요청은 아마도 클라이언트쪽에서 인증된 것임을 가정한다.
실제적인 이것의 요지는 포트번호를 1024보다 작게 바인딩 하는 것은 프로세스가 유효한 UID==root를 갖도록 프로세스에게 남겨둔다(reserved)는 것이다.
이것은 때로 그 자체가 보안의 문제를 만들 수 있다. 예를 들어 서버프로세스가 잘 알려진 포트로 바인딩 할 필요가 있고, root 접근이 필요한 경우는 아닐 경우(예를 들어 news server)이다. 이것은 단순하게 소켓을 바인드 하고 실제 사용자 ID를 반환하는 작은 프로그램을 만들어 실제 서버에서 exec()를 실행함으로써 해결 될 수 있다. 이 프로그램은 root 소유로 setuid될 수 있다.
4.9.클라이언트의 주소나 이름을 나의 서버가 알 수 있는 방법이 있는가?
Andrew Gierth (andrew@erlenstar.demon.co.uk):
접속을 accept()한 후에, getpeername()을 사용하여 클라이언트의 주소를 얻을 수 있다. 클라이언트의 주소는 물론 accept()로부터도 리턴된다. 그러나 이것에 대한 accept()호출이 잘 동작하기 전에 이것은 주소 길이 파라메터를 초기화 하는데 필요한 것일 뿐이다.
Jari Kokko (jkokko@cc.hut.fi)은 클라이언트 주소를 알아내는데 다음의 코드를 사용할 것을 추천하였다.
int t; int len; struct sockaddr_in sin; struct hostent *host; len = sizeof sin; if (getpeername(t, (struct sockaddr *) &sin, &len) < 0) perror("getpeername"); else { if ((host = gethostbyaddr((char *) &sin.sin_addr, sizeof sin.sin_addr, AF_INET)) == NULL) perror("gethostbyaddr"); else printf("remote host is '%s'\n", host->h_name); }
4.10. 나의 서버를 위한 포트번호를 어떻게 선택 해야 하는가?
등록된 포트할당 리스트는 STD2나 RFC1700에 있다. 거기에서 아직 등록되지 않은 것을 선택하면 된다. 그리고 여러분의 시스템의 /etc/services에 없는 것으로 선택하면 된다. 다른 등록되지 않은 다른 서버의 포트 번호를 그 서버의 사용자가 선택할 수 있도록 해주기 위해 포트번호 설정을 사용자가 할 수 있도록 해 주는 것도 좋은 방법이다. 가장 좋은 방법은 서비스 이름을 하드코딩 하고 getservbyname()을 사용하여 실제 포트번호를 찾도록 하는 것이다. 이 방법은 사용자가 자신들의 포트 번호를 단지 /etc/services파일을 바꿈으로 해서 마음대로 바꿀 수 있다는 잇점이 있다.
4.11. SO_REUSEADDR 와 SO_REUSEPORT의 차이는 무엇인가?
SO_REUSEADDR는 여러분의 서버로 하여금 TIME_WAIT상태에 있는 주소를 바인드 하게 해준다. 이것은 같은 주소로 하나 이상의 서버가 바인드 하는 것를 허용하지 않는다. 이것은 보안의
위험성 있다고들 말한다. 왜냐하면 다른 서버도 INADDR_ANY에 반대되는 것 처럼 주소를 지정하여 같은 포트로 바인드 할 수 있기 때문이다. SO_REUSEPORT는 하나의 포트로 여러 프로세
스가 바인드 하는 것을 허용한다.
Richard Stevens (rstevens@noao.edu):
이것은4.4BSD의 멀티캐스팅 코드에서 새로 생긴 flag이다. (비록 이 코드가 다른 어떤 곳으로부터 생긴 것일지라도, 그래서 이 새로운 SO_REUSEPORT flag를 누가 개발했는지는 몰라도)
이 flag는 여러분들이 이미 사용중인 하나의 포트를 재바인드 할 수 있도록 해준다. 그러나 모든 이 포트의 사용자들이 그 flag를 지정할 경우에만이다. 이것은 멀티캐스팅 어플리케이션을 위한 의도라고 생각된다. 왜냐하면 만약 여러분이 같은 어플리케이션을 하나의 호스트에서 실행할 때 모든 어플리케이션은 같은 포트를 바인드할 필요가 있기 때문이다. 그러나 이 flag는 다른 용도도 가지고 있다. 예를 들어 다음의 이 사람이 올린 글을 보면...
Stu Friedberg (stuartf@sequent.com):
SO_REUSEPORT는 ftpd의 데이터 연결 설정에서 try-10-times-to-bind 해킹을 제거하기
위해 유용하다. SO_REUSEPORT가 없으면 오직 하나의 ftpd thread만이 준비하고 있는 클라이언트쪽의 TCP(lhost, lport, INADDR_ANY, 0)와 연결 할 수 있다. 이런 많은 로드가 발생하는 상황에서는 try-10-times해킹에서보다 더 많은 충돌 thread들이 발생한다. SO_REUSEPORT로 모든
것이 잘 돌아가고, 해킹은 무의미 하게 된다.
나는 또한 DEC OSF가 이 flag를 지원한다는 말을 들었다. 또한 4.4BSD하에서 만약 여러분이 멀티캐스트 주소를 바인딩 하는지, 그리고 SO_REUSEADDR이 SO_REUSEPORT와 같다고 생각되는지를 주의하라.(p. 731 of "TCP/IP Illustrated, Volume 2"). 나는 솔라리스하에서 여러분들은 단지 SO_REUSEPORT를 SO_REUSEADDR로 바꾸어 놓을 수 있다고 생각한다.
Stevens이 보낸 글중 약간 고친것:
기본적으로 SO_REUSEPORT는 멀티캐스팅이 BSD에 추가되면서 생긴 것이다. 비록 그것이 Steve Deering코드에서 사용되지 않을 지라도. BSD 파생시스템에는 OSF 도 해당된다. 그래서 SO_REUSEPORT는 여러분들이 같은 주소와 포트를 바인드 할 수 있도록 해준다. 그러나 모든 바인더가 그것을 지정 했을 경우에만 그렇다는 얘기다. 그러나 멀티캐스팅 주소를 바인드 할 때, SO_REUSEADDR은 완전히 SO_REUSEPORT 와 같다.(P 731, "TCP/IP Illustrated, Volume 2". 그래서 멀티캐스팅 어플리케이션의 호환성을 위해 나는 항상 SO_REUSEADDR를 사용한다.
4.12. 어떻게 multi-homed 서버를 만들 수 있는가?
원래의 질문은 사실 Shankar Ramamoorthy(shankar@viman.com)이 한거다.
나는 multi-homed호스트에서 사용하길 원한다. 그 호스트는 두 네트웍의 부분에 물려 있으며, 두 개의 랜카드가 있다. 나는 이 기계에서 미리 정해진 포트 번호에 서버를 실행 시키고 싶다. 나는 각각의 이더넷 클라이언트들이 그 포트로 브로드캐스트 패킷을 보내고 서버가 그것을 받을 수 있도록 하고 싶다.
Andrew Gierth (andrew@erlenstar.demon.co.uk):
이 시나리오에서 당신의 첫 번째 질문에서 패킷이 오는 서브넷이 어느 서브넷인지 알아야 한다. 나는 이것이 정말 모든 경우에 결정될 수 있는것인지 전혀 확신할 수가 없다.
만약 당신이 주의깊게 할 수 있다면, 당신이 필요한 것은 INADDR_ANY로 하나의 소켓 바운드를 하는 것이다. 그것은 모든 것을 매우 간단하게 만들어 준다.
만약 당신이 주의깊게 할 수 있다면, 다중 소켓을 바인드 해야 한다. 이것을 분명히 당신의 코드에 적용시켜라.
나는 아래와 같은 것이 제대로 동작하길 바란다. 그럴까요? 그건 Solaris 2.4/2.5가 돌아가고 있는 Sparc에서 제대로 동작한다.
나는 솔라리스를 써본적이 없다. 그러나 나의 다른 유닉스 사용 경험에 비추어 코멘트 해보건데.
[Shankar의 실제 코드는 제거됨]
당신이 하고 있는 것은현재 호스트의 모든 unicast 주소를 hosts/NIS/DNS 호스트에 나와 있는 모든 호스트에 바인드 시키려는 것이다. (이것이 실제 상황을 반영했든 안했든 간에.), 그러나 더 중요한건, 브로드캐스트 주소를 무시하는 것이다. 이것은 소켓이 목적지 브로드캐스트 주소를 가지고 들어오는 패킷을 보지 않을 unicast주소로 바운드 하는 구현(implementation)의 대다수의 경우 인 듯 보인다.
내가 선택한 방법은 SIOGIFCONF를 사용해서 활동중인 네트웍인터페이스의 리스트를 얻어 내고 SIOCGIFFLAGS와 SIOCGIFBRDADDR를 브로드캐스트 가능한 인터페이스를 알아내고 브로드캐스트 주소를 얻는데 사용할 수 있다. 그리고 나서 각각의 unicast주소와 브로드캐스트 주소에 바인드하고, 그리고 또한 INADDR_ANY에도 바인드 한다. 그 마지막은 목적지에서 INADDR_BROADCAST로 애태우고 있는 패킷을 잡는게 필요하다. (SO_REUSEADDR는 INADDR_ANY뿐만 아니라 지정된 주소도 바인드 하는데도필요하다.)
이것은 거의 내가 원하는 바를 제공해 준다. 모책은 다음과 같다.
- 나는 특정 소켓을 통하여 패킷을 취하는 것이 반드시 그것이 실제 그 인터페이스에 도달했다는 것을 가정하지 않는다.
- 만약 그것의 목적지가 INADDR_BROADCAST 일 때 나는 패킷이 발생한 어떤 서브넷에 관해서도 알 수가 없다.
- 외관상 멀티캐스트를 지원하는 몇몇 스택들 위에서 나는 INADDR_ANY 을 통하여 들어오는 메시지를 복제 한다.
이 질문은 보통 그들의 서버를 telnet으로 테스트를 하거나 키 입력에 따른 프로세싱을 원하는 사람들에 하는 질문이다. 올바른 기법은 psuedo 터미널(Pty)를 사용하는 것이다.
Roger Espel Llima (espel@drakkar.ens.fr)에 의하면 여러분은 여러분의 서버가 다음과 같은 제어문자의 순서를 보내도록 할 수 있다. (0xff 0xfb 0x01 0xff 0xfb 0x03 0xff 0xfd 0x0f3) 이것은 다음과 같이 해석된다.(IAC WILL ECHO IAC WILL SUPPRESS-GO-AHEAD IAC DO SUPPRESS-GO-AHEAD) 이것이 뭘 의미하는지에 대한 더 자세한 정보는 std8, std28, std29를 참조하라. 그리고 Roger는 또한 다음과 같은 tip을 줬다.
- 이 코드는 echo를 막을 것이다. 그래서 만약 사용자가 그것들을 보게 하길 원한다면 사용자가 클라이언트에게 다시 입력하는 그 문자를 보내야 할 것이다.
- 캐리지 리턴은 널문자 다음에 넘어 올 것이다. 그래서 그것들을 예상하고 있어야 할 것이다.
- 만약 당신이 0xff를 받게 되면, telnet 탈출 코드인 그 다음 두 코드를 받게 될 것이다.
pty의 사용은 또한 자식 프로로세스를 실행시키고 i/o를 소켓으로 전달하는 올바른 방법이 될 것이다.
나는 faq에 포함하려고 하는 예제소스에 pty부분을 포함시켰다. 만약 어떤 사람이 이 faq에 넣어서 좋을 소스(저작권이 없는)를 가지고 있으면 나에게 email로 좀 보내 달라.
4.14. 서버에서 exec()를 실행하고 그것에 소켓 I/O를 attach시키려고 한다. 그런데 그것을 통하여 모든 데이터를 얻지 못하고 있다. 왜 그런가?
만약 당신이 printf()이나 다른 stdio.h에 정의되어 있는 스트림 함수를 사용하여 실행시키고 있는 그 프로그램이 있을 경우 당신은 두 개의 버퍼를 가지고 있어야 한다. 커널은 모든 소켓 I/O 를 버퍼링 시킨다. 이것은 2.11에 설명되어 있다. 두 번째 버퍼가 그 재난의 버퍼(?)인데 이것은 stdio버퍼이다. 그리고 이문제는 Andrew에 의해서 잘 설명되고 있다.: (이 질문에 간단하게 대답하면 당신은 소켓 보다는 pty를 사용해야 한다. 이 문서의 나머지에서 왜 그런지를 설명하려 한다)
우선, setsockopt()에 의해 제어되는 소켓 버퍼는 stdio버퍼에 대해 아무런 관계가 없다. 그것을 1로 설정하는 것은 잘못되도록 한다(tm) 아마 아래 그림이 보다 잘 설명 할 것이다.
프로세스 A 프로세스 B +---------------------+ +---------------------+ | | | | | mainline code | | mainline code | | | | | ^ | | v | | | | | fputc() | | fgetc() | | | | | ^ | | v | | | | | +-----------+ | | +-----------+ | | | stdio | | | | stdio | | | | buffer | | | | buffer | | | +-----------+ | | +-----------+ | | | | | ^ | | | | | | | | write() | | read() | | | | | | | +-------- | ----------+ +-------- | ----------+ | | User space ------------|------------------------- | --------------------------- | | Kernel space v | +-----------+ +-----------+ | socket | | socket | | buffer | | buffer | +-----------+ +-----------+ | ^ v | (AF- and protocol- (AF- and protocol- dependent code) dependent code)
이 두 프로세스가 각각 통신한다고 가정하자. 여러분은 A프로세스가 stdio 버퍼에 의해서 write
되는 데이터를 볼 수 있을 것이다. 그러나 이 버퍼는 B프로세스에 의해서는 접근이 불가능하다.
그리고 그 버퍼의 내용을 커널로 플러시 할 경우에만 그 데이터는 실제로 다른 프로세스에 전달된다.
A프로세스 안에서 버퍼링에 영향을 주는 한가지 확실한 방법은 코드를 바꾸는 것이다. 그러나 stdout을 위한 기본 버퍼링은 근원인 FD가 터미널을 가리키느냐 아니냐에 의해 제어된다.
일반적으로 터미널 출력은 line-buffered되어 있다. 그리고 터미널이 아닌 곳으로의 출력은(파일이나 파이프, 소켓 그리고 non-tty 장치등) 완전한 버퍼링이 된다. 그래서 바라는 효과는 보통
pty장치를 사용함으로써 얻을 수 있다. 예를 들어 이것은 'expect' 프로그램이 하는 방법이다.
stdio버퍼가 사용자레벨 데이터라면 exec()호출을 통해 보존되지 않는다. 그러므로 exec가 효력을 발휘하기 전에 setvbuf()를 사용하라.
다른 몇 개의 대안은 Roger Espel Llima(espel@drakkar.ens.fr)에 의해 제안 되었다.:
만약 이것이 선택사항이라면, 어떤 standalone프로그램이 그것의 입출력버퍼와 pty안에서 어떤 것인가를 수행 시킬수 있을 것이다. pty.tar.gz이라는 프로그램이 있는데 이게 그런 일을 하는 것 같다. 한 번 AltaVista같은걸로 찾아봐라. 다른 선택사항(**경고, 나쁜 해킹이다.**), 만약 여러분이 이것을 지원하는 시스템(SunOS, Solaris, Linux ELF 가 지원한다. 다른 것은 잘 모르겠다.)에서 여러분의 주 프로그램에 putenv()을 이용해 LD_PRELOAD 환경변수에 공유 라이브러리(*.so)를 넣어라, 그리고 나서 .so 안에서 몇몇 일반적으로 쓰이는 libc 함수(당신의 exec하는 것이 일찍 실행 될 것이라고 알려진) 를 재정의(redefine) 한다. 여기서 그 실행되는 시스템에서 'get control'을 할 수 있다. 그리고 그것을 얻은 처음에, setbuf(stdout, NULL)을 여러분의 프로그램 중간에 실행하라, 그리고 나서 원래의 libc함수를 dlopen()+dlsysm()과 함께 호출하라. 그리고 dlsym()값을 static 변수로 유지한다. 그러면 당신은 다음의 몇번 이것을 호출 할 수 있다.