리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.
리눅스 시스템 기초 4 / 50 · Part 1. Linux 기본 구조
실습 환경: Rocky Linux 9 (10.0.0.200)
이전 글: 3. Shell과 Bash 이해
1편에서 "Linux는 User Space와 Kernel Space로 나뉜다"고 정리했고, 2편에서 root도 User Mode 프로그램이라는 점을 봤다. 이번 글은 이 두 공간이 메모리와 CPU 실행 모드 측면에서 실제로 어떻게 나뉘는지 를 다룬다.
이 내용이 관제와 연결되는 지점은 다음과 같다.
ps 결과에서 [kworker]처럼 대괄호로 표시되는 커널 스레드 를 구분할 줄 알아야, 이름을 위장한 악성 프로세스를 찾아낼 수 있다.| 구분 | User Space | Kernel Space |
|---|---|---|
| 실행 주체 | 일반 프로그램 (bash, sshd, httpd — root 실행 포함) | 커널 본체, 커널 모듈, 커널 스레드 |
| CPU 모드 | User Mode (x86 Ring 3) | Kernel Mode (x86 Ring 0) |
| 메모리 접근 | 자기 프로세스의 가상 주소 공간만 | 전체 물리 메모리 |
| 하드웨어 접근 | 불가 → System Call로 요청 | 직접 가능 |
| 오류 발생 시 | 해당 프로세스만 종료 (Segmentation fault) | 시스템 전체 중단 가능 (Kernel panic) |
각 프로세스는 자기만의 가상 주소 공간 을 가진다. 프로세스 A의 주소 0x1000과 프로세스 B의 주소 0x1000은 서로 다른 물리 메모리를 가리킨다. 커널이 페이지 테이블로 이 연결을 관리하기 때문에 프로세스끼리 서로의 메모리를 직접 읽거나 쓸 수 없다. 이것이 메모리 격리다.
| 용어 | 의미 |
|---|---|
| 모드 전환 | 같은 프로세스가 User Mode ↔ Kernel Mode로 오가는 것 (System Call, 인터럽트) |
| 문맥 교환 | CPU가 실행하는 프로세스 자체를 A → B로 바꾸는 것 (스케줄러) |

| 영역 | 내용 | 특징 |
|---|---|---|
| Text | 실행 코드 | 읽기·실행 전용 |
| Data / BSS | 전역·정적 변수 | |
| Heap | 실행 중 동적으로 할당한 메모리 | 위쪽으로 커짐 |
| 공유 라이브러리 | libc.so 등 (mmap으로 매핑) | 여러 프로세스가 같은 물리 페이지 공유 |
| Stack | 함수 호출, 지역 변수 | 아래쪽으로 커짐 |
| Kernel Space | 커널 영역 | 사용자 모드에서 접근 불가 |
실제 배치는 /proc/<PID>/maps에서 볼 수 있다.
ASLR(Address Space Layout Randomization)은 실행할 때마다 Stack, Heap, 라이브러리의 주소를 무작위로 바꿔 메모리 공격(버퍼 오버플로 등)의 성공률을 낮추는 커널 기능이다. kernel.randomize_va_space 값이 2면 전체 적용 상태다(2편 참고).
커널도 백그라운드 작업(디스크 쓰기, 작업 큐 처리 등)을 위해 스레드를 돌린다. 이것이 커널 스레드이며, ps에서 다음 특징으로 구분된다.
| 특징 | 커널 스레드 | 일반 프로세스 |
|---|---|---|
| 이름 표시 | [kthreadd], [kworker/0:1]처럼 대괄호 | /usr/sbin/sshd -D |
| 부모 | kthreadd(PID 2)가 부모 (kthreadd 자신은 PPID 0) | 보통 systemd(PID 1) 또는 다른 프로세스 |
| 명령줄 | /proc/<PID>/cmdline 비어 있음 | 실행 인자 존재 |
| 실행 파일 | /proc/<PID>/exe 링크 없음 | 실행 파일 경로 존재 |
| 메모리 | 사용자 공간 메모리 없음 (VSZ, RSS = 0) | 0보다 큼 |
# 1) 현재 쉘의 메모리 배치
cat /proc/$$/maps | head -20
# 2) 커널 스레드 확인
ps -o pid,ppid,user,vsz,rss,cmd -p 2
ps -ef | awk '$3==2' | head -10
# 3) 커널 스레드의 실행 파일 링크 (없어야 정상)
sudo readlink /proc/2/exe; echo "exit=$?"
# 4) ASLR 확인 — 같은 명령을 두 번 실행해 주소 비교
grep -m1 stack /proc/self/maps
grep -m1 stack /proc/self/maps
# 5) 모드 전환 통계 (사용자/시스템 CPU 시간)
grep -E '^cpu ' /proc/stat
아래 출력은 형식 설명용 예시다.
① /proc/$$/maps
55d4xxxx-55d4xxxx r-xp ... /usr/bin/bash ← Text (실행 권한 x)
55d4xxxx-55d4xxxx rw-p ... [heap]
7f1cxxxx-7f1cxxxx r-xp ... /usr/lib64/libc.so.6
7ffcxxxx-7ffcxxxx rw-p ... [stack]
| 권한 표기 | 의미 | 보안 의미 |
|---|---|---|
r-xp | 읽기·실행 | 코드 영역 |
rw-p | 읽기·쓰기 (실행 불가) | 데이터 영역 — 여기서 코드 실행이 막히는 것이 정상 (NX) |
rwxp | 읽기·쓰기·실행 | 일반 프로그램에서 드묾 → 메모리 인젝션 의심 지표 중 하나 |
② 4)번 결과: 두 번 실행한 [stack] 주소가 서로 다르면 ASLR이 동작하는 것이다.
③ readlink /proc/2/exe: 커널 스레드는 실행 파일이 없으므로 오류와 함께 종료 코드가 0이 아니다.
| 공격 | User/Kernel 경계와의 관계 | 대응 |
|---|---|---|
| 권한 상승 | 일반 사용자 권한에서 root 또는 커널 권한으로 경계를 넘으려는 시도 | 패치, 최소 권한 |
| 메모리 공격 | 격리된 프로세스 메모리 안에서 흐름을 조작 | ASLR, NX, 스택 보호 |
| 프로세스 위장 | 악성 프로그램이 argv[0]을 [kworker/0:2]처럼 바꿔 커널 스레드인 척 | PPID·exe·메모리 확인 |
| 파일리스 실행 | 디스크에 파일을 남기지 않고 메모리에서만 실행 | /proc/<PID>/exe의 (deleted), memfd: 경로 |
프로세스 이름(명령줄)은 실행한 쪽이 마음대로 정할 수 있는 값 이다. 그래서 이름만 보고 정상이라고 판단하면 안 되고, 커널이 관리하는 정보(PPID, 실행 파일 링크, 메모리 사용량)와 대조해야 한다.
# 이름이 대괄호로 시작하는데 실행 파일(exe)이 있는 프로세스 = 위장 의심
for p in /proc/[0-9]*; do
pid=${p#/proc/}
cmd=$(tr '\0' ' ' < $p/cmdline 2>/dev/null)
comm=$(cat $p/comm 2>/dev/null)
exe=$(readlink $p/exe 2>/dev/null)
if [[ -n "$exe" && ( "$cmd" == \[* || "$comm" == \[* ) ]]; then
echo "[의심] PID=$pid CMD=$cmd EXE=$exe"
fi
done
# 실행 파일이 삭제되었거나 메모리 기반(memfd)인 프로세스
sudo ls -l /proc/*/exe 2>/dev/null | grep -E '\(deleted\)|memfd:'
[Event] CPU 사용률 경보 — 프로세스 이름 [kworker/0:2]
↓
[검증] PPID=1(2가 아님), exe=/tmp/.x/kw, RSS>0 → 커널 스레드 아님
↓
[IOC] 실행 파일 해시(sha256sum), 경로, 실행 계정, 외부 연결(ss -tanp)
↓
[Root Cause] 누가 언제 /tmp/.x/kw 를 실행했는가 → secure·audit 로그
↓
[Response] 프로세스 메모리·파일 증적 확보 → 종료 → 지속성 경로 점검
/proc/<PID>/maps로 메모리 배치를, rwxp 같은 이상 권한을 확인할 수 있다.randomize_va_space=2)은 메모리 공격을 어렵게 만든다.다음 글 「5. Linux 시스템 호출(System Call)」 에서는 User Mode에서 Kernel Mode로 넘어가는 유일한 공식 통로인 System Call을 strace로 직접 관찰하고, auditd로 System Call을 기록해 누가 무엇을 실행했는지 추적하는 방법을 다룬다.