태스크 (Task - 리눅스 프로세스/스레드 통일 용어)
Ⅰ. 개요
1. 정의
**태스크(Task)**는 리눅스 커널에서 프로세스와 스레드를 구분 없이 부르는 통일된 용어이다. 리눅스 커널은 전통적인 UNIX의 프로세스와 스레드 구분을 없애고, 모든 실행 단위를 task_struct라는 하나의 자료구조로 관리한다.
리눅스의 통일 모델
┌─────────────────────────────────────────────────┐
│ 리눅스 커널 관점 │
│ │
│ ┌───────────┐ ┌───────────┐ ┌─────────┐│
│ │task_struct│ │task_struct│ │task_ ││
│ │(프로세스) │ │(스레드) │ │struct ││
│ │TGID=PID │ │TGID=부모 │ │(커널 ││
│ │ │ │ PID │ │스레드) ││
│ └───────────┘ └───────────┘ └─────────┘│
│ └──────────────┼──────────────┘ │
│ │ │
│ 모두 "태스크" │
│ 모두 동일 스케줄링 대상 │
│ │
│ ★ 커널은 프로세스와 스레드를 구분하지 않음 │
└─────────────────────────────────────────────────┘
비유: 리눅스 커널에게 프로세스와 스레드는 "혼자 사는 집"과 "룸메이트와 함께 사는 집"의 차이다. 커널은 모두 같은 "집(task_struct)"으로 취급하며, 방을 공유하느냐(메모리 공유) 아니냐만 다를 뿐이다.
Ⅱ. task_struct 구조체
1. 핵심 필드
task_struct 주요 구성
┌──────────────────────────────────────────────────┐
│ struct task_struct { │
│ ┌─────────────────────┐ │
│ │ 식별자 │ │
│ │ pid, tgid, comm │ │
│ ├─────────────────────┤ │
│ │ 스케줄링 정보 │ │
│ │ state, prio, │ │
│ │ sched_class, │ │
│ │ sched_entity │ │
│ ├─────────────────────┤ │
│ │ 메모리 관리 │ │
│ │ mm (메모리 디스크립터)│ │
│ │ active_mm │ │
│ ├─────────────────────┤ │
│ │ 파일 관리 │ │
│ │ files (파일 테이블) │ │
│ │ fs (파일시스템 정보) │ │
│ ├─────────────────────┤ │
│ │ 시그널 │ │
│ │ signal, sighand │ │
│ ├─────────────────────┤ │
│ │ 실행 컨텍스트 │ │
│ │ thread (레지스터) │ │
│ │ stack │ │
│ └─────────────────────┘ │
│ }; │
└──────────────────────────────────────────────────┘
2. 프로세스와 스레드의 task_struct 차이
┌───────────────┬──────────────────┬──────────────────┐
│ │ 프로세스 │ 스레드 │
├───────────────┼──────────────────┼──────────────────┤
│ task_struct │ 1개 │ 1개 (각각) │
│ pid │ = tgid │ = 고유 TID │
│ tgid │ = pid │ = 리더의 PID │
│ mm (메모리) │ 독립 소유 │ 포인터 공유 │
│ files │ 독립 소유 │ 포인터 공유 │
│ fs │ 독립 소유 │ 포인터 공유 │
│ sighand │ 독립 소유 │ 포인터 공유 │
│ stack │ 독립 │ 독립 (각각) │
└───────────────┴──────────────────┴──────────────────┘
Ⅲ. PID vs TGID
1. 식별자 체계
PID와 TGID의 관계
┌─────────────────────────────────────────────────┐
│ │
│ 프로세스 A (단일 스레드) │
│ ┌────────────────────┐ │
│ │ PID = 1000 │ │
│ │ TGID = 1000 │ ← PID == TGID │
│ │ (리더 스레드) │ │
│ └────────────────────┘ │
│ │
│ 프로세스 B (다중 스레드) │
│ ┌────────────────────┐ │
│ │ PID=2000 TGID=2000 │ ← 메인 스레드 (리더) │
│ ├────────────────────┤ │
│ │ PID=2001 TGID=2000 │ ← 워커 스레드 1 │
│ ├────────────────────┤ │
│ │ PID=2002 TGID=2000 │ ← 워커 스레드 2 │
│ └────────────────────┘ │
│ │
│ getpid() → TGID 반환 (스레드 그룹 리더 PID) │
│ gettid() → 실제 PID (각 스레드 고유 ID) │
│ kill(pid) → TGID로 전체 프로세스에 시그널 │
│ tgkill() → 특정 TID로 특정 스레드에 시그널 │
└─────────────────────────────────────────────────┘
2. 스레드 그룹 리더
- 스레드 그룹의 첫 번째 태스크가 리더
- 리더의 PID가 TGID가 된다
- 리더가 종료되어도 그룹은 유지 (다른 스레드 존재 시)
Ⅳ. /proc 파일시스템과 태스크
1. /proc/[pid]/task/ 구조
/proc에서 스레드 확인
┌─────────────────────────────────────────────────┐
│ │
│ /proc/2000/ ← 프로세스 (TGID) │
│ ├── task/ ← 스레드 목록 │
│ │ ├── 2000/ ← 메인 스레드 │
│ │ │ ├── status ← 개별 상태 │
│ │ │ ├── regs ← 레지스터 │
│ │ │ └── comm ← 스레드 이름 │
│ │ ├── 2001/ ← 워커 스레드 1 │
│ │ └── 2002/ ← 워커 스레드 2 │
│ ├── status ← 프로세스 전체 상태 │
│ ├── maps ← 메모리 맵 (공유) │
│ └── fd/ ← 파일 디스크립터 │
│ │
│ 확인 명령어: │
│ $ ls /proc/2000/task/ ← 2000 2001 2002 │
│ $ cat /proc/2000/task/2001/comm ← 스레드명 │
└─────────────────────────────────────────────────┘
Ⅴ. 스케줄러와 태스크
1. 균등한 스케줄링
스케줄러의 태스크 처리
┌─────────────────────────────────────────────────┐
│ │
│ 스케줄러 (CFS, RT, Deadline) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ Run Queue │ │
│ │ │ │
│ │ [Task A] [Task B] [Task C] │ │
│ │ 프로세스 스레드 프로세스 │ │
│ │ (TGID) (TID) (TGID) │ │
│ │ │ │
│ │ ★ 구분 없이 모두 동일한 태스크 │ │
│ │ ★ vruntime 기준 공정 스케줄링 │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
- 커널 스케줄러는 모든 task_struct를 동등한 스케줄링 단위로 취급
- 프로세스와 스레드 간에 우선순위 차이 없음
- CFS(Completely Fair Scheduler)가 가상 실행 시간(vruntime)으로 공정 분배
Ⅴ. 지식 그래프 및 요약
1. 지식 그래프
[태스크 (Task)]
├── [자료구조]
│ ├── task_struct ─── 모든 실행 단위의 커널 표현
│ └── thread_info ─── 저수준 아키텍처 정보
├── [식별자]
│ ├── PID ──────── 각 태스크의 고유 번호
│ ├── TGID ─────── 스레드 그룹 리더의 PID
│ └── getpid() ─── TGID 반환, gettid() ─ PID 반환
├── [자원 공유 여부에 따른 분류]
│ ├── 프로세스 ─── mm, files, fs 독립 소유
│ ├── 스레드 ────── mm, files, fs 포인터 공유
│ └── 커널 스레드 ── mm 없음 (커널 공간만 사용)
├── [관찰 방법]
│ ├── /proc/[pid]/task/ ── 스레드 목록
│ └── ps -eLf ─────── 모든 스레드 표시
└── [스케줄링]
├── CFS ──────── 가상 실행 시간 기준 공정 스케줄링
├── RT 스케줄러 ── 실시간 우선순위 기반
└── 구분 없이 모든 태스크를 동등 취급
2. 준말
- PID: Process ID (프로세스 식별자)
- TGID: Thread Group ID (스레드 그룹 식별자)
- TID: Thread ID (스레드 식별자, 리눅스에서는 PID와 동일)
- CFS: Completely Fair Scheduler (완전 공정 스케줄러)
3. 어린이를 위한 3줄 설명
리눅스에서는 프로세스도 스레드도 모두 "태스크"라고 불러요. 각 태스크는 자기만의 번호표(PID)를 가지고, 같은 그룹에 속한 스레드들은 그룹 번호(TGID)도 같이 가져요. 커널은 모든 태스크를 똑같이 취급해서 차례대로 실행 기회를 줘요.