Devin.KR
로그인

리눅스 lsof 명령어 사용법 - 포트 점유 프로세스와 삭제된 파일 찾기

개발자 조회 2

이 명령어를 언제 쓰는가

리눅스에서는 소켓도, 파이프도, 디바이스도 전부 파일이다. lsof(list open files)는 지금 열려 있는 모든 파일과 그것을 연 프로세스를 보여 준다. 그래서 겉보기에 전혀 달라 보이는 세 가지 장애를 같은 명령으로 해결한다.

첫째, 배포하려는데 "Address already in use"가 뜬다. 둘째, umount가 "target is busy"로 거부된다. 셋째, 큰 로그를 지웠는데 df 수치가 그대로다. 셋 다 "누가 이걸 붙잡고 있는가"라는 같은 질문이고, 답은 lsof에 있다.

기본 형식

lsof [옵션] [파일 또는 디렉터리]

lsof에는 다른 명령과 다른 중요한 규칙이 있다. 여러 선택 옵션을 나열하면 AND가 아니라 OR로 결합된다. lsof -u deploy -i TCP는 "deploy가 연 TCP 소켓"이 아니라 "deploy가 연 모든 파일 또는 모든 TCP 소켓"이다. AND로 묶으려면 -a를 명시해야 한다.

자주 쓰는 옵션

옵션의미예시
-i :포트해당 포트를 쓰는 네트워크 소켓lsof -i :8080
-sTCP:상태TCP 연결 상태로 필터(-i와 함께 쓴다)lsof -nP -iTCP -sTCP:LISTEN
-nIP를 호스트명으로 역조회하지 않는다lsof -n -i
-P포트를 서비스명 대신 번호로 표시lsof -nP -i
-p PID특정 프로세스가 연 파일lsof -p 21874
-u 사용자특정 사용자가 연 파일lsof -u deploy
-c 이름명령 이름으로 필터lsof -c nginx
+D 경로디렉터리 하위 전체를 재귀 검사lsof +D /mnt/data
+L1링크 수가 1 미만, 즉 삭제된 파일lsof +L1
-tPID만 출력. 파이프용lsof -t -i :8080
-a앞뒤 조건을 AND로 결합lsof -a -u deploy -i TCP

실전 예제

1) 포트를 이미 쓰고 있는 프로세스를 찾는다. 가장 많이 쓰는 용도다. -n -P를 붙이는 이유는 아래 함정에서 설명한다.

sudo lsof -nP -i :8080
COMMAND    PID   USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
java     21874 deploy   42u  IPv6  183920      0t0  TCP *:8080 (LISTEN)

2) umount를 막는 프로세스를 찾는다. 스토리지 교체나 볼륨 분리 작업에서 필수다. 프로세스가 그 디렉터리를 현재 작업 디렉터리로 쓰고 있는 경우도 잡힌다.

sudo lsof +D /mnt/data
sudo fuser -vm /mnt/data

3) 지웠는데 공간이 안 도는 파일을 찾는다. df와 du 합계가 크게 어긋날 때의 정답이다. SIZE/OFF 컬럼이 아직 잡혀 있는 용량이다.

sudo lsof -nP +L1 2>/dev/null | grep -i deleted
COMMAND    PID   USER   FD   TYPE DEVICE   SIZE/OFF NLINK   NODE NAME
java     21874 deploy    3w   REG  253,0 8271130624     0 265301 /var/log/myapp/app.log (deleted)

가장 안전한 해결은 해당 프로세스 재시작이다. 재시작이 어려우면 파일 디스크립터를 통해 내용만 비운다. FD 컬럼의 숫자(위에서는 3)를 쓴다.

sudo sh -c ': > /proc/21874/fd/3'

4) 특정 사용자의 네트워크 연결만 본다. -a 없이 쓰면 전혀 다른 결과가 나온다.

sudo lsof -a -u deploy -i TCP

5) 프로세스가 어떤 설정 파일을 실제로 읽었는지 확인한다. 설정을 고쳤는데 반영이 안 될 때, 프로세스가 정말 그 파일을 보고 있는지 확인한다.

sudo lsof -p 21874 | grep -E '\.(conf|properties|yml|yaml)$'

6) 포트를 잡은 프로세스를 바로 정리한다. 급할 때 쓰지만, 무엇이 잡혀 있는지 먼저 눈으로 확인한 뒤에 친다.

sudo kill $(sudo lsof -t -i :8080)

함정과 주의점

옵션은 기본이 OR다. 앞에서 언급한 이 규칙이 lsof 오용의 최대 원인이다. lsof -u deploy -i TCP가 수천 줄을 뱉는데 그중 상당수가 deploy와 무관한 TCP 소켓이다. 조건을 좁힐 때는 -a를 습관처럼 붙인다.

sudo 없이 쓰면 "아무도 안 쓴다"는 오답이 나온다. 일반 사용자로 실행하면 자기 소유 프로세스만 보인다. 8080을 root 권한 프로세스가 잡고 있는데 결과가 비어 있으면 포트가 비었다고 착각하게 된다. lsof는 sudo로 쓰는 것이 기본이다.

-n -P를 안 붙이면 멈춘 것처럼 느려진다. lsof는 기본적으로 모든 IP를 DNS로 역조회하고 포트 번호를 /etc/services에서 서비스명으로 바꾼다. 외부 연결이 많은 서버에서 DNS 응답이 느리면 명령이 수십 초씩 걸린다. 네트워크 관련 조회에는 언제나 -nP를 붙인다.

+D는 느리다. 지정한 디렉터리 트리 전체를 stat 하기 때문에 파일이 수백만 개인 경로에서는 실용적이지 않다. 한 단계만 볼 거면 +d를, 마운트 지점을 통째로 볼 거면 훨씬 빠른 fuser -vm이나 lsof /mnt/data(마운트 지점 자체 지정)를 쓴다.

lsof가 기본 설치가 아닐 수 있다. 최근 Ubuntu 최소 이미지와 컨테이너 베이스 이미지에는 lsof가 빠져 있다. 포트 확인만 필요하다면 iproute2의 ss가 기본 설치이고 더 빠르다. ss -lntp 한 줄이면 리스닝 포트와 프로세스가 나온다. lsof의 진가는 포트가 아니라 파일을 다룰 때다.

함께 보면 좋은 명령어

  • ss — 포트와 소켓만 볼 거라면 이쪽이 빠르고 기본 설치다. ss -lntp를 먼저 쓴다.
  • fuser — 마운트 지점이나 파일을 쓰는 프로세스를 빠르게 지목한다. -k로 바로 죽일 수도 있다.
  • df — 삭제된 열린 파일 문제는 df와 du의 불일치로 처음 발견된다. lsof는 그 원인을 확인하는 단계다.

lsof(8) 공식 man 페이지