단일 프로세스 서버의 한계
클라이언트에서 입력한 순서대로 서버에서 데이터를 받는 것이 아닌
첫번째로 접속한 클라이언트의 데이터를 다 기록을 하고
두번째로 접속한 클라이언트의 데이터를 받음
서버는 클라이언트를 하나만 받아서 다른 클라이언트는 기다려야함
Parent/Child process
fork
Fork함수를 통해 프로세스 생성이 가능함
Fork함수가 호출되면, 호출한 프로세스가 복사됨
Fork함수호출 이후, 각각의 프로세스가 독립적으로 실행하게 됨
자식프로세스에는 독립적인 메모리 공간이 만들어짐
#include <unistd.h>
pid_t(int형태임) fork()
Return Value
-성공시
부모 프로세스: 자식프로세스의 pid 값을 리턴
자식 프로세스: 0을 리턴
-에러 발생시,
-1을 리턴
부모프로세스에서는 else문에 들어가서 lval 값이 26이 되고 자식프로세스에서는 gval 값이 12가 된다.
Child 프로세스 종료
-Child 프로세스가 종료되면, Parent 프로세스에 해당 정보가 전송됨
-Parent 프로세스는 wait함수를 이용하여 Child 프로세스의 변화를 알 수 있음
#include <sys/wait.h>
pid_t wait(int *status)
Argument
-status: 프로세스 상태를 저장하기 위한 인자(status에 대한 정보를 미리 선언한 매크로를 통해 확인할 수 있음)
WIFEXITED(status): 자식 프로세스가 정상 종료한 경우 참 반환
WEXITSATUS(status): 자식 프로세스의 반환 값
WIFSIGNALED(status): 자식 프로세스가 시그널에 의해 종료한 경우 참 반환
Return value
-상태 변화가 발생한 자식 프로세스의 식별 자
성공적으로 실행되었을 때
자식프로세스에서 3을 리턴했으므로 WEXITSTATUS(status)의 값은 3임
자식 프로세스에서는 1, 부모 프로세스에서는 0이 출력됨 (독립된 메모리 공간을 사용)
Signal에 의해 종료되었을 때
Child의 PID를 출력하고(kill하기 위해), signal에 의한 종료시 메세지 출력하도록 코드 수정
출력된 자식프로세스를 kill 했을때, 시그널에 의해 종료되었다는 메시지가 출력됨
Multi-process-based server
fork()후에 부모 프로세스는 서비스 소켓을 닫고, 자식 프로세스는 듣기 소켓을 닫음
보낸 순서에 따라 실시간으로 반영됨, 이것을 응용 하면 채팅프로그램이 만들어진다
멀티프로세스 기반 UDP 채팅 서버 프로그램
UDP 채팅 서버의 역할(단일 프로세스)
-클라이언트 접속 처리
-클라이언트가 전송하는 문자열을 다른 클라이언트들에게 전송
서버는 클라이언트의 IP주소와 포트번호를 클라이언트 리스트로 관리함
최대 10개까지 클라이언트를 관리함
클라이언트 리스트 삭제는 생략함(구현의 용이를 위해)
-클라이언트 접속 종료 처리
UDP 채팅 클라이언트의 역할(멀티 프로세스)
-사용자에게 문자열을 입력받음
-서버로부터 수신한 문자열을 화면에 출력
UDP 채팅 클라이언트 프로그램 예제
사용자에게 문자열을 입력 받음
서버로부터 수신한 문자열을 출력함
fgets와 recvfrom이 IO와 관련된 것이기 때문에, concurrent하게 일어하게 하기 위해 멀티프로세스를 사용
부모 프로세스는 fgets를 통해 사용자의 입력을 받고 데이터를 서버로 보냄
자식 프로세스는 서버로부터 데이터를 받고 사용자에게 데이터를 보여줌
사용자는 내가 데이터를 입력하면서 다른 사용자가 보낸 데이터를 볼 수 있음
IPC(Inter Process Communications)
Signal
-IPC에서 사용되는 메시지 형태, UNIX관련 운영체제에서 사용됨
시그널 기반 이벤트 처리
Signal의 종류
kill -l 명령어로 시그널의 종류를 확인할 수 있다. man 7 signal로도 가능
Signal 함수
#include <signal.h>
void(*signal(int sig, void(*func)(int)))(int);
Arguments
sig: signal의 종류를 지칭하는 시그널 번호
void (*func)(int): signal이 발생했을 때 호출되는 함수 값
Signal 함수 예제
터미널에서 ctrl+c 입력, SIGINT(시그널인터럽트) 시그널 발생
터미널에서 ctrl_c 10번 입력 까지 이벤트 msg 출력, 11번째부터는 프로세스 종료
무한루프를 돌다그 ctrl+c를 통해 sigint가 10번 발생하면 프로그램 종료
signal함수를 등록하지 않으면 handler를 호출하지 않기 때문에 바로 종료된다
sigaction 함수
#include <signal.h>
int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oldact);
Arguments
sig: signal 번호
act: signal 발생 시 호출되는 signal 핸들러 포인터
oldact: 이전에 등록되었던 signal 핸들러 포인터
Return value
성공 시, 0을 리턴
에러 발생시, -1을 리턴
sigaction구조체
#include<signal.h>
struct sigaction{
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t*, void*);
int sa_flags; /*signal options*/
sigset_t sa_mask; /*signal mask to apply blocking signals */
};
sigaction 함수 예제
sigaction으로 signal이 발생했을때 ignore하도록 설정해더서 ctrl+c를 눌러도 종료되지 않는 것을 볼 수 있음
좀비(defunct) 프로세스
부모 프로세스에서 end를 통해 자식프로세스를 종료했지만 운영체제 리스트에는 남아있음
부모프로세스는 자식프로세스를 종료하고 wait를 통해 그 값을 가져와야함
하지만 부모프로세스가 무조건 wait함수를 호출하자 이렇게 접근하면, 부모 프로세스는 자식 프로세스가 종료될 때까지 waiting을 하고 있어야 하기 때문에 쓸데없는 자원낭비 발생
좀비 프로세스 방지(과제)
부모 프로세스를 대기 상태로 만들지 않고, 적절히 wait함수를 사용해야 함 → signal 사용
SIGCHID이용 (handler에서 wait함수 호출)
시그널 발생
#include <signal.h>
int kill(pid_t pid, int sig);
Arguments
pid: 시그널을 전송하려고 하는 목적지 프로세스의 PID
sig: 시그널의 종류
Return value
성공시, 0을 리넡
에러 발생 시, -1을 리턴