fork()된 프로세스들에 대하여...
글쓴이: 익명 사용자 / 작성시간: 화, 2002/08/13 - 3:43오후
fork()된 프로세스들에 대하여 궁금한게 있습니다...
예를 들어 80개의 프로세스를 fork()시켜서 놓았을 경우,
시스테은 각각의 프로세스들에게 어떤 방식으로 질의를 하나요?
예를 들어 그 프로세스들이 웹서버의 프로세스라고 할 경우,
그리고 외부에서 꾸준히 80포트로 웹서비스에 대한 질의가 들어올
경우, 시스템은 그 질의들을 각각의 프로세스들에게 어떠한 방식으로
분배하게 되는지 궁금합니다...
시스템이 판단해서 사용되지 않는 프로세스에 할당하는 건가요,
아니면 그냥 무작위로 할당하는 건가요... 혹시 이런 부분에 대해
자세히 알고 계신 분 계시면, 설명을 부탁드립니다~~~
도와주셈!!
Forums:
Re: fork()된 프로세스들에 대하여...
아파치를 기준를 설명을 드리면
리슨을 하는 것은 parent가 하고
fd를 child로 pass해주는 방법을 사용합니다.
그림> http//www.math.uwaterloo.ca/~oadragoi/CS746G/a2/concur.gif
이것은 일전에 정리해둔게 있는데 아래의 주소를 참조하세요
Passing descriptors between processes--sendmsg() and recvmsg()
http//www.ezdoum.com/stories.php?story=02/07/18/6030539
그리고 fd를 어느넘으로 보낼까 하는 것은
scoreboard란 알고리즘을 사용하는데,
scoreboard에 child의 상태가 모니터링 되고 있어서 요것을
기준으로 child를 더 만들거나 죽이거나 하고,
분배될 child를 고르기도 합니다.
보너스 아파치의 구조분석이 잘된 문서
The Conceptual Architecture of the Apache Web Server
http//www.math.uwaterloo.ca/~oadragoi/CS746G/a1/apache_conceptual_arch.html
http//www.math.uwaterloo.ca/~oadragoi/CS746G/a2/acc.html
Re^2: fork()된 프로세스들에 대하여...
답변 감사드립니다...
근데 제가 정말 궁금한건 그 scoreboard라는 알고리즘이 어떤것인가 하는
건데,, 여기 저기 뒤져봐도 설명이 잘 된 곳이(제가 알아들을 수 있는
곳이-_-) 없네요... -_-;;
(근데, scoreboard라는 알고리즘은 시스템이 사용하는 것이지요?
아파치가 아니라...)
혹시 scoreboard라는 알고리즘에 대해서 간략하게나마 설명해 주시길
부탁드립니다~ 감사합니다~~~~ (--) (__) (--)
Re^3: 아파치가 만든 점수판이에요..
음..
아파치가 클라이언트의 요청을 분배하고 child process를 관리하기 위한
프로세스의 목록입니다.
여기엔 프로세스관리와 요청분배를 처리하기 위한 정보를
가지고 있겠지요..
/apache_1.3.24/src/include/scoreboard.h를 보시면
어떤 정보를 가지고 있는지 알수가 있습니다.
아래가..
http///www.ezdoum.com/server-status
(이 주소는 다른분들은 조회가 안될꺼에요 서버정보를 포함하고 있어서
특정 주소영역만 볼수 있게 제한을 걸어놨습니다.)
의 결과인데 요것은 아파치 서버 상태를 보여주는 것으로
./apache_1.3.24/src/modules/standard/mod_status.c
소스를 보면 scoreboard에서 정보를 조회하는 것을 볼수가 있지요.
음.. scoreboard key를 보면..
"_" Waiting for Connection, "S" Starting up, "R" Reading Request,
"W" Sending Reply, "K" Keepalive (read), "D" DNS Lookup,
"L" Logging, "G" Gracefully finishing, "." Open slot with no current process
요렇게.. 서버의 상태가 나와서..
어느 child에게 요청을 분배할지 알게 되는 겁니다.
아울러 서버가 뭐하느라 블럭이 걸리는지도 알게되어서
/server-status 처럼 모니터링 정보로도 활용할수 있구요..
Apache Server Status for www.ezdoum.com
Server Version Apache/1.3.24 (Unix) PHP/4.2.2
Server Built Aug 13 2002 191230
--------------------------------------------------------------------------------
Current Time Tuesday, 13-Aug-2002 202433 KST
Restart Time Tuesday, 13-Aug-2002 191246 KST
Parent Server Generation 0
Server uptime 1 hour 11 minutes 47 seconds
1 requests currently being processed, 9 idle servers
____W_____......................................................
................................................................
................................................................
................................................................
Scoreboard Key
"_" Waiting for Connection, "S" Starting up, "R" Reading Request,
"W" Sending Reply, "K" Keepalive (read), "D" DNS Lookup,
"L" Logging, "G" Gracefully finishing, "." Open slot with no current process
PID Key
30129 in state _ , 30130 in state _ , 30131 in state _
30132 in state _ , 30133 in state W , 30135 in state _
30136 in state _ , 30137 in state _ , 30182 in state _
30183 in state _ ,
--------------------------------------------------------------------------------
To obtain a full report with current status information you need to use the
ExtendedStatus On directive.
--------------------------------------------------------------------------------
Apache/1.3.24 Server at www.ezdoum.com Port 80
근데요!!!!
이해가 잘 가진 않지만^^;;;
답변에 감사드립니다~~
많은 도움이 되었네요...~
마지막으로 한번만 더 여쭤볼께요^^
그렇다면, 아파치처럼 child process들을
관리하는 특별한 처리를 프로그램에서
해주지 않았다면 어떻게 되는 건가요?
아파치가 아니라요... 그냥 80포트에
대해서 응답하는 프로그램이며 다수의
(80개 정도)프로세스를 포크하여
클라이언트로부터 질의를 받아 처리하는
것인데, 프로세스에게 질의를 배분하는
특별한 알고리즘이 없다면, 제 생각에는
시스템에서 분배할 것 같은데 (여기서
시스템은 linux가 깔린 서버입니다.)
그 경우 어떻게 되는지가 궁금합니다~
그런 부분에 대해 알려면 어떤 부분을
찾아봐야 하는지요???
스케쥴링?
아마도 이걸 말씀하시는 거 같군요.
그건 당연히 커널의 몫입니다.
cpu를 어떤 프로세스 하나가 점유한 채 실행이 되고 있는데
그 제어권을 어떻게 다른 프로세스로 넘길 수 있느냐
하는 문제가 궁금하시다면 다음 내용입니다.
즉, 내가 프로그램을 짜면서
다른 프로세스에게 cpu를 양보하는 코드를 작성하지 않았는데
어떻게 내가 작성한 프로그램이 종료가 되지 않았는데도
다른 프로세스가 같이 실행될 수 있느냐 하는 문제인거죠.
프로세스가 실행이 되는 레벨이 유저레벨과 커널레벨
2가지가 있습니다.
어플리케이션 프로그래머가 작성한 코드들, 가령
while(1)
statement;
이런 코드들은 유저레벨에서 실행이 됩니다.
(이 상태에서는 당연히 커널조차도 이 프로세스를 어떻게 할 수가 없겠죠)
이때 만일 어플리케이션이
open()과 같은 시스템콜을 호출하게 되면 커널레벨로 들어갑니다.
이때부터는 커널 영역입니다.
이제 제어권은 유저의 프로세스가 아닌 커널이 가지게 되므로
이때가 바로 다른 프로세스로 제어를 넘길 수 있는,
즉 스케쥴링을 할 수 있는 절호의 찬스가 되겠죠?
그렇다면 모든 시스템콜 마지막에는
스케쥴링을 하는 커널 코드(스케쥴러라고 합시다)가
붙어 있어야 하는가 하는 의문이 생기겠죠?
커널레벨에서 작업을 마치고 유저레벨로 돌아가기 전에는
늘 ret_from_sys_call 이라는 부분의 코드를 실행하게 됩니다.
여기에서는 need_resched(int 타입의 변수)라는 플래그를 검사하여
1이면 스케쥴러를 호출하게 되어 있습니다.
다시 말해서 좀 전에 커널레벨로 들어왔던 프로세스가 아닌
다른 프로세스로 전환이 되는 거겠죠.
만일 need_resched가 0이라면 다시 좀 전의 그 프로세스로 복귀할 테고요.
그럼 이 need_resched라는 변수는 누가, 언제 1로 바꾸느냐 하는 의문이 들죠? ^^
이런 프로그램을 한 번 가정해 봅시다.
int main()
{
while(1)
;
return 0;
}
위의 프로그램이 실행이 된다면 스케쥴링이 절대 되지 않을까요?
왜냐하면 제가 앞서 설명한대로라면 어떤 시스템콜도 호출하지 않고 있으니
커널레벨로 진입할 일이 없고
그러므로 커널역시 스케쥴링을 할 수가 없지 않겠습니까?
정답은 그럼에도 스케쥴링이 된다입니다.
이유는 이렇습니다.
리눅스에서는 10ms마다 타이머 인터럽트가 발생하여
커널레벨로 진입하게 되어 있습니다.
다시 말해서 어플리케이션에서 시스템콜을 호출하지 않더라도
커널은 스케쥴링할 수 있는 기회가 1초에 100번은 있는 셈인거죠.
이때마다 커널은 현재 프로세스가 cpu를 사용한 시간에 10ms를 더하고,
이 값이 만일 100ms가 되면 위에서 말한 need_resched를 1로 셋팅합니다.
이 말은 한 프로세스가 한번에 cpu를 차지할 수 있는 가장 긴 시간은
100ms라는 의미가 되겠죠.
times()라는 시스템콜을 아시죠?
그 시스템콜도 위의 저 값을 읽어오는 것일 뿐입니다.
타이머 인터럽트가 발생될 때마다 커널이 그때그때 계산해 놓은 값이죠...
struct task_struct라는 구조체가 있습니다.
모든 프로세스가 생성될 때마다(fork() 실행시)
그 프로세스에 할당되는 구조체입니다.
프로세스의 모든 정보를 담고 있는 구조체라고 보시면 됩니다.
시스템에 현재 실행중인 프로세스가 100개라면
커널 역시 100개의 task_struct 구조체를 유지하고 있겠죠.
이 구조체 안에 int need_resched라는 필드가 있습니다.
그리고 그 프로세스가 cpu를 사용한 시간 정보도 여기에 들어 있고요...
참고로 현재 실행중인 프로세스가 누구냐하는 정보는
struct task_struct *current;
라는 전역변수에서 유지하고 있습니다.
Re^2: 정정 아파치는 multiple Listen을 사용합니다.
제가 작성한 글에 오류가 있어서 다시 정정을 하고자 합니다.
The Conceptual Architecture of the Apache Web Server
문서의 내용을 토대로 답글을 달았는데, 아무래도 찝찝해서
제가 요청을 처리하는 부분을 다시 분석을 해봤는데
동작 원리가 틀리다는 것을 알았습니다.
아파치가 요청을 받을때, fd passing을 사용한다고 했는데
이것은 제가 잘못 알고 있던 것이었네요..
결론부터 말하자면 아파치는 multiple Listen 형태로 동작합니다.
즉 포크 이전에 listen, bind를 실행하고,
fork된 child들에서 accept를 합니다.
#0 0x2abc7a02 in __libc_accept () from /lib/libc.so.6
#1 0x8079e7e in child_main (child_num_arg=0) at http_main.c4367
#2 0x807a2c0 in make_child (s=0x80d0e84, slot=0, now=1029550455) at http_main.c4710
#3 0x807a419 in startup_children (number_to_start=5) at http_main.c4792
#4 0x807aa76 in standalone_main (argc=2, argv=0x7ffffb44) at http_main.c5100
#5 0x807b243 in main (argc=2, argv=0x7ffffb44) at http_main.c5448
요것은 accept에서 블럭이 걸린상태에서 call stack을 뜬것입니다.
호출 순서는 5->4->..->1 입니다. (아파치 버젼은 1.3.26)
4번에서 setup_listeners(pconf)를 실행하고
여기서 listen,bind가 make_sock 함수에 의해서 이루어 집니다.
make_sock (p=0x80d0e5c, server=0x80e1788) at http_main.c3651
3651 ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */
3658 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) {
3670 one = 1;
3672 if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(int)) < 0) {
3686 sock_disable_nagle(s);
3709 if (server_conf->send_buffer_size) {
3726 if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) {
3747 if (listen(s, ap_listenbacklog) == -1) {
3794 ap_unblock_alarms();
3798 if (s >= FD_SETSIZE) {
3813 return s;
3814 }
그리고
4번 단계에서 3번을 호출해서 fork가 시작됩니다.
fork가 된 다음 부모는 만들어진 child를 scoreboard에 정보를 기록하고
child를 child_main을 실행해서 요청을 받아드릴 accept를 준비하게 됩니다.
그런데 아파치는 단순히 accept를 하는게 아니라.
이것을 직렬화를 하는데 이것에 대한 설명은 너무 길어지니까
http//httpd.apache.org/docs/misc/perf-tuning.html
문서를 보세요 아래쪽에 보면 accept Serialization 라고 나옵니다.
for (;;) {
accept_mutex_on ();
for (;;) {
fd_set accept_fds;
FD_ZERO (&accept_fds);
for (i = first_socket; i <= last_socket; ++i) {
FD_SET (i, &accept_fds);
}
rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL);
if (rc < 1) continue;
new_connection = -1;
for (i = first_socket; i <= last_socket; ++i) {
if (FD_ISSET (i, &accept_fds)) {
new_connection = accept (i, NULL, NULL);
if (new_connection != -1) break;
}
}
if (new_connection != -1) break;
}
accept_mutex_off ();
process the new_connection;
}
댓글 달기