운영체제 기초 입문
목표: “좀비 프로세스예요”, “OOM으로 죽었네요”, “
kill -9하세요”, “실행 권한이 없대요”, “SSH 끊기니까 학습이 죽었어요” 같은 말을 듣고 OS 안에서 무슨 일이 일어났는지 떠올리고, 명령으로 직접 확인할 수 있기.
터미널·셸·환경 변수가 낯설다면 컴퓨터 기본 도구와 개발 환경 입문을 먼저 읽는다. 이 노트는 Docker 입문과 원격 GPU 서버 사용 입문으로 이어진다.
한 줄로 먼저
운영체제(OS)는 한정된 하드웨어를 여러 프로그램이 안전하게 나눠 쓰게 관리하고, 각 프로그램에게 “혼자 쓰는 것 같은 컴퓨터”를 보여 주는 프로그램이다.
| 진짜 자원 | OS가 보여 주는 모습 | 확인 명령 |
|---|---|---|
| CPU 코어 | 프로세스·스레드 | ps, top, htop |
| RAM | 프로세스마다 따로인 가상 메모리 | free(Linux), vm_stat(macOS) |
| 디스크 | 파일과 디렉터리 | ls -l, df, du |
| 파일·화면·네트워크 | 번호로 다루는 파일 디스크립터 | lsof |
| 여러 사람 | 사용자·그룹과 권한 | id, chmod |
0. OS가 하는 일: 자원 관리자
교과서 OSTEP은 OS의 일을 셋으로 나눈다.
- 가상화(virtualization): CPU와 메모리를 여러 프로그램이 각자 독차지한 것처럼 보이게 한다.
- 동시성(concurrency): 여러 일이 동시에 진행될 때 서로 꼬이지 않게 한다.
- 영속성(persistence): 전원이 꺼져도 남도록 데이터를 파일로 디스크에 저장한다.
OS는 호텔 지배인과 같다. 손님(프로그램)은 방 번호만 알고, 방 배정과 출입 통제는 지배인이 한다.
1. 커널, 사용자 공간, 시스템 콜
커널(kernel) 은 OS의 핵심이다. 컴퓨터가 켜져 있는 내내 메모리에 상주하며 하드웨어를 직접 다룬다.
CPU에는 권한 단계가 있다. 커널 모드에서는 모든 메모리와 장치에 접근할 수 있고, 커널만 이 모드로 돈다. 사용자 모드에서는 하드웨어를 직접 건드리는 명령이 막혀 있다. 셸, Python, 브라우저가 모두 여기서 돈다.
커널 바깥의 모든 것이 사용자 공간(user space) 이다. 이 구분 덕분에 프로그램 하나가 잘못돼도 컴퓨터 전체가 멈추지 않는다. 사용자 프로그램이 커널에 일을 부탁하는 창구가 시스템 콜(system call) 이다.
open("data.csv").read() ← Python
→ libc (C 표준 라이브러리)
→ 시스템 콜 openat(), read() ← 여기서 사용자 모드 → 커널 모드
→ 커널이 디스크에서 읽어 돌려줌대표적인 시스템 콜은 open·read·write·close(파일), fork·execve·wait·exit(프로세스), mmap(메모리), kill(시그널)이다. Linux에서는 strace python a.py로 프로그램이 부르는 시스템 콜을 볼 수 있다.
2. macOS, Linux, Windows의 관계
Unix (1970년대 Bell Labs)
├─ BSD 계열 ─────────────▶ macOS의 바닥 Darwin에 일부 쓰임
└─ 영향을 받아 새로 작성 ──▶ Linux 커널 (1991) ─▶ Ubuntu, Rocky, Android
Windows NT ─────────────────▶ Windows 10/11 (Unix와 다른 계보)| macOS | Linux | Windows | |
|---|---|---|---|
| 커널 | XNU (Mach + BSD 코드) | Linux | Windows NT |
| 계보 | Unix 계열 (여러 버전이 UNIX 03 인증) | Unix 계열(Unix-like) | Unix 아님 |
| 연구에서 | 내 노트북 | GPU 서버, 클라우드, 컨테이너 | 개인 PC |
- Darwin은 macOS 밑바닥의 오픈소스 층(XNU 커널 + BSD 도구)이다.
- POSIX는 “Unix 계열이면 이런 시스템 콜과 명령을 제공한다”는 IEEE 표준이다. 두 OS가 대체로 따르므로
ls,ps,kill,chmod가 양쪽에서 거의 같이 돈다. - “거의”가 중요하다. macOS 명령은 BSD판, Linux 명령은 GNU판이라 옵션이 다를 때가 있다. 예:
free는 Linux에만 있고,sed -i는 macOS에서sed -i ''로 쓴다. - Windows는 계보가 다르다. 대신 WSL2가 가벼운 가상머신에 진짜 Linux 커널을 띄워 준다.
3. 프로세스
프로그램은 디스크의 실행 파일이고, 프로세스(process) 는 그것을 실행 중인 상태다. 레시피와 지금 하는 요리의 차이다. 같은 python을 세 번 실행하면 프로세스가 셋 생기고, 각자 다음을 따로 받는다.
- PID: 프로세스 번호.
echo $$는 지금 셸의 PID다. - 가상 주소 공간(7절), 열린 파일 목록(10절), 소유 사용자(9절)
부모와 자식, fork와 exec
모든 프로세스는 다른 프로세스가 만든다. 만든 쪽이 부모, 그 PID가 자식의 PPID다. 거슬러 올라가면 PID 1(Linux는 보통 systemd, macOS는 launchd)에 닿는다.
셸에서 python train.py를 치면 개념적으로 이런 일이 일어난다.
zsh (PID 1200)
│ fork() ← 자기를 복제
├──▶ zsh 복사본 (PID 1201)
│ │ exec("python train.py") ← 내용물을 python으로 교체, PID는 그대로
│ ▼
│ python (PID 1201) ... exit(0)
│ wait() ← 자식이 끝나길 기다려 종료 코드를 받음
▼
다음 프롬프트, $? = 0fork는 복제, exec는 변신이다. 둘 사이에 셸이 >나 |로 입출력 방향을 바꿔 끼운다.
상태
ps의 STAT 열로 본다(Linux 기준, macOS도 대체로 같다).
| STAT | 뜻 |
|---|---|
R | 실행 중이거나 CPU 차례를 기다림 |
S | 잠듦. 입력이나 이벤트를 기다림 (대부분 평소 이 상태) |
D | 깨울 수 없는 잠. 주로 디스크·NFS 입출력 대기. kill -9도 바로 안 먹을 수 있음 |
T | 멈춤 (Ctrl+Z, 디버거) |
Z | 좀비 |
뒤에 붙는 +는 포그라운드, s는 세션 리더라는 표시다.
좀비와 고아
- 좀비(zombie): 끝난 자식의 PID와 종료 코드를 부모가
wait()로 아직 안 가져간 상태다. 이미 죽었으므로kill로 못 없앤다. 부모를 고치거나 끝내야 사라진다. - 고아(orphan): 부모가 먼저 끝난, 아직 살아 있는 자식이다. 커널이 PID 1(또는 그 역할을 맡은 프로세스)에게 입양시키고, 새 부모가
wait()를 대신 해 준다.
연구에서는 메인 스크립트가 비정상 종료된 뒤 DataLoader 워커나 multiprocessing 자식이 남아 GPU 메모리를 잡고 있는 모습으로 만난다. ps -ef | grep python으로 찾아 정리한다.
4. 포그라운드, 백그라운드, 작업 제어
키보드 입력을 받는 쪽이 포그라운드, 뒤에서 도는 쪽이 백그라운드다.
python train.py & # &: 백그라운드 실행. [1] 4321 (작업 번호, PID)
jobs # 이 셸의 작업 목록
fg %1 # 1번 작업을 앞으로
# Ctrl+Z # 포그라운드 작업을 멈춤 (T 상태)
bg %1 # 멈춘 작업을 뒤에서 계속
kill %1 # 1번 작업 종료jobs, fg, bg는 그 셸 안에서만 통한다. 다른 터미널의 작업은 PID로 다룬다.
&로 띄워도 SSH가 끊기면 같이 죽을 수 있다(5절 SIGHUP). 오래 걸리는 학습은 nohup python train.py > out.log 2>&1 &으로 SIGHUP을 무시하게 하거나, tmux 안에서 돌린다. 클러스터라면 Slurm 같은 작업 스케줄러를 쓴다(원격 GPU 서버 사용 입문).
5. 시그널
시그널(signal) 은 프로세스에게 보내는 번호 하나짜리 알림이다. 받은 프로세스는 기본 동작(대개 종료)을 하거나, 미리 등록한 처리 함수를 실행하거나, 무시한다. 단 SIGKILL과 SIGSTOP은 막을 수 없다.
| 시그널 | 번호 | 언제 | 기본 동작 | 가로채기 |
|---|---|---|---|---|
SIGINT | 2 | Ctrl+C | 종료 | 가능 (Python은 KeyboardInterrupt) |
SIGTERM | 15 | kill PID 기본값, systemctl stop, docker stop | 종료 | 가능 → 정리할 기회 |
SIGKILL | 9 | kill -9 PID, OOM killer | 즉시 종료 | 불가 |
SIGHUP | 1 | 터미널·SSH 연결이 끊김 | 종료 | 가능 (nohup이 무시하게 함) |
SIGTSTP | — | Ctrl+Z | 멈춤 | 가능 |
SIGSTOP | — | kill -STOP PID | 멈춤 | 불가 |
1, 2, 9, 15는 macOS와 Linux가 같다. 나머지는 OS마다 번호가 달라 이름으로 쓴다(kill -l).
끌 때는 TERM 먼저, KILL은 마지막이다. SIGKILL은 정리할 틈을 주지 않으므로 쓰던 체크포인트나 로그가 깨질 수 있다. docker stop도 SIGTERM을 보내고, 기본 10초를 기다린 뒤 SIGKILL을 보낸다.
Python은 SIGTERM에 처리 함수를 따로 두지 않는다. 그래서 그냥 받으면 finally도 실행되지 않고 끝난다. 종료 신호에 체크포인트를 저장하려면 직접 등록한다.
import signal, sys
def on_term(signum, frame):
save_checkpoint() # 내 저장 함수
sys.exit(0)
signal.signal(signal.SIGTERM, on_term)SSH가 끊기면 왜 학습이 죽나
연결이 끊기면 서버 쪽 셸이 SIGHUP(hang up, 전화 끊김에서 온 이름)을 받는다. 셸은 자기 작업들에게 SIGHUP을 다시 보내고, 대부분의 프로그램은 기본 동작대로 끝난다. tmux 안의 프로그램은 SSH 셸이 아니라 tmux 서버의 자식이라 이 사슬에 걸리지 않는다.
6. 스레드, 동시성, 스케줄링
스레드(thread) 는 한 프로세스 안의 실행 흐름이다.
| 프로세스 | 스레드 | |
|---|---|---|
| 메모리 | 각자 따로 | 같은 프로세스 안에서 공유 |
| 따로 갖는 것 | 주소 공간, fd 표, PID | 스택, 레지스터, 실행 위치 |
| 하나가 죽으면 | 다른 프로세스는 무사 | 보통 프로세스 전체가 죽음 |
스레드끼리 같은 변수를 동시에 고치면 결과가 꼬인다. 이것이 경쟁 상태(race condition) 이고, 락(lock) 으로 한 번에 하나만 고치게 막는다.
- 동시성(concurrency): 여러 일이 번갈아 가며 함께 진행 중. 코어 하나로도 된다.
- 병렬성(parallelism): 여러 일이 같은 순간에 다른 코어에서 실행됨. 코어가 여러 개여야 한다.
실행할 스레드는 코어보다 훨씬 많다. 커널의 스케줄러가 밀리초 단위로 돌아가며 CPU를 나눠 준다. 넘겨줄 때마다 지금 스레드의 레지스터를 저장하고 다음 것을 불러오는데, 이것이 컨텍스트 스위칭(context switch) 이다. 공짜가 아니어서 일꾼을 코어 수보다 훨씬 많이 띄우면 오히려 느려질 수 있다. 공용 서버에서 급하지 않은 작업은 nice -n 10 python prep.py처럼 우선순위를 양보해 돌린다.
Python의 GIL
CPython에는 GIL(Global Interpreter Lock) 이 있다. 한 프로세스에서 Python 바이트코드를 실행하는 스레드는 한 순간에 하나뿐이라는 잠금이다.
| 일의 종류 | 예 | 맞는 도구 |
|---|---|---|
| I/O 위주 | API 호출, 다운로드 | threading, ThreadPoolExecutor, asyncio |
| CPU 위주 순수 Python | 토큰화, 파싱 루프 | multiprocessing, ProcessPoolExecutor |
| C·CUDA 연산 | NumPy, PyTorch | 계산 중 GIL을 푸는 경우가 많아 스레드로도 병렬 |
기다리는 동안에는 GIL이 풀리므로 I/O 위주 일은 스레드로 충분하다. CPU 위주 일은 프로세스를 여러 개 띄워 각자 GIL을 갖게 한다. macOS의 multiprocessing은 자식을 새 Python으로 띄우는 spawn이 기본이라, 메인 코드를 if __name__ == "__main__": 아래에 두지 않으면 에러가 난다.
GIL은 바뀌는 중이다. Python 3.13부터 GIL 없는 free-threaded 빌드가 실험 기능으로 나왔고, 이후 버전에서 지원 수준이 올라가고 있다. 다만 기본 설치본은 여전히 GIL이 있고 확장 패키지 대응도 진행 중이니, 쓰기 전에 공식 문서를 확인한다.
7. 메모리
가상 메모리와 페이지
각 프로세스는 자기만의 넓은 가상 주소 공간을 가진 것처럼 보인다. CPU의 MMU가 커널이 만든 페이지 테이블을 보고 가상 주소를 실제 RAM 위치로 바꿔 준다. 그래서 두 프로세스가 같은 주소를 써도 서로의 메모리를 못 본다.
변환은 페이지(page) 라는 고정 크기 조각 단위다. 대부분의 x86-64 Linux는 4 KiB, Apple Silicon Mac은 16 KiB다(getconf PAGESIZE). 아직 RAM에 없는 페이지를 건드리면 페이지 폴트가 나고, 커널이 채워 주면 이어서 실행된다. 에러가 아니다.
스택과 힙
- 스택(stack): 함수를 부를 때 지역 변수 자리가 생기고, 함수가 끝나면 자동으로 사라진다. 빠르지만 작다(Linux에서 흔한 기본값 8 MB,
ulimit -s). - 힙(heap): 실행 중에 필요한 만큼 받는 큰 공간이다. 배열, 객체, 텐서가 여기 산다. Python은 안 쓰는 객체를 자동으로 회수한다.
RAM과 스왑
RAM이 모자라면 커널은 오래 안 쓴 페이지를 디스크의 스왑(swap) 으로 내보낸다. 계속 돌기는 하지만 디스크는 RAM보다 훨씬 느려서 컴퓨터가 버벅인다. macOS는 먼저 메모리를 압축하고, 그다음 스왑 파일을 쓴다(활성 상태 보기의 “메모리 압력”, macOS 활성 상태 보기 입문).
- Linux
free -h에서는free가 아니라available열을 본다. 남는 RAM을 파일 캐시로 쓰기 때문에free는 늘 작아 보인다. ps·top의 VSZ/VIRT는 가상 공간 크기일 뿐이다. 실제 RAM 사용량은 RSS/RES다.
OOM killer
Linux에서 RAM과 스왑이 다 차면 커널의 OOM killer가 메모리를 많이 쓰는 프로세스를 골라 SIGKILL로 끝낸다.
- traceback 없이
Killed한 줄만 남는다. - 종료 코드가
137(= 128 + 9)이다. dmesg에Out of memory: Killed process ...가 남는다(서버에 따라sudo필요).
컨테이너는 cgroup 메모리 한도를 넘어도 같은 식으로 죽는다. macOS는 이렇게 조용히 죽이기보다 앱 종료를 권하는 창을 띄우는 쪽에 가깝다. GPU의 CUDA out of memory는 다른 문제다(GPU와 CUDA 입문).
메모리 누수
메모리 누수(memory leak) 는 안 쓰는 메모리를 돌려주지 않아 사용량이 계속 느는 현상이다. Python에서도 참조가 남아 있으면 회수되지 않는다.
losses.append(loss) # 나쁨: 계산 그래프까지 통째로 붙잡음
losses.append(loss.item()) # 좋음: 숫자 하나만 저장htop에서 RSS가 스텝마다 오르기만 하면 누수를 의심한다. 프로세스가 끝나면 그 메모리는 OS가 전부 회수한다.
8. 파일 시스템
디렉터리 트리
Unix 계열에서는 모든 파일이 /(루트) 하나에 매달린다. C:, D: 같은 드라이브 문자가 없다.
| 경로 | Linux | macOS |
|---|---|---|
| 홈 | /home/이름 | /Users/이름 |
| 설정 | /etc | /etc (→ /private/etc 링크) |
| 임시 | /tmp | /tmp (→ /private/tmp) |
| 외부 디스크 | /mnt, /media | /Volumes |
inode
ext4 같은 파일 시스템에서 파일 하나는 inode라는 기록으로 표현된다. inode에는 크기, 소유자, 권한, 시각, 데이터 위치가 있다. 파일 이름은 inode에 없다. 이름은 디렉터리 안의 “이름 → inode 번호” 표에 있다(ls -i로 번호 확인).
작은 파일 수백만 개짜리 데이터셋은 용량이 남아도 inode가 먼저 바닥나 No space left on device가 날 수 있다. df -h가 아니라 df -i로 확인한다.
하드 링크 vs 심볼릭 링크
ln original.txt hard.txt # 하드 링크
ln -s original.txt soft.txt # 심볼릭 링크| 하드 링크 | 심볼릭 링크 | |
|---|---|---|
| 정체 | 같은 inode의 또 다른 이름 | 경로를 적어 둔 작은 파일 |
| 원본 이름을 지우면 | 데이터 그대로 | 링크가 끊김 |
| 다른 디스크로 | 불가 | 가능 |
| 디렉터리에 | 일반적으로 불가 | 가능 |
연구에서는 ln -s /data/imagenet data처럼 큰 데이터셋을 심볼릭 링크로 연결하는 일이 많다.
마운트와 “모든 것은 파일”
마운트(mount) 는 디스크나 네트워크 저장소를 트리의 한 폴더에 붙이는 일이다. df -h로 무엇이 어디에 붙었는지 본다. 연구실 서버는 /home이 용량 작은 네트워크 저장소(NFS)이고, 큰 데이터는 /data나 /scratch에 두라는 규칙이 흔하다. 서버마다 다르니 먼저 확인한다.
Unix는 장치, 파이프, 네트워크 연결까지 열고·읽고·쓰고·닫는 같은 방식으로 다룬다. 이것이 “모든 것은 파일” 원칙이다. /dev/null은 쓰면 버려지는 블랙홀이고, Linux의 /proc/meminfo는 메모리 정보를 파일처럼 읽게 해 준다.
9. 권한
ls -l 읽기
-rwxr-xr-x 1 juhyeon lab 2048 Oct 7 10:00 run.sh
│└┬┘└┬┘└┬┘ │ └소유자┘ └그룹┘ └크기┘ └─수정 시각─┘ └이름┘
│ u g o 링크 수
종류: - 파일, d 디렉터리, l 심볼릭 링크u는 소유자, g는 그룹(예: 연구실 그룹 lab), o는 그 밖의 모든 사람이다. 각자 r, w, x 세 칸을 가진다.
| 파일 | 디렉터리 | |
|---|---|---|
r = 4 | 내용 읽기 | 안의 이름 목록 보기 |
w = 2 | 내용 고치기 | 안에서 파일 만들기·지우기·이름 바꾸기 |
x = 1 | 프로그램으로 실행 | 안으로 들어가기(cd)·접근 |
파일을 지울 수 있는지는 그 파일이 든 디렉터리의 w 가 정한다.
chmod, chown
숫자는 4·2·1을 더해 u·g·o 순으로 쓴다. 755(rwxr-xr-x)는 스크립트, 644는 일반 파일, 700은 ~/.ssh 같은 개인 폴더, 600은 SSH 개인키나 .env 같은 비밀 파일이다.
chmod 600 ~/.ssh/id_ed25519 # 숫자로
chmod u+x run.sh # 기호로: 소유자에게 실행 추가
chmod g+w,o-r shared.txt # 그룹 쓰기 추가, 기타 읽기 제거
sudo chown juhyeon:lab results/ # 소유자·그룹 변경 (보통 관리자 권한 필요)SSH는 개인키가 남에게 열려 있으면 UNPROTECTED PRIVATE KEY FILE! 경고를 내고 그 키를 쓰지 않는다.
실행 권한과 shebang
#!/usr/bin/env bash
echo "hello"첫 줄의 #!가 shebang이다. “이 파일은 이 프로그램으로 실행하라”는 뜻이다. #!/usr/bin/env python3이라고 쓰면 PATH에서 찾은 python3(활성화한 가상환경의 것)를 쓴다.
./run.sh # x 권한 + shebang 필요. 없으면 Permission denied
bash run.sh # bash가 읽기만 하므로 x 권한 없이도 됨./를 붙이는 이유는 보안상 현재 폴더가 PATH에 없기 때문이다.
root와 sudo
root는 UID 0인 관리자 계정이고, 권한 검사를 거의 받지 않는다. sudo는 허락된 사용자가 “이 명령 하나만 root로” 실행하게 해 준다. id, whoami, groups로 내가 누구인지 본다.
공용 서버에서는 보통 sudo가 없다. 패키지는 홈의 가상환경(uv, conda, venv)에 깔고, 시스템 패키지가 꼭 필요하면 관리자에게 요청하거나 컨테이너를 쓴다.
10. 파일 디스크립터, 종료 코드, 환경
파일 디스크립터 0, 1, 2
프로세스가 연 파일·파이프·소켓에 커널이 붙이는 작은 정수가 파일 디스크립터(fd) 다. 모든 프로세스는 셋을 열고 시작한다.
| fd | 이름 | 기본 연결 |
|---|---|---|
| 0 | stdin (표준 입력) | 키보드 |
| 1 | stdout (표준 출력) | 화면 |
| 2 | stderr (표준 에러) | 화면 |
python train.py > out.log 2>&1 # 1을 파일로, 2를 1이 가는 곳으로
python train.py 2>&1 | tee out.log # 화면에도 보이고 파일에도 저장순서가 중요하다. 2>&1 > out.log로 쓰면 에러는 여전히 화면으로 간다. fd 수 한도(ulimit -n)를 넘으면 Too many open files가 난다.
종료 코드
프로세스는 끝날 때 0~255 숫자 하나를 부모에게 남긴다. 셸에서는 $?로 본다. 0이 성공이고, 나머지는 실패다.
| 코드 | 흔한 뜻 |
|---|---|
0 | 성공 |
1 | 일반 실패 (Python의 처리 안 된 예외 등) |
126 / 127 | 실행 권한 없음 / 명령을 찾을 수 없음 |
128 + N | 시그널 N으로 죽음: 130=Ctrl+C, 137=SIGKILL(OOM 포함), 143=SIGTERM |
python a.py && python b.py # a가 성공했을 때만 b
python a.py || echo "실패" # a가 실패했을 때만CI와 작업 스케줄러도 이 숫자로 성공·실패를 판단한다(CI-CD와 GitHub Actions 입문).
환경은 아래로만 흐른다
자식 프로세스는 부모의 환경 변수와 현재 폴더를 복사해서 물려받는다. 자식이 바꾼 값은 부모에게 돌아가지 않는다. 스크립트 안에서 cd나 export를 해도 실행한 셸이 그대로인 이유다. 현재 셸을 바꾸려면 source script.sh로 실행한다.
11. 데몬과 서비스, 그리고 컨테이너
데몬(daemon) 은 사용자와 대화하지 않고 뒤에서 계속 도는 프로세스다. sshd, dockerd, cron처럼 이름이 d로 끝나는 경우가 많다.
- Linux: 대부분의 배포판에서 systemd가 PID 1로 돌며 서비스를 켜고 끄고 다시 살린다.
systemctl status sshd,journalctl -u sshd -f(배포판에 따라 이름이ssh). - macOS: launchd가 같은 역할을 하고,
launchctl과~/Library/LaunchAgents의.plist파일로 다룬다.
Docker 입문에서 컨테이너는 “격리된 프로세스”라고 했다. 이 노트의 말로 하면 이렇다.
- namespace: 프로세스가 볼 수 있는 것을 제한한다. 자기만의 PID 목록(컨테이너 안의 첫 프로세스가 PID 1), 마운트 트리, 네트워크, 호스트 이름을 가진 것처럼 보인다.
- cgroup: 쓸 수 있는 CPU·메모리 양을 제한한다. 메모리 한도를 넘으면 7절의 OOM killer가 끝낸다.
컨테이너를 여러 서버에 배치하는 일은 Kubernetes 입문으로 이어진다.
12. 확인 명령 치트시트
# 프로세스
ps aux # 모든 프로세스 (macOS·Linux 공통)
ps -o pid,ppid,stat,command -p 4321
pgrep -fl train.py # 명령줄로 PID 찾기
top # 실시간 (macOS: top -o cpu / top -o mem)
htop # 보기 좋은 top (brew / apt install htop)
kill 4321 ; kill -9 4321 # SIGTERM ; SIGKILL(최후 수단)
# 메모리
free -h # Linux: available 열을 볼 것
vm_stat # macOS: 페이지 단위 통계
sysctl vm.swapusage # macOS: 스왑 사용량
# 디스크·파일
df -h ; df -i # 용량 ; inode
du -sh data/ # 폴더 크기
du -h -d 1 . | sort -h # 한 단계 아래 폴더별 크기
ls -l ; ls -i # 권한·소유자 ; inode 번호
# 열린 파일·한도
lsof -p 4321 # 그 프로세스가 연 파일
lsof -i :8888 # 8888번 포트를 쓰는 프로세스
ulimit -a # 한도 (-n 열린 파일, -s 스택)
dmesg | tail # Linux 커널 메시지, OOM 확인lsof가 없는 Linux 서버에서는 포트를 ss -tlnp로 본다. GPU를 잡은 프로세스는 nvidia-smi로 본다.
13. 아주 짧은 암기
- OS = 자원 관리자. CPU는 프로세스로, 메모리는 가상 주소로, 디스크는 파일로 나눠 준다.
- 커널만 하드웨어를 직접 만진다. 프로그램은 시스템 콜로 부탁한다.
- macOS·Linux는 둘 다 Unix 계열·POSIX. 그래도 BSD판·GNU판 옵션 차이가 있다.
- fork는 복제, exec는 변신.
- 좀비 = 부모가
wait()안 한 죽은 자식. 고아 = 부모가 먼저 죽은 산 자식. kill은 TERM,kill -9는 KILL. TERM 먼저.- SSH가 끊기면 SIGHUP. 오래 도는 일은
tmux나nohup. - 스레드는 메모리 공유, 프로세스는 따로. GIL: I/O는 스레드, CPU는 프로세스.
Killed+ 종료 코드 137 → OOM부터 의심.- 하드 링크는 같은 파일의 다른 이름, 심볼릭 링크는 경로 메모.
- rwx = 4·2·1. 비밀 파일
600, 스크립트755. - fd 0·1·2 = 입력·출력·에러. 종료 코드 0 = 성공.
입문자가 조심할 점
kill -9를 습관처럼 쓰지 않는다. 저장 중이던 체크포인트와 로그가 깨질 수 있다.pkill -f python처럼 넓게 잡지 않는다. 내 다른 작업까지 다 죽는다. PID를 확인하고 정확히 끈다.Permission denied에 바로sudo나chmod 777을 쓰지 않는다.ls -l과id로 이유부터 본다. root로 만든 파일은 나중에 내 계정으로 못 고친다.- 디스크 가득 참은 두 종류다.
df -h가 여유 있는데 공간이 없다면df -i. - 파이프의 종료 코드는 마지막 명령 것이다.
python train.py | tee log는 학습이 실패해도$?가 0이다. 스크립트에서는set -o pipefail. - Mac에서 된 셸 스크립트가 서버에서 안 되면
sed,date,stat같은 BSD·GNU 옵션 차이를 의심한다.
출처
- Arpaci-Dusseau, R. H. & Arpaci-Dusseau, A. C. Operating Systems: Three Easy Pieces (OSTEP). https://pages.cs.wisc.edu/~remzi/OSTEP/
- Linux man-pages,
fork(2),execve(2),wait(2),signal(7),proc(5),inode(7),symlink(7),namespaces(7),cgroups(7). https://man7.org/linux/man-pages/ - The Open Group, POSIX.1 / The Single UNIX Specification. https://pubs.opengroup.org/onlinepubs/9799919799/
- GNU Bash Reference Manual — Job Control, Signals, Redirections, Exit Status. https://www.gnu.org/software/bash/manual/
- Linux kernel documentation, Memory Management. https://docs.kernel.org/admin-guide/mm/
- systemd,
systemctl(1),journalctl(1). https://systemd.io · Apple,launchd.plist(5),launchctl(1)man pages - Python Docs,
signal,threading,multiprocessing, Python support for free threading. https://docs.python.org/3/ · PEP 703. https://peps.python.org/pep-0703/ - Docker Docs,
docker stop. https://docs.docker.com