top 사용법

|

시스템 모니터링

top는 시스템 모니터링을 하는 명령어입니다. 시스템의 부하 관련 정보를 수초마다 체크하여 다음과 같이 화면에 갱신해줍니다.

image

  • load average : CPU가 처리하는 걸 기다리는 작업 개수. 1 분당 평균으로 몇 개의 일이 쌓이는지 나타냄
  • TIME+ : 해당 프로세스가 실제로 CPU를 사용하는 시간
  • COMMAND : 프로세스가 실행되었을 때 실행한 명령어 커맨드. C를 눌러 상세 표시 전환 가능


프로세스에 대한 내용

항목 내용
PID 프로세스 ID
USER 프로세스를 실행한 사용자 ID
PR 프로세스 우선 순위
NI 작업 수행의 Nice Value 값으로 마이너스를 갖는 값이 우선 순위가 높음
VIRT 가상 메모리 사용량(SWAP + RES)
RES 현재 페이지의 상주 크기(Resident Size)
SHR 분할된 페이지로 프로세스에 의해 사용된 메모리를 나눈 메모리의 총합
S 프로세스의 상태. S(Sleeping), R(Running), W(Swapped out process), Z(Zombies) 등의 상태를 가짐
%CPU CPU 사용률
%MEM 메모리 사용률


단축키

단축키 설명
Shift + M 메모리 소비량 순으로 정렬
Shift + T CPU 실행 시간 순으로 정렬
Shift + P CPU 점유량 순으로 정렬
Space 화면 갱신

파이프라인(Pipeline) 사용법

|

파이프라인

파이프라인은 어떤 명령의 실행 결과 출력을 그대로 다른 명령어에 전달하는 것을 의미합니다. 예를 들어 엄청난 양의 Log가 있다고 할 때, 여기서 원하는 단어가 들어간 라인만 필터링하고, 그 결과에서 또 다른 검색어로 필터링해서 그 결과를 조회하는 것도 파이프라인을 사용하는 것이라고 생각할 수 있습니다.

안드로이드의 logcat의 예를 들어보겠습니다.

adb shell로 안드로이드 쉘(Shell)에 접속한 다음

logcat

을 입력하면 엄청난 양의 Log가 화면에 출력이 됩니다. 눈으로 쫓아가기도 힘들 정도인데, 여기에 grep을 이용해서 필터링을 해보도록 하겠습니다.

logcat | grep "snowdeer"
여기서 ‘ ‘는 파이프라인을 의미합니다. 양쪽의 명령어를 연결해주는 역할을 합니다. 즉, logcat으로 나온 결과를 grep "snowdeer"로 다시 필터링을 하도록 만들어줍니다.

파이프라인은 다음과 같이 여러 개 연결할 수 있습니다.

logcat | grep "snowdeer" | grep -v "ignore"

grep-v 옵션은 해당 검색어를 제외하라는 옵션입니다.

그리고 만약, 마지막 결과를 less와 같은 텍스트뷰어에서 조회하는 것도 가능합니다. (안드로이드 Shell에는 less가 없습니다.)

명령어 | grep "snowdeer" | grep -v "ignore" | less


tail

실시간으로 바뀌는 파일의 끝 부분만 출력하는 명령어로 tail이 있습니다. (마찬가지로 안드로이드 Shell에는 없습니다.)

tail -F access.log

라고 하면, ‘access.log’ 파일이 갱신될 때마다 추가된 내용을 실시간으로 갱신해서 보여주는 기능을 합니다. -F 옵션은 해당 파일의 변경을 감시하라는 옵션입니다.

여기에도 마찬가지로 파이프라인으로 추가 필터링을 걸어줄 수 있습니다.

ldd 사용법

|

실행 파일은 있는데 필요한 라이브러리가 없는 경우

실행 파일은 있는데 필요한 라이브러리가 없는 경우 다음과 같은 오류 메세지를 출력합니다.

./snowdeer: error while loading shared libraries: libglog.so.0: cannot open shared object file: No such file or directory

위 오류 메세지는 libglog.so.0 이라는 동적 라이브러리가 없다는 메세지입니다. 이 경우 해당 라이브러리를 시스템 폴더(주로 ‘/usr/lib’ 아래)에 복사해주면 해결이 됩니다.

다만, 프로그램을 실행했을 때 오류 메세지로는 필요한 라이브러리를 1개씩밖에 확인이 안되게 때문에 많이 번거롭습니다. 이 경우 ldd 명령어를 이용하면 필요한 라이브러리 리스트를 모두 확인할 수 있습니다.


List Dynamic Dependencies`

ldd 명령어는 프로그램이 사용하고 있는 공유 라이브러리(Shared Library) 리스트를 출력합니다. ‘List Dynamic Dependencies’의 약자입니다.

? ldd ./snowdeer
	/usr/lib/arm-linux-gnueabihf/libarmmem.so (0xb6f3f000)
        libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xb6f04000)
        libglog.so.0 => not found
        libprotobuf.so.9 => not found
        libboost_system.so.1.55.0 => /usr/lib/arm-linux-gnueabihf/libboost_system.so.1.55.0 (0xb6ef1000)
        libcaffe.so.1.0.0 => not found
        libasound.so.2 => /usr/lib/arm-linux-gnueabihf/libasound.so.2 (0xb6e17000)
        libopencv_core.so.2.4 => /usr/lib/arm-linux-gnueabihf/libopencv_core.so.2.4 (0xb6bfe000)
        libopencv_highgui.so.2.4 => /usr/lib/arm-linux-gnueabihf/libopencv_highgui.so.2.4 (0xb6bad000)
        libopencv_objdetect.so.2.4 => /usr/lib/arm-linux-gnueabihf/libopencv_objdetect.so.2.4 (0xb6b32000)
        libopencv_imgproc.so.2.4 => /usr/lib/arm-linux-gnueabihf/libopencv_imgproc.so.2.4 (0xb68e3000)
        libopencv_video.so.2.4 => /usr/lib/arm-linux-gnueabihf/libopencv_video.so.2.4 (0xb6889000)
        libwiringPi.so => /usr/lib/libwiringPi.so (0xb686b000)
        ...

위와 같이 snowdeer라는 파일이 사용하는 라이브러리 리스트가 출력이 되며, 라이브러리 이름 옆에 위치가 출력이 됩니다. 만약 라이브러리가 없다면 이름 옆에 => not found 라는 메세지가 출력됩니다. 즉, 필요한 라이브러리들을 확인해서 시스템 폴더 아래 복사를 해주면 됩니다.


사용 예제

ldd ./snowdeer | grep "not"

다만 ldd 명령어로도 필요한 라이브러리들이 한 번에 안 나오는 경우도 종종 있는 것 같습니다. 그럴 때는 필요한 라이브러리들을 먼저 설치한다음 다시 ldd 명령어로 확인할 수 있습니다.

네이글 알고리즘(Nagle Algorithm)

|

일반적인 TCP 통신 방법

TCP 통신은 상대방이 패킷을 받았는지 안 받았는지 확인하기 위해서, 데이터를 받은 쪽에서 ACK 신호를 보냅니다. ACK 신호를 확인해야 송신부에서는 패킷 전송이 제대로 되었음을 확인하고 그 다음 패킷을 계속해서 전송할 수 있습니다.


Nagle Algorithm

네이글 알고리즘은 ‘가능하면 조금씩 여러 번 보내지 말고, 한 번에 많이 보내라’는 원칙을 기반으로 만들어진 알고리즘입니다.

기존에는 ‘NAGLE’라는 단어를 패킷으로 보낸다고 할 때, ‘N’을 전송한 다음 상대방으로부터 ACK를 받으면 ‘A’를 전송. 또 `ACK’를 받은 다음 ‘G’를 전송하는 형태로 이루어집니다. 이런식으로 반복해서 한 글자씩 차례대로 전송하게 됩니다.

하지만, 네이글 알고리즘을 적용하게 되면 처음에 ‘N’을 전송한다음 상대방으로부터 ACK를 받기 전까지 ‘AGLE’에 대한 패킷 정보를 송신 버퍼에 저장한다음 ACK가 오면 여러 개의 패킷을 모아서 한 번에 전송하게 됩니다.

image


네이글 알고리즘의 장단점

네이글 알고리즘의 장단점은 다음과 같습니다.

  • 장점 : 같은 양의 데이터더라도 한 번에 많이 보내기 때문에 데이터 전송 횟수가 줄어들기 때문에 네트워크의 효율성이 높아짐
  • 단점 : ACK를 받을 때까지 패킷을 모으고 있기 때문에 반응 속도가 느려짐


C++ 코드 예제

C++에서 TCP 소켓 통신은 기본적으로 네이글 알고리즘이 적용되어 있습니다. setsockopt() 함수를 이용해서 네이글 알고리즘을 On/Off 할 수 있습니다.

다음 예제는 네이글 알고리즘을 ‘Off’하는 예제입니다. (값이 True이면 ‘Off’라는 것을 주의합시다.)

int opt_val = TRUE;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt_val, sizeof(opt_val));
  • 값이 TRUE 또는 1 이면 Off
  • 값이 FALSE 또는 2 이면 On

소켓 옵션 설정하기(setsockopt)

|

setsockopt

소켓의 송수신 동작을 setsockopt() 함수를 이용해서 다양한 옵션으로 제어할 수 있습니다.

int setsockopt(SOCKET socket, int level, int optname, const void* optval, int optlen);
  • socket : 소켓의 번호
  • level : 옵션의 종류. 보통 SOL_SOCKETIPPROTO_TCP 중 하나를 사용
  • optname : 설정을 위한 소켓 옵션의 번호
  • optval : 설정 값이 저장된 주소값.
  • optlen : optval 버퍼의 크기


예제

예를 들어 SO_REUSEADDR을 설정하려면 다음과 같이 호출합니다.

int reuseAddress = 1;
setsockopt(socket, SOL_SOCKET, (const char*)&reuseAddress, sizeof(reuseAddress));


SOL_SOCKET 레벨

setsockopt() 함수의 level에 들어갈 값입니다.

자료형 설명
SO_REUSEADDR BOOL 이미 사용된 주소를 재사용하도록 함
SO_RCV_BUF int 수신용 버퍼의 크기 지정
SO_SND_BUF int 송신용 버퍼의 크기 지정
SO_RECVTIMEO DWORD(timeval) 수신시 Blocking 제한 시간을 설정
SO_SNDTIMEO DWORD(timeval) 송신시 Blocking 제한 시간을 설정
SO_KEEPALIVE BOOL TCP 통신에서만 유효. 일정 시간마다 연결 유지 상태를 체크.
SO_LINGER struct LINGER 소켓을 닫을 때 남은 데이터의 처리 규칙 지정
SO_DONTLINGER BOOL 소켓을 닫을때 남은 데이터를 보내기 위해서 블럭되지 않도록 함
SO_DONTROUTE BOOL 라우팅(Routing)하지 않고 직접 인터페이스로 전송
SO_BROADCAST BOOL 브로드캐스트 사용 가능 여부


SO_REUSEADDR 옵션

소켓 사용시 만약 다음과 같은 에러가 발생하는 경우가 있습니다.

bind error : Address already in use

보통 소켓을 사용하는 프로그램은 강제 종료되었지만, 커널단에서 해당 소켓을 바인딩해서 사용하고 있기 때문에 발생하는 에러입니다.

이 경우 SO_REUSEADDR 옵션을 이용해서 기존에 바인딩된 주소를 다시 사용할 수 있게 할 수 있습니다.


TCP_NODELAY 옵션

IPPROTO_TCP 레벨의 옵션이며, TCP 소켓에서만 사용할 수 있습니다.

자료형 설명
TCP_NODELAY BOOL(int) 소켓에 네이글 알고리즘(Nagle Algorithm)을 사용할지 여부를 지정. TRUE인 경우 알고리즘을 사용하지 않음. 알고리즘을 사용하지 않을 경우 실제 전송까지의 지연 시간이 줄어듬.

값이 1 또는 TRUE인 경우 네이글 알고리즘을 사용하지 않습니다. 값이 2 또는 FALSE인 경우 네이글 알고리즘을 사용합니다.

예제 코드는 다음과 같습니다.

int opt_val = TRUE;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt_val, sizeof(opt_val));


SO_LINGER 옵션

linger 구조체는 다음과 같은 형태로 되어 있습니다.

struct linger {
  int l_onoff;
  int l_linger;
}
  • l_onoff : linger 옵션의 On/Off 여부
  • l_linger : 기다리는 시간

위의 두 개의 변수 값에 따라 3 가지의 close 방식이 존재합니다.

  1. l_onoff == 0 : 소켓의 기본 설정 l_linger에 관계없이 버퍼에 있는 모든 데이터를 전송. close()는 바로 리턴을 하지만 백그라운드에서 이러한 작업이 이루어짐.

  2. l_onoff > 0, l_linger == 0 : close()는 바로 리턴하며, 버퍼에 있는 데이터는 버림. TCP 연결 상태에서는 상대편 호스트에게 리셋을 위한 RST 패킷 전송. hard 혹은 abortive 종료라고 부름.

  3. l_onoff > 0, l_linger > 0 : 버퍼에 남아있는 모든 데이터를 보내며, 그 동안 close()는 블럭되어 대기함.