TCP/IP 소켓 프로그래밍 책을 보다가 너무 지겨워서.. 웹 서핑.. 그러다가 음.. python의 실력을 늘리기 위한 책 없을까라고 뒤지다가 찾게된 사이트. 보아하니 파이썬을 잘 아는 이가 원서를 번역한 사이트인듯. 요즘 느끼는게.. 내가 영어 진짜 못한다다. 아.. 아무리 책이 쉽게 씌여 있어도 한글로 된 책 읽느니만 못하다. 아쉽다. 원서를 읽으면 속도가 한참 반감되는 내자신이. 여튼 한풀이는 이정도로 하고 목표는 틈나는 대로 아래 사이트 뒤적 거리고 한 강좌씩 읽기다.
http://coreapython.hosting.paran.com/pygnudoc.html
예전에 찾은 것인데 이것도 혹시나 해서 붙여 놓는다
django 설명
http://www.hannal.net/think/01-python_django_lecture/
Sunday, January 30, 2011
chapter 12 (IO 멀티플렉싱(Multiplexing))
-IO 멀티플렉싱 기반의 서버-
멀티프로세스 서버의 단점과 대안
멀티프로세스 서버의 단점 : 프로세스 생성에 많은 양의 연산, 메모리 공간 요구. IPC (inner process communcation) 방법도 복잡하다.
멀티플렉싱이라는 단어의 이해
그럼 멀티프로세스의 해결방법은 무엇인가? IO 멀티플렉싱 서버가 그 답이 될수 있다.
그럼 멀티 플렉싱이란 뭐냐? 이건 책의 그림을 보아야 할듯. 말로 하자면.. 책보자. 사전적으로 이야기 하자면 하나의 통신채널을 통해서 둘 이상의 데이터(시그널)를 전송하는데 사용되는 기술
멀티플렉싱의 개념을 서버에 적용하기
하나의 프로세스를 이용해서 둘 이상의 클라이언트에게 서비스를 제공하는 방법인데..
IO 멀티플렉싱 서버에서는 하나의 프로세스가 데이터가 수신된 소켓이 있는지 확인하고 그 소켓을 통해서 전송된 데이터를 수신하게 된다.
-select 함수의 이해와 서버의 구현-
select 함수를 이용하는 것이 멀티플렉싱 서버의 구현에 대표적인 방법이다.
select 함수의 기능과 호출순서
select 함수를 이용하면 한곳에 여러 개의 파일 디스크립터를 모아놓고 동시에 다음을 관찰할 수 있다.
관찰 항목(event) :
1.수신한 데이터를 지니고 있는 소켓이 존재 하는가?
2.블로킹되지 않고 데이터의 전송이 가능한 소켓은 무엇인가?
3.예외사항이 발생한 소켓은 무엇인가?
이와 같은 관찰 항목을 이벤트(event) 라고 한다. 관찰 항목에 속하는 상황이 발생 하였을때 이를 이벤트가 발생하였다라고 표현한다.
select 함수의 사용이 상당히 복잡한데 이를 정리하면 다음과 같다.
step1 : 파일 디스크립터의 설정, 검사의 범위 지정, 타임아웃의 설정
step2 : select 함수의 호출
step3 : 호출결과 확인
파일 디스크립터의 설정
select함수가 여러개의 파일 디스크립터를 동시에 관찰 할수 있기 때문에(곧 여러 소켓을 동시에 관찰) 우선 파일 디스크립터를 모아야 한다. 모을때도 이벤트(관찰 항목; 수신, 전송, 예외)에 따라 구분해서 모아야 한다.
이 파일 디스크립터는 fd_set이라는 구조체에 모으는데 비트단위로 이뤄진 배열이라고 생각하면 된다.그러니까 첫번째 배열의 값 (index 0인 값)은 0번의 파일 디스크립터를 의미하고 이것이 1이면 관찰 대상에 있는 파일 디스크립터라는 의미이다. select 함수 이후 변화가 없는 파일디스크립터의 fd_set 구조체의 1은 0이 된다. 그래서 select 후 여전히 1값을 갖는 fd_set 구조체의 component를 찾으면 그것이 관심있는 변화가 생긴 파일 디스크립터이다.
검사(관찰)의 범위지저오가 타임아웃의 설정
select는 함수는 다음과 같다. int select(int maxfd, fd_set *readset, fd_set *write_set, fd_set *exceptset, const struct timeval * timeout); 형태로 오류시 -1 timeout 지정시간보다 시간이 지났는데 변화가 있는 fd_set이 없으면 0을 변화가 있으면 변화된 파일 디스크립터의 수를 리턴한다.
select 함수호출 이후의 결과확인
최종! select 함수를 호출하는 예제의 확인
멀티플렉싱 서버의 구현
전체적으로 select 를 이용한 멀티플랙싱을 들여다보니, 이게 어떻게 진행되는거냐 하면 우선은 프로세스가 하나이고 전에는 한 프로세스가 하나의 파일 디스크립터만을 감당했는데, 이제는 fd_set이라는 구조체를 이용해서 파일디스크립터들을 fd_set 구조체에 묶고 fd_set을 하나하나 for문으로 들여다 보면서 관심 대상의 변화가 있는 파일디스크립터를 골라내서 특정 작업을 하는 것이다.
멀티프로세스 서버의 단점과 대안
멀티프로세스 서버의 단점 : 프로세스 생성에 많은 양의 연산, 메모리 공간 요구. IPC (inner process communcation) 방법도 복잡하다.
멀티플렉싱이라는 단어의 이해
그럼 멀티프로세스의 해결방법은 무엇인가? IO 멀티플렉싱 서버가 그 답이 될수 있다.
그럼 멀티 플렉싱이란 뭐냐? 이건 책의 그림을 보아야 할듯. 말로 하자면.. 책보자. 사전적으로 이야기 하자면 하나의 통신채널을 통해서 둘 이상의 데이터(시그널)를 전송하는데 사용되는 기술
멀티플렉싱의 개념을 서버에 적용하기
하나의 프로세스를 이용해서 둘 이상의 클라이언트에게 서비스를 제공하는 방법인데..
IO 멀티플렉싱 서버에서는 하나의 프로세스가 데이터가 수신된 소켓이 있는지 확인하고 그 소켓을 통해서 전송된 데이터를 수신하게 된다.
-select 함수의 이해와 서버의 구현-
select 함수를 이용하는 것이 멀티플렉싱 서버의 구현에 대표적인 방법이다.
select 함수의 기능과 호출순서
select 함수를 이용하면 한곳에 여러 개의 파일 디스크립터를 모아놓고 동시에 다음을 관찰할 수 있다.
관찰 항목(event) :
1.수신한 데이터를 지니고 있는 소켓이 존재 하는가?
2.블로킹되지 않고 데이터의 전송이 가능한 소켓은 무엇인가?
3.예외사항이 발생한 소켓은 무엇인가?
이와 같은 관찰 항목을 이벤트(event) 라고 한다. 관찰 항목에 속하는 상황이 발생 하였을때 이를 이벤트가 발생하였다라고 표현한다.
select 함수의 사용이 상당히 복잡한데 이를 정리하면 다음과 같다.
step1 : 파일 디스크립터의 설정, 검사의 범위 지정, 타임아웃의 설정
step2 : select 함수의 호출
step3 : 호출결과 확인
파일 디스크립터의 설정
select함수가 여러개의 파일 디스크립터를 동시에 관찰 할수 있기 때문에(곧 여러 소켓을 동시에 관찰) 우선 파일 디스크립터를 모아야 한다. 모을때도 이벤트(관찰 항목; 수신, 전송, 예외)에 따라 구분해서 모아야 한다.
이 파일 디스크립터는 fd_set이라는 구조체에 모으는데 비트단위로 이뤄진 배열이라고 생각하면 된다.그러니까 첫번째 배열의 값 (index 0인 값)은 0번의 파일 디스크립터를 의미하고 이것이 1이면 관찰 대상에 있는 파일 디스크립터라는 의미이다. select 함수 이후 변화가 없는 파일디스크립터의 fd_set 구조체의 1은 0이 된다. 그래서 select 후 여전히 1값을 갖는 fd_set 구조체의 component를 찾으면 그것이 관심있는 변화가 생긴 파일 디스크립터이다.
검사(관찰)의 범위지저오가 타임아웃의 설정
select는 함수는 다음과 같다. int select(int maxfd, fd_set *readset, fd_set *write_set, fd_set *exceptset, const struct timeval * timeout); 형태로 오류시 -1 timeout 지정시간보다 시간이 지났는데 변화가 있는 fd_set이 없으면 0을 변화가 있으면 변화된 파일 디스크립터의 수를 리턴한다.
select 함수호출 이후의 결과확인
최종! select 함수를 호출하는 예제의 확인
멀티플렉싱 서버의 구현
전체적으로 select 를 이용한 멀티플랙싱을 들여다보니, 이게 어떻게 진행되는거냐 하면 우선은 프로세스가 하나이고 전에는 한 프로세스가 하나의 파일 디스크립터만을 감당했는데, 이제는 fd_set이라는 구조체를 이용해서 파일디스크립터들을 fd_set 구조체에 묶고 fd_set을 하나하나 for문으로 들여다 보면서 관심 대상의 변화가 있는 파일디스크립터를 골라내서 특정 작업을 하는 것이다.
chapter 11 (프로세스간 통신(Inner Process Communication))
-프로세스간 통신의 기본 개념-
프로세스간 통신의 기본 이해
예를 들어 프로세스 A가 프로세스 B의 특정 변수를 통해서 프로세스 B의 상태 내지는 값을 알려고 할때 이것이 프로세스 간의 통신다. 그런데 fork 함수를 통해서 프로세스를 생성하게 되면 부모 프로세스의 내용을 새로운 메모리 공간에 복사 하기 때문에 두 프로세스 간에는 공유하는 메모리 공간이 없기 때문에 두 프로세스 간에 통신이 이루어 지기 위해서는 별도의 방법이 필요하다.
파이프(PIPE) 기반의 프로세스간 통신
이렇듯 두 프로세스가 통신하기 위해서는 파이프 라는 것을 생성해야 한다. 이는 운영체제의 자원, 즉 메모리 공간으로 fork에 의해 복사 되는 것이 아니다(다만 그 파이프를 의미하는 파일 디스크립터가 복사 된다). 이 파이프 생성 함수로 int pipe(int filedes[2]); 가 있으며 int형 배열 filedes에 [0]인자를 파이프의 출구에 해당하는 파일 디스크립터가 [1]에는 입구에 해당하는 파일 디스크립터가 저장된다. 이 파이프를 이용해서 두 프로세스간에 통신이 가능하다.
파이프 기반의 프로세스간 양방향 통신
*책의 예제를 반드시 살펴라. pipe 사용시 주의 해야 할 점이 코딩되어 있다.
파이프 하나를 놓고 두 프로세스에서 통신할때 두 프로세스 모두 read write 함수를 호출해서 pipe에다가 메시지를 주고 받을때, 코딩에 따라 자기가 보낸 메시지를 자기가 받는 경우가 생긴다. (책의 예제를 보게 되면 알게 되는 점이 부모 프로세스가 끝나면 자식 프로세스의 실행 코드의 유무에 상관없이 명령 프롬프트로 돌아오게 된다. 또 하나 A라는 프로세스에서 read를 호출하기 전에 B라는 프로세스에서 write가 호출되어야 파이프에 B에서의 메시지가 써져서 A 프로세스에서 읽혀지게 되는데 write 함수가 호출되기전에 read함수가 호출되면 어떻게 되나 생각이되는데 이는 read가 함수가 pipe의 내용을 읽을때까지 대기하게 된다.) 그럼 자기가 보낸 메시지를 자기가 받지 않고 반드시 상대방이 받게 하는 방법은 두개의 파이프를 사용하는 것이다.
-프로세스간 통신의 적용-
메시지를 저장하는 형태의 에코 서버
무엇인가 더 큰걸 만들어보고 싶다면
프로세스간 통신의 기본 이해
예를 들어 프로세스 A가 프로세스 B의 특정 변수를 통해서 프로세스 B의 상태 내지는 값을 알려고 할때 이것이 프로세스 간의 통신다. 그런데 fork 함수를 통해서 프로세스를 생성하게 되면 부모 프로세스의 내용을 새로운 메모리 공간에 복사 하기 때문에 두 프로세스 간에는 공유하는 메모리 공간이 없기 때문에 두 프로세스 간에 통신이 이루어 지기 위해서는 별도의 방법이 필요하다.
파이프(PIPE) 기반의 프로세스간 통신
이렇듯 두 프로세스가 통신하기 위해서는 파이프 라는 것을 생성해야 한다. 이는 운영체제의 자원, 즉 메모리 공간으로 fork에 의해 복사 되는 것이 아니다(다만 그 파이프를 의미하는 파일 디스크립터가 복사 된다). 이 파이프 생성 함수로 int pipe(int filedes[2]); 가 있으며 int형 배열 filedes에 [0]인자를 파이프의 출구에 해당하는 파일 디스크립터가 [1]에는 입구에 해당하는 파일 디스크립터가 저장된다. 이 파이프를 이용해서 두 프로세스간에 통신이 가능하다.
파이프 기반의 프로세스간 양방향 통신
*책의 예제를 반드시 살펴라. pipe 사용시 주의 해야 할 점이 코딩되어 있다.
파이프 하나를 놓고 두 프로세스에서 통신할때 두 프로세스 모두 read write 함수를 호출해서 pipe에다가 메시지를 주고 받을때, 코딩에 따라 자기가 보낸 메시지를 자기가 받는 경우가 생긴다. (책의 예제를 보게 되면 알게 되는 점이 부모 프로세스가 끝나면 자식 프로세스의 실행 코드의 유무에 상관없이 명령 프롬프트로 돌아오게 된다. 또 하나 A라는 프로세스에서 read를 호출하기 전에 B라는 프로세스에서 write가 호출되어야 파이프에 B에서의 메시지가 써져서 A 프로세스에서 읽혀지게 되는데 write 함수가 호출되기전에 read함수가 호출되면 어떻게 되나 생각이되는데 이는 read가 함수가 pipe의 내용을 읽을때까지 대기하게 된다.) 그럼 자기가 보낸 메시지를 자기가 받지 않고 반드시 상대방이 받게 하는 방법은 두개의 파이프를 사용하는 것이다.
-프로세스간 통신의 적용-
메시지를 저장하는 형태의 에코 서버
무엇인가 더 큰걸 만들어보고 싶다면
Saturday, January 29, 2011
chapter 10 (멀티프로세스 기반의 서버구현)
-프로세스의 이해와 활용-
두가지 유형의 서버
다중접속 서버의 구현방법들
멀티 프로세스 기반 서버 : 다수의 프로세스를 생성하는 방식으로 서비스 제공
멀티 플렉싱 기반 서버 : 입출력 대상을 묶어서 관리하는 방식으로 서비스 제고
멀티쓰레딩 기반 서버 : 클라이언트의 수만큼 쓰레드를 생성하는 방식으로 서비스 제공
프로세스(Process)의 이해
프로세스 : 메모리 공간을 차지한 상태에서 실행중인 프로그램
프로세스 ID
프로세스 ID : 프로세스는 생성되는 형태에 상관없이 운영체제로 부터 ID를 부여 받는데 이를 프로세스 ID라 한다.
fork 함수호출을 통한 프로세스의 생성
fork 함수는 호출한 프로세스의 복사본을 생성한다. 새로운 다른 프로그램을 바탕으로 생성하는 것이 아니라 이미 실행중인 fork를 실행한 프로세스를 복사해서 생성된다. 이때 완전히 메모리 영역까지 동일하게 복사하기 때문에 fork 함수 이전에 실행했던 변수의 값역시 똑같이 복사한다. 다시 말하면 fork 함수가 호출되는 되서 반환되는 순간 두프로세스는 따로 돌아 가게 되는데 복제된 프로세스와 부모 프로세스의 fork의 반환값이 다르다. 부모 프로세스의 경우 자식 프로세스의 pid를 반환 받고 자식 프로세스의 경우 0을 반환 받는다. 이를 이용해서 프로그래밍을 해야 한다.
-프로세스 좀비(Zombie) 프로세스-
좀비 프로세스
좀비 프로세스 : 프로세스가 생성되고 나서 할일을 다하면 사라져야 하는데 사라지지 않고 시스템의 리소스를 차지 하고 잇는 프로세스
좀비 프로세스의 생성이유
fork에 의해 생성된 자식 프로세스는 1. 인자를 전달하면서 exit를 호출하거나 2. main 함수에서 return문을 실행하면서 값을 반환하는경우 종료된다. 이 반환값은 일단 운영체제로 넘어가게 되고 부모 프로세스로 전달된다. 부모 프로세스로 이 값이 전달 되기 전까지는 자식 프로세스는 종료되지 않는데 이 상태에 있는 자식 프로세스는 좀비 프로세스이다.
좀비 프로세스의 소멸1 : wait 함수의 사용
좀비 프로세스를 안만들려면 부모 프로세스가 운영체제로 부터 자식 프로세스의 반환값을 요청하면 된다. 그 첫번째 방법이 wait함수 이용하는것. pid_t wait(int * statloc); 형태로 성공시 자식의 PID를 반환, 실패시 -1 이 반환되며 성공시 종료된 자식 프로세스가 있다면 전달인자인 statloc에 그 반환값이 저장된다.
이 wait 함수는 호출된 시점에서 종료된 자식 프로세스가 없다면, 임의이 자식 프로세스가 종료될 때까지 blocking 상태에 놓인다.
좀비 프로세스의 소멸2 : waitpid 함수의 사용
위 wait 함수의 blocking 상태가 걱정이 된다면 waitpid 함수를 사용하면 된다. pid_t waitpid(pid_t pid, int * statloc, int options); 의 형태로 인자로 종료를 확인하고자 하는 자식 프로세스의 pid가 pid에(자식 프로세스의 pid대신 -1을 던지면 임의의 자식 프로세스를 의미), options 인자에는 WNOHANG 인자를 전달하면 종료된 자식 프로세스가 존재하지 않아도 블로킹 상태에 있지 않고 0을 반환하면서 함수를 빠져 나온다. 성공시 자식 프로세스의 pid , 실패시 -1반환
-시그널 핸들링-
위의 waitpid를 통한 자식 프로세스의 종료 메시지를 받는 방식은 효율적이지 못하다. 왜? 부모 프로세스가 계속 그 메시지를 기다려야 하므로. 다른 방식을 알아보자
운영체제야! 네가 좀 알려줘
자식 프로세스의 종료의 인식주체는 운영체제이다. 그렇기 때문에 운영체제가 부모 프로세스에게 종료되었음을 알려주면 효율적이다. 이러한 프로그램 구현을 위해 시그널 핸들링(Signal Handling)이라는 것이 존재한다. 시그널이란 특정상황이 발생했음을 알리기 위해 운영체제가 프로세스에게 전달하는 메시지를 의미하고 핸들링또는 시그널 핸들링이란 특정 메시지와 관련하여 미리 정의된 작업을 진행 하는 것을 의미한다.
잠시 JAVA 얘기를 : 열려있는 사고를 지니자!
C나 C++은 프로세스나 쓰레드의 생성방법을 언어차원에서 지원하지 않는다(곧 ANSI 표준애서 이의 지원을 위한 함수를 정의하지 않고 있다). 반면 JAVA는 운영체제 독립적인 플랫폼 위에서 돌아가기 때문에 프로세스나 쓰레드를 생성하는 함수를 지원한다.
시그널과 signal 함수
프로세스가 운영체제에게 특정 시그널이 발생했을때 특정 함수를 호출을 요구할수 있게 해주는 함수가 signal 이란 함수이다. void (*signal(int signo, void (*func)(int)))(int); 형태로 시그널 발생시 호출되도록 등록된 함수의 포인터가 반환된다. signal 함수의 첫번째 인자인 signo에는 SIGALRM(alarm 함수호출을 통해서 등록된 시간이 된 상황) , SIGINT(CTRL+C가 입력된 상황), SIGCHLD(자식 프로세스가 종료된 상황)이 올수 있다.
주의 할것은 시그널이 발생되면 sleep 함수로 blocking되어 있던 프로세스는 깨어나고 다시 block 상태로 돌아가지 않는다. 책의 예제를 살펴보면 이를 확인할 수 있다. 또 하나 alarm함수를 이용해서 특정 시간 이후에 SIGALRM 시그널을 발생시킬 수 있는데, 예를 들어 alarm(2) 라고 한뒤 signal(SIGALRM, FUNC)이라고 했으면 2초뒤 FUNC 함수가 실행되어진다. 그런데 여기서 프로세스 자체가 2초동안 유지 되지 않는다면 프로그램은 FUNC 함수를 실행하지 않은 채로 종료된다.
sigaction 함수를 이용한 시그널 핸들링
signal 함수는 유닉스 계열의 운영체제 별로 동작 방식에 있어서 약간의 차이가 있기 때문에 보통 sigaction 함수를 쓴다. int signaction(int signo, const struct sigaction * act, struct sigaction * oldact); 형태로 signo에는 signal함수와 동일하게 signal 정보를 넣어준다 , act 인자에는 시그널 발생시 호출될 함수의 정보가 담긴 sigaction 구조체를 넣는데 sigaction 구조체의 한 변수에 함수 포인터가 있다.
시그널 핸들링을 통한 좀비 프로세스의 소멸
-멀티태스킹 기반의 다중접속 서버-
프로세스 기반의 다중접속 서버의 구현 모델
다중접속 에코 서버의 구현
fok 함수호출을 통한 파일 디스크립터의 복사
멀티 프로세스 기반의 다중 접속 서버를 코딩할때 처음의 코딩과 accept 함수까지는 동일하다. 그 이후로 fork 함수를 이용해서 프로세스를 생성하고 그 자식 프로세스에서 클라이언트와의 통신을 처리하게 한다. 다만 주의 해야 할점은 fork이후 자식 프로세스에서는 서버 소켓을 close 해야 하고 부모 프로세스에서는 클라이언트 소켓을 close해야 한다. fork를 통해 프로세스가 생성될때 메모리의 모든 내용이 복사 된다고 하였는데 소켓이 복사 되는것은 아니고 소켓을 의미하는 파일 디스크립트가 복사 되는 것이다(소켓은 운영체제의 소유이다). 그래서 각 소켓에 2번의 파일 디스크립트가 있는건데 두 파일 디스크립트가 소멸되야 종료시 소켓이 소멸된다. 그렇게에 미리 각 파일 디스크립트를 close하는 것.
-TCP 의 입출력 루틴 분할-
입출력 루틴 분할의 의미와 이점
입력과 출력 루틴을 분할해서 각각 다른 프로세스에서 각 루틴을 수행하게 하는 것이 프로그래밍 자체도 쉬워지고 속도도 통신의 속도도 빨라진다. 마찬가지로 fork통해 프로세스를 하나 더 생성되면 소켓에 대한 파일 디스크립터 역시 복사 되기 때문에 각 프로세스에서 닫아줘야 한다.
에코 클라이언트의 입출력 루틴 분할
두가지 유형의 서버
다중접속 서버의 구현방법들
멀티 프로세스 기반 서버 : 다수의 프로세스를 생성하는 방식으로 서비스 제공
멀티 플렉싱 기반 서버 : 입출력 대상을 묶어서 관리하는 방식으로 서비스 제고
멀티쓰레딩 기반 서버 : 클라이언트의 수만큼 쓰레드를 생성하는 방식으로 서비스 제공
프로세스(Process)의 이해
프로세스 : 메모리 공간을 차지한 상태에서 실행중인 프로그램
프로세스 ID
프로세스 ID : 프로세스는 생성되는 형태에 상관없이 운영체제로 부터 ID를 부여 받는데 이를 프로세스 ID라 한다.
fork 함수호출을 통한 프로세스의 생성
fork 함수는 호출한 프로세스의 복사본을 생성한다. 새로운 다른 프로그램을 바탕으로 생성하는 것이 아니라 이미 실행중인 fork를 실행한 프로세스를 복사해서 생성된다. 이때 완전히 메모리 영역까지 동일하게 복사하기 때문에 fork 함수 이전에 실행했던 변수의 값역시 똑같이 복사한다. 다시 말하면 fork 함수가 호출되는 되서 반환되는 순간 두프로세스는 따로 돌아 가게 되는데 복제된 프로세스와 부모 프로세스의 fork의 반환값이 다르다. 부모 프로세스의 경우 자식 프로세스의 pid를 반환 받고 자식 프로세스의 경우 0을 반환 받는다. 이를 이용해서 프로그래밍을 해야 한다.
-프로세스 좀비(Zombie) 프로세스-
좀비 프로세스
좀비 프로세스 : 프로세스가 생성되고 나서 할일을 다하면 사라져야 하는데 사라지지 않고 시스템의 리소스를 차지 하고 잇는 프로세스
좀비 프로세스의 생성이유
fork에 의해 생성된 자식 프로세스는 1. 인자를 전달하면서 exit를 호출하거나 2. main 함수에서 return문을 실행하면서 값을 반환하는경우 종료된다. 이 반환값은 일단 운영체제로 넘어가게 되고 부모 프로세스로 전달된다. 부모 프로세스로 이 값이 전달 되기 전까지는 자식 프로세스는 종료되지 않는데 이 상태에 있는 자식 프로세스는 좀비 프로세스이다.
좀비 프로세스의 소멸1 : wait 함수의 사용
좀비 프로세스를 안만들려면 부모 프로세스가 운영체제로 부터 자식 프로세스의 반환값을 요청하면 된다. 그 첫번째 방법이 wait함수 이용하는것. pid_t wait(int * statloc); 형태로 성공시 자식의 PID를 반환, 실패시 -1 이 반환되며 성공시 종료된 자식 프로세스가 있다면 전달인자인 statloc에 그 반환값이 저장된다.
이 wait 함수는 호출된 시점에서 종료된 자식 프로세스가 없다면, 임의이 자식 프로세스가 종료될 때까지 blocking 상태에 놓인다.
좀비 프로세스의 소멸2 : waitpid 함수의 사용
위 wait 함수의 blocking 상태가 걱정이 된다면 waitpid 함수를 사용하면 된다. pid_t waitpid(pid_t pid, int * statloc, int options); 의 형태로 인자로 종료를 확인하고자 하는 자식 프로세스의 pid가 pid에(자식 프로세스의 pid대신 -1을 던지면 임의의 자식 프로세스를 의미), options 인자에는 WNOHANG 인자를 전달하면 종료된 자식 프로세스가 존재하지 않아도 블로킹 상태에 있지 않고 0을 반환하면서 함수를 빠져 나온다. 성공시 자식 프로세스의 pid , 실패시 -1반환
-시그널 핸들링-
위의 waitpid를 통한 자식 프로세스의 종료 메시지를 받는 방식은 효율적이지 못하다. 왜? 부모 프로세스가 계속 그 메시지를 기다려야 하므로. 다른 방식을 알아보자
운영체제야! 네가 좀 알려줘
자식 프로세스의 종료의 인식주체는 운영체제이다. 그렇기 때문에 운영체제가 부모 프로세스에게 종료되었음을 알려주면 효율적이다. 이러한 프로그램 구현을 위해 시그널 핸들링(Signal Handling)이라는 것이 존재한다. 시그널이란 특정상황이 발생했음을 알리기 위해 운영체제가 프로세스에게 전달하는 메시지를 의미하고 핸들링또는 시그널 핸들링이란 특정 메시지와 관련하여 미리 정의된 작업을 진행 하는 것을 의미한다.
잠시 JAVA 얘기를 : 열려있는 사고를 지니자!
C나 C++은 프로세스나 쓰레드의 생성방법을 언어차원에서 지원하지 않는다(곧 ANSI 표준애서 이의 지원을 위한 함수를 정의하지 않고 있다). 반면 JAVA는 운영체제 독립적인 플랫폼 위에서 돌아가기 때문에 프로세스나 쓰레드를 생성하는 함수를 지원한다.
시그널과 signal 함수
프로세스가 운영체제에게 특정 시그널이 발생했을때 특정 함수를 호출을 요구할수 있게 해주는 함수가 signal 이란 함수이다. void (*signal(int signo, void (*func)(int)))(int); 형태로 시그널 발생시 호출되도록 등록된 함수의 포인터가 반환된다. signal 함수의 첫번째 인자인 signo에는 SIGALRM(alarm 함수호출을 통해서 등록된 시간이 된 상황) , SIGINT(CTRL+C가 입력된 상황), SIGCHLD(자식 프로세스가 종료된 상황)이 올수 있다.
주의 할것은 시그널이 발생되면 sleep 함수로 blocking되어 있던 프로세스는 깨어나고 다시 block 상태로 돌아가지 않는다. 책의 예제를 살펴보면 이를 확인할 수 있다. 또 하나 alarm함수를 이용해서 특정 시간 이후에 SIGALRM 시그널을 발생시킬 수 있는데, 예를 들어 alarm(2) 라고 한뒤 signal(SIGALRM, FUNC)이라고 했으면 2초뒤 FUNC 함수가 실행되어진다. 그런데 여기서 프로세스 자체가 2초동안 유지 되지 않는다면 프로그램은 FUNC 함수를 실행하지 않은 채로 종료된다.
sigaction 함수를 이용한 시그널 핸들링
signal 함수는 유닉스 계열의 운영체제 별로 동작 방식에 있어서 약간의 차이가 있기 때문에 보통 sigaction 함수를 쓴다. int signaction(int signo, const struct sigaction * act, struct sigaction * oldact); 형태로 signo에는 signal함수와 동일하게 signal 정보를 넣어준다 , act 인자에는 시그널 발생시 호출될 함수의 정보가 담긴 sigaction 구조체를 넣는데 sigaction 구조체의 한 변수에 함수 포인터가 있다.
시그널 핸들링을 통한 좀비 프로세스의 소멸
-멀티태스킹 기반의 다중접속 서버-
프로세스 기반의 다중접속 서버의 구현 모델
다중접속 에코 서버의 구현
fok 함수호출을 통한 파일 디스크립터의 복사
멀티 프로세스 기반의 다중 접속 서버를 코딩할때 처음의 코딩과 accept 함수까지는 동일하다. 그 이후로 fork 함수를 이용해서 프로세스를 생성하고 그 자식 프로세스에서 클라이언트와의 통신을 처리하게 한다. 다만 주의 해야 할점은 fork이후 자식 프로세스에서는 서버 소켓을 close 해야 하고 부모 프로세스에서는 클라이언트 소켓을 close해야 한다. fork를 통해 프로세스가 생성될때 메모리의 모든 내용이 복사 된다고 하였는데 소켓이 복사 되는것은 아니고 소켓을 의미하는 파일 디스크립트가 복사 되는 것이다(소켓은 운영체제의 소유이다). 그래서 각 소켓에 2번의 파일 디스크립트가 있는건데 두 파일 디스크립트가 소멸되야 종료시 소켓이 소멸된다. 그렇게에 미리 각 파일 디스크립트를 close하는 것.
-TCP 의 입출력 루틴 분할-
입출력 루틴 분할의 의미와 이점
입력과 출력 루틴을 분할해서 각각 다른 프로세스에서 각 루틴을 수행하게 하는 것이 프로그래밍 자체도 쉬워지고 속도도 통신의 속도도 빨라진다. 마찬가지로 fork통해 프로세스를 하나 더 생성되면 소켓에 대한 파일 디스크립터 역시 복사 되기 때문에 각 프로세스에서 닫아줘야 한다.
에코 클라이언트의 입출력 루틴 분할
chapter 9 (소켓의 다양한 옵션)
-소켓의 옵션과 입출력 버퍼의 크기-
소켓의 다양한 옵션
프로토콜 옵션은 계층별로 구별된다. protocol level로 SOL_SOCKET (소켓의 가장 일반적인 옵션),
getsockopt & setsockopt
getsockopt를 이용해서 옵션의 설정상태를 참조할수 있고 setsockopt함수를 이용해서 옵션을 설정할수 있다.
so_SNDBUF & SO_RCVBUF
-SO_REUSEADDR-
주소할당 에러(Binding Error) 발생
Time-wait 상태
TCP 소켓에서 두 호스트의 접속 종료를 할때 Four hand shaking이라는 단계를 거친다고 했다. 다시 한번 말하면 host A에서 host B로 접속 접속을 끊을 때 FIN 패킷이 전달되고 B에서는 A 한테 잠깐 기다리라는 ACK 패킷을 보낸다음 종료가 완료 됐음을 나타내는 FIN 패킷을 보내면 A에서는 FIN 패킷을 잘 받았다는 최종 ACK 패킷을 보내고 접속은 종료된다. 그런데 접속 종료를 먼저 시도한 호스트 그러니까 최종 ACK 패킷을 보내는 호스트 최종 ACK를 보내고 time-wait 상태에 있는데 이는 최종 ACK가 상대 호스트에게 잘 갔나 잠시 기다리는 것이다. 만약 최종 ACK가 제대로 전송이 되지 않았다면 상대 호스트는 FIN 패킷을 다시 보낼 것이기 때문에 이 FIN 패킷이 다시 오나 안오나 의무적으로 기다리는 시간이 time-wait 상태인 것이다.
주소의 재할당
이 time-wait 상태 때문에 서버측에서 접속 종료를 한뒤 바로 서버 프로그램을 시행하면 예전 프로그램에서 해당 PORT에 대한 time-wait 상태에 있기 때문에 bind error가 생긴다. 이는 서버에 문제가 생겨서 종료한 다음에 바로 서버를 재가동 시켜야 하는 상황에는 문제가 아닐수 없다. 이를 해결하기 위한 소켓 옵션이 SO_REUSEADDR이다. 디폴트로 SO_REUSEADDR이 0, 즉 false로 되어 있는데 이를 1, 즉 true로 설정하면 time-wait 상태에 있는 소켓에 할당되어 잇는 port 번호를 새로 시작 하는 소켓에 할당되게끔 할수 있다.
-TCP_NODELAY-
Nagle 알고리즘
nagle알고리즘은 네트워크 상에 패킷들의 흘러 넘침을 방지 하기 위해 제안된 알고리즘이다. 이는 기본적으로 TCP소켓에서 적용하는 것으로 패킷을 보내면 전송한 패킷에 대한 ACK 메시지를 받아야 다음 데이터를 전송한 것이다. 이는 ACK 메시지를 받을때까지 출력 버퍼에다가 데이터를 버퍼링 하기 때문에 적은 갯수의 패킷으로 데이터의 송수신이 가능하게 한다. 곧 네트워크 트래픽을 줄인다. 그러나 이 Nagle 알고리즘은 항상 좋은 것은 아니다. 일반적으로 출력버퍼로 데이터를 넣는데는 시간이 걸리지 않는다. 그렇기에 대용량 파일을 전송할 경우에는 nagle 알고리즘을 적용하나 안하다 출력버퍼에 최대로 데이터를 채우고 파일을 전송하게 되기 때문에 오히려 nagle 알고리즘을 적용하지 않고 전송하는 것이 빠르다.
Nagle 알고리즘의 중단
TCP_NODELAY의 설정값을 TRUE, 즉 1로 설정하면 된다.
소켓의 다양한 옵션
프로토콜 옵션은 계층별로 구별된다. protocol level로 SOL_SOCKET (소켓의 가장 일반적인 옵션),
getsockopt & setsockopt
getsockopt를 이용해서 옵션의 설정상태를 참조할수 있고 setsockopt함수를 이용해서 옵션을 설정할수 있다.
so_SNDBUF & SO_RCVBUF
-SO_REUSEADDR-
주소할당 에러(Binding Error) 발생
Time-wait 상태
TCP 소켓에서 두 호스트의 접속 종료를 할때 Four hand shaking이라는 단계를 거친다고 했다. 다시 한번 말하면 host A에서 host B로 접속 접속을 끊을 때 FIN 패킷이 전달되고 B에서는 A 한테 잠깐 기다리라는 ACK 패킷을 보낸다음 종료가 완료 됐음을 나타내는 FIN 패킷을 보내면 A에서는 FIN 패킷을 잘 받았다는 최종 ACK 패킷을 보내고 접속은 종료된다. 그런데 접속 종료를 먼저 시도한 호스트 그러니까 최종 ACK 패킷을 보내는 호스트 최종 ACK를 보내고 time-wait 상태에 있는데 이는 최종 ACK가 상대 호스트에게 잘 갔나 잠시 기다리는 것이다. 만약 최종 ACK가 제대로 전송이 되지 않았다면 상대 호스트는 FIN 패킷을 다시 보낼 것이기 때문에 이 FIN 패킷이 다시 오나 안오나 의무적으로 기다리는 시간이 time-wait 상태인 것이다.
주소의 재할당
이 time-wait 상태 때문에 서버측에서 접속 종료를 한뒤 바로 서버 프로그램을 시행하면 예전 프로그램에서 해당 PORT에 대한 time-wait 상태에 있기 때문에 bind error가 생긴다. 이는 서버에 문제가 생겨서 종료한 다음에 바로 서버를 재가동 시켜야 하는 상황에는 문제가 아닐수 없다. 이를 해결하기 위한 소켓 옵션이 SO_REUSEADDR이다. 디폴트로 SO_REUSEADDR이 0, 즉 false로 되어 있는데 이를 1, 즉 true로 설정하면 time-wait 상태에 있는 소켓에 할당되어 잇는 port 번호를 새로 시작 하는 소켓에 할당되게끔 할수 있다.
-TCP_NODELAY-
Nagle 알고리즘
nagle알고리즘은 네트워크 상에 패킷들의 흘러 넘침을 방지 하기 위해 제안된 알고리즘이다. 이는 기본적으로 TCP소켓에서 적용하는 것으로 패킷을 보내면 전송한 패킷에 대한 ACK 메시지를 받아야 다음 데이터를 전송한 것이다. 이는 ACK 메시지를 받을때까지 출력 버퍼에다가 데이터를 버퍼링 하기 때문에 적은 갯수의 패킷으로 데이터의 송수신이 가능하게 한다. 곧 네트워크 트래픽을 줄인다. 그러나 이 Nagle 알고리즘은 항상 좋은 것은 아니다. 일반적으로 출력버퍼로 데이터를 넣는데는 시간이 걸리지 않는다. 그렇기에 대용량 파일을 전송할 경우에는 nagle 알고리즘을 적용하나 안하다 출력버퍼에 최대로 데이터를 채우고 파일을 전송하게 되기 때문에 오히려 nagle 알고리즘을 적용하지 않고 전송하는 것이 빠르다.
Nagle 알고리즘의 중단
TCP_NODELAY의 설정값을 TRUE, 즉 1로 설정하면 된다.
Thursday, January 27, 2011
chapter 8 (도메인 이름과 인터넷 주소)
-Domain Name System-
IP주소와 도메인 이름 간의 변환하는 시스템을 DNS(Domain Name System) 이라고 한다
도메인 이름이란?
DNS 서버
도메인 이름은 가상 주소. 때문에 도메인으로 특정 서버에 접속 하려면 실제 주소인 IP주소로 변환 해야 한다. 이러한 변환을 담당하는 것이 DNS 서버. 보통 컴퓨터 마다 DNS 서버를 지정한다. 그런데 디폴트 DNS 서버가 모든 도메인의 IP를 알고 있지는 않다. 만약 내 컴퓨터가 정한 DNS 서버가 도메인의 IP를 모른다면 그 DNS 서버는 자기보다 상위의 DNS 서버에게 물어보고 결국 최상위 DNS 서버까지 가서 최상위 DNS 서버는 그 정보가 있는 DNS 서버까지 질문을 돌리고 다시 거꾸로 IP정보가 돌아온다.
-IP주소와 도메인 이름 사이의 변환-
프로그램상에서 도메인 이름을 쓸 필요가 있는가?
도메인 이름은 한번 등록하면 계속 쓰는 한편 IP 주소는 자주 변하니까
도메인 이름을 이용해서 IP주소 얻어오기
struct hostent * gethostbyname(const char * hostname); 란 함수를 사용하면 문자열 형태의 도메인 이름으로 IP주소를 얻을수 있다. 성공시 hostent 구조체 변수의 주소값이 실패시 NULL 포인트 반환. 이 구조체는 책 참조
IP주소를 이용해서 도메인 정보 얻어오기
반대로 IP주소에서 도메인 정보를 얻어 올때 쓰는 함수는 struct hostent * gethostbyaddr(const char * addr, socklen_t len, int family); 로 addr에는 in_addr 구조체 변수(IPv4 일때. 만약 다른 주소체계면 거기에 맞는 구조체 사용), len은 addr 인자의 길이, family는 주소체계(IPv4 경우 AF_INET 전달). 성공시 hostent 구조체 변수의 주소값 전달, 실패시 null 반환
IP주소와 도메인 이름 간의 변환하는 시스템을 DNS(Domain Name System) 이라고 한다
도메인 이름이란?
DNS 서버
도메인 이름은 가상 주소. 때문에 도메인으로 특정 서버에 접속 하려면 실제 주소인 IP주소로 변환 해야 한다. 이러한 변환을 담당하는 것이 DNS 서버. 보통 컴퓨터 마다 DNS 서버를 지정한다. 그런데 디폴트 DNS 서버가 모든 도메인의 IP를 알고 있지는 않다. 만약 내 컴퓨터가 정한 DNS 서버가 도메인의 IP를 모른다면 그 DNS 서버는 자기보다 상위의 DNS 서버에게 물어보고 결국 최상위 DNS 서버까지 가서 최상위 DNS 서버는 그 정보가 있는 DNS 서버까지 질문을 돌리고 다시 거꾸로 IP정보가 돌아온다.
-IP주소와 도메인 이름 사이의 변환-
프로그램상에서 도메인 이름을 쓸 필요가 있는가?
도메인 이름은 한번 등록하면 계속 쓰는 한편 IP 주소는 자주 변하니까
도메인 이름을 이용해서 IP주소 얻어오기
struct hostent * gethostbyname(const char * hostname); 란 함수를 사용하면 문자열 형태의 도메인 이름으로 IP주소를 얻을수 있다. 성공시 hostent 구조체 변수의 주소값이 실패시 NULL 포인트 반환. 이 구조체는 책 참조
IP주소를 이용해서 도메인 정보 얻어오기
반대로 IP주소에서 도메인 정보를 얻어 올때 쓰는 함수는 struct hostent * gethostbyaddr(const char * addr, socklen_t len, int family); 로 addr에는 in_addr 구조체 변수(IPv4 일때. 만약 다른 주소체계면 거기에 맞는 구조체 사용), len은 addr 인자의 길이, family는 주소체계(IPv4 경우 AF_INET 전달). 성공시 hostent 구조체 변수의 주소값 전달, 실패시 null 반환
chapter 7 (소켓의 우아한 연결종료)
-TCP 기반의 Half-close-
TCP에서 연결과정에서는 큰변수가 생기지 않지만 종료과정은 그렇지 않다. 따라서 종료과정은 명확해야 한다,
일방적인 연결종료의 문제점
close함수는 완전 종료를 의미. 이는 데이터의 전송뿐 아니라 수신하는 것 조차 불가능하게 한다.
half-close 란 데이터의 송수신에 사용되는 스트림의 일부만 종료하는 것으로 전송은 가능하지만 수신을 불가능 하거나 반대로 전송은 불가능 하지만 수신은 가능한 종료를 의미 한다.
소켓과 스트림
두 호스트가 소켓을 통해 연결된 상태를 '스트림이 형성된 상태'라고 한다. 좀더 자세하게 이야기 하자면 두 호스트는 두개의 스트림에 의해 연결된 상태이다. 호스트 A에서의 출력스트림은 호스트 B의 입력 스트림과 연결되고 A의 입력스트림은 B의 출력 스트림과 연결된 상태이다.
close 함수를 쓰면 이 두 스트림이 다 끊기는 경우고 half-close는 하나만 끊는거다.
우아한 종료를 위한 shutdown 함수
half-close를 위해 shutdown 함수를 사용한다. int shutdown(int sock, int howto); 의 형태로 성공시 0 실패시 -1 반환. 두번째 인자의 종류로는 SHUT_RD, SHUT_WR, SHUT_RDWR. RD는 입력스트림 종료(데이터 수신 불가능), WR는 출력 스트림 종료(데이터 송신 불가능), RDWR는 둘다 종료.
Half-close가 필요한 이유
서버가 클라이언트로 데이터를 전송하고 전송이 다 마치면 클라이언트라 서버로 메시지를 하나 던지는 상황을 생각해보면, 데이터 전송이 다 끝났다는 표시는 서버가 클라이언트로 EOF를 전송하는 것인데 close를 써서 EOF를 던지게 되면 마지막에 클라이언트로 부터의 메시지를 수신할 수 없게 된다. 그렇기에 출력 스트림만 종료하는 상황이 필요. 그럴때 shutdown 함수 사용.
Half-close 기반의 파일 전송 프로그램
*애매한게 있었는데 EOF를 던진다는 것은 사실은 -1을 던진다는 것이고 read 의 경우 EOF를 받게 되면 return을 0을 한다.
TCP에서 연결과정에서는 큰변수가 생기지 않지만 종료과정은 그렇지 않다. 따라서 종료과정은 명확해야 한다,
일방적인 연결종료의 문제점
close함수는 완전 종료를 의미. 이는 데이터의 전송뿐 아니라 수신하는 것 조차 불가능하게 한다.
half-close 란 데이터의 송수신에 사용되는 스트림의 일부만 종료하는 것으로 전송은 가능하지만 수신을 불가능 하거나 반대로 전송은 불가능 하지만 수신은 가능한 종료를 의미 한다.
소켓과 스트림
두 호스트가 소켓을 통해 연결된 상태를 '스트림이 형성된 상태'라고 한다. 좀더 자세하게 이야기 하자면 두 호스트는 두개의 스트림에 의해 연결된 상태이다. 호스트 A에서의 출력스트림은 호스트 B의 입력 스트림과 연결되고 A의 입력스트림은 B의 출력 스트림과 연결된 상태이다.
close 함수를 쓰면 이 두 스트림이 다 끊기는 경우고 half-close는 하나만 끊는거다.
우아한 종료를 위한 shutdown 함수
half-close를 위해 shutdown 함수를 사용한다. int shutdown(int sock, int howto); 의 형태로 성공시 0 실패시 -1 반환. 두번째 인자의 종류로는 SHUT_RD, SHUT_WR, SHUT_RDWR. RD는 입력스트림 종료(데이터 수신 불가능), WR는 출력 스트림 종료(데이터 송신 불가능), RDWR는 둘다 종료.
Half-close가 필요한 이유
서버가 클라이언트로 데이터를 전송하고 전송이 다 마치면 클라이언트라 서버로 메시지를 하나 던지는 상황을 생각해보면, 데이터 전송이 다 끝났다는 표시는 서버가 클라이언트로 EOF를 전송하는 것인데 close를 써서 EOF를 던지게 되면 마지막에 클라이언트로 부터의 메시지를 수신할 수 없게 된다. 그렇기에 출력 스트림만 종료하는 상황이 필요. 그럴때 shutdown 함수 사용.
Half-close 기반의 파일 전송 프로그램
*애매한게 있었는데 EOF를 던진다는 것은 사실은 -1을 던진다는 것이고 read 의 경우 EOF를 받게 되면 return을 0을 한다.
Subscribe to:
Posts (Atom)