socket programming 수업 들을 때 공부 좀 열심히 해둘 걸..
지나고 하는 후회는 다 부질없다. 이제라도 부끄러움을 무릅쓰고 정리 해보려 한다.
소켓 인터페이스는 UNIX 계열 시스템에서 함수의 통신을 위해 설계한 함수의 집합이다. 소켓 통신을 이해하려면 먼저 이 소캣 인터페이스가 어떻게 이루어져 있는지 이해해야 한다.
1. 소켓 연결 과정
1-1. socket() : 소캣 생성 + 파일 디스크립터 할당
file desciptor
유닉스 계열 시스템에서 모든 것은 파일이다. 소켓, 디렉토리 등등 프로세스의 모든 것은 파일로 관리되고, 이들 프로세스의 open file 을 관리하는 user file descriptor table의 인덱스를 파일의 식별자로 사용한다. 이를 file descriptor 이라고 부르는데, 프로세스가 system call¹ 할 때 이 0이 아닌 정수값 (0 < .. < OPEN_MAX) 인덱스를 보고 구조체 파일 (struct file fd_array) 에 접근하여 파일을 찾을 수 있다.
대학 수업 때 user space 와 kernel space 가 다르다는 것을 배웠다. 그리고 커널은 인터럽트를 통해서 모든 하드웨어 접근이 가능하나 응용 프로그램에서 이를 사용하려면 커널에 접근해야 한다고 배웠다.
시스템 호출이란 프로그래밍 언어에서 지원하지 않는 기능에 대하여 운영 체제의 루틴을 호출하여 이용하는 것을 말한다.
<기능>
1. 사용자 모드에 있는 응용 프로그램이 커널의 기능을 사용할 수 있도록 한다.
2. 시스템 호출을 하면 사용자 모드에서 커널 모드로 바뀐다.
3. 커널에서 시스템 호출을 처리하면 커널 모드에서 사용자 모드로 돌아가 작업을 계속한다.
※ 시스템 호출의 유형
프로세스 제어(process control)
파일 조작(file manipulation)
장치 관리(device management)
정보 유지(information maintenance)
통신(communication)
소켓도 마찬가지로 socket()을 호출하면 fd 가 할당된다. 그리고 이 fd는 소켓 구조체를 가리키고 있다. 구조체에는 포트 넘버, ip 등 정보가 포함된다.
1-2. server socket -> bind() / listen() / accept()
서버 소켓의 행동은 다음과 같다.
- 소켓fd와 소켓 구조체에 나온 정보를 연결하는 bind()
- 서버 소켓으로서 연결을 기다리도록 (listening socket) 명시하는 listen()
- listening socket의 fd와 대기열에 있던 클라이언트의 주소를 주고 연결을 수락하는 단계인 accept()
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
#include <sys/socket.h>
int listen(int sockfd, int backlog); // backlog 는 대기열의 크기
1-3. client socket -> connect()
클라이언트 소캣은 connect()로 서버 소캣과의 연결을 요청한다.
#include <sys/socket.h>
int connect(int clientfd, const struct sockaddr *addr,
socklen_t addrlen);
이제 위 코드를 이해할 수 있게 되었다. 야호!
TCP 소켓이 연결을 맺는 과정은 https://squeak-squeak.tistory.com/35 여기에 정리했다. 이 과정과 관련한 서버 튜닝값에 대해 정리한 글이다.
2. 커널 튜닝 시 fd 값의 의미
위의 내용을 바탕으로 생각해보면 서버가 처리할 수 있는 커넥션 수를 늘리고 싶다면, 동시에 연결 맺을 수 있는 max client 값만 튜닝할 것이 아니라 이 fd 수도 따라 늘려줘야 할 것 같다.
- 시스템에 할당된 파일 디스크립터 수. (max 값은 65536 라고 한다 참고~) 늘리기
- 리눅스의 경우 각 사용자 별로 이 값을 설정할 수 있기 때문에 사용자 레벨에서의 df limit 늘려주기
또한, file descriptor 값의 한도를 늘려줌과 동시에 시스템이 사용할 수 있는 최대 워커 스레드 수도 늘려줘야 합당할 것이다.
(thread_max). 실제로 회사에서 부하테스트를 거쳐 튜닝했던 vm 값 중에 fd가 있었다. 사실 이 글은 거기서 출발했다.
정리해놓고 보니 학생 때 나는 운영체제, 리눅스 시스템 설계, 네트워크 수업 시간에 도대체 뭘 한 건지.. 내 인생에서 가장 헛살았던 4년이 대학 다니던 시절이 아니었나 싶다. 그래도 포기하지 않고 개발자 하고 있는 걸 보면 신기하기도 하고 대단하기도 하고...운이 좋았던 것 같다.
참고한 글
https://dev-ahn.tistory.com/96
https://velog.io/@whwogur/파일-디스크립터File-descriptor-소켓-프로그래밍