QNX RTOS: 4-1. Processes and Threads
2025. 11. 26. 13:53ㆍ운영체제/QNX
1. 프로세스 (Process): 자원과 보안의 컨테이너
QNX에서 프로세스는 프로그램을 메모리에 로드하여 실행한 상태를 말하며, PID(Process ID)로 식별됨. 프로세스의 본질은 '실행'보다는 '소유(Ownership)'와 '보안(Security)'에 있음.

1.1. 자원 소유 (Resource Ownership)
프로세스는 시스템 자원을 담는 그릇임.
- 메모리: 코드, 데이터, 힙(Heap), 스택(Stack).
- 핸들: 오픈된 파일, 시스템 타이머.
- IPC: 채널, 커넥션, 뮤텍스 등 동기화 객체.
이 '소유' 개념에는 두 가지 중요한 원칙이 따름.
- 보호(Protection): 내가 소유한 메모리는 남이 건드릴 수 없음.
- 정리(Cleanup): 프로세스가 죽으면(정상 종료든, 크래시든, Kill 당하든), OS는 주인이 사라진 모든 자원(메모리, 파일 핸들 등)을 즉시 회수함. 이것이 QNX의 안정성을 보장하는 핵심임.
1.2. 보안 컨텍스트 (Security Context)
프로세스는 시스템에서 "무엇을 할 수 있는가"를 정의하는 단위임.
- Identity: UID, GID (누구인가? -> 파일 접근 권한 제어)
- Abilities: QNX 특유의 권한 체계. (무엇을 할 수 있는가? -> 인터럽트 연결, 물리 메모리 매핑 등)
2. 스레드 (Thread): 실행의 흐름
스레드는 프로세스 내부를 흐르는 실행 흐름(Execution Flow)임. Instruction Pointer(IP)가 코드를 따라가며 함수를 호출하고 루프를 도는 주체임.

2.1. 스레드의 속성
프로세스가 자원을 '소유'한다면, 스레드는 실행을 위한 '속성(Attributes)'을 가짐.
- 스케줄링: 우선순위(Priority), 스케줄링 알고리즘 (FIFO, Round Robin 등).
- 레지스터 셋: IP, Stack Pointer 등 (Context Switching 시 저장/복구되는 대상).
- Affinity: 멀티코어에서 어떤 CPU 코어에서 돌 것인가.
- Signal Mask: 어떤 시그널을 처리할 것인가.
2.2. 프로세스와의 관계
- 프로세스는 최소 1개 이상의 스레드를 가져야 함. (없으면 프로세스도 소멸)
- 한 프로세스 내의 스레드들은 모든 자원을 공유함.
- Thread A가 메모리를 할당하고, Thread B가 쓰고, Thread C가 해제할 수 있음.
- 이 공유 때문에 Mutex 같은 동기화가 필수적임.

3. 설계 철학: Single vs Multi Thread
3.1. Single Thread가 좋은 이유
- 예측 가능성(Predictability): 실행 경로가 단순함.
- 디버깅 용이성: 재현이 쉬움.
- 안전성 증명: 단위 테스트(Unit Test)가 쉽고, Safety 인증을 받기에 유리함.
3.2. 그럼 언제 Multi Thread를 써야 하는가?
"이전 작업을 끝내기 전에 새로운 작업을 시작해야 할 때"
[사례 1: 드라이버와 하드웨어 데드라인]
- 상황: 드라이버가 클라이언트의 요청(처리 1ms 소요)을 수행 중임.
- 문제: 갑자기 하드웨어 인터럽트가 발생함. 하드웨어 스펙상 200µs(마이크로초) 안에 응답해야 함.
- 싱글 스레드: 1ms 작업을 하느라 200µs 데드라인을 놓침. 중간중간 "인터럽트 왔니?"라고 폴링(Polling)을 넣으면 코드가 꼬이고 성능이 저하됨.
- 해결: 하드웨어 전담 고우선순위 스레드를 둠. 인터럽트가 오면 즉시 하던 일을 멈추고(Preemption) 하드웨어부터 처리함. OS가 컨텍스트 저장/복구를 알아서 해줌.
[사례 2: 긴 작업과 긴급 작업]
- 상황: 파일 시스템이 20MB짜리 거대한 이미지를 읽고 있음(Low Priority).
- 문제: 갑자기 중요한 로그 20바이트를 빨리 써야 한다는 요청(High Priority)이 들어옴.
- 해결: 별도의 스레드가 긴급 요청을 가로채서 먼저 처리함.
결론적으로 병렬성(Parallelism)이나 선점(Preemption)이 반드시 필요한 경우가 아니라면, 멀티 스레딩은 오버 엔지니어링임.
4. 아키텍처링: 프로세스를 객체(Object)처럼
시스템을 설계할 때 프로세스를 불투명한(Opaque) 객체로 취급해야 함.

- 예시: 공장 자동화 시스템 (컨베이어 벨트, 스탬퍼, 드릴)
- 나쁜 설계: 하나의 거대한 프로세스 안에 '벨트 스레드', '스탬퍼 스레드', '드릴 스레드'를 둠.
- 문제: 스탬퍼 스레드에서 메모리 침범 버그가 터지면? 공장 전체 프로세스가 죽어서 드릴과 벨트도 멈춤.
- 좋은 설계: '벨트 제어 프로세스', '스탬퍼 프로세스', '드릴 프로세스'로 분리.
- 나쁜 설계: 하나의 거대한 프로세스 안에 '벨트 스레드', '스탬퍼 스레드', '드릴 스레드'를 둠.

- 이유:
- 드릴과 스탬퍼는 코드도 다르고 데이터도 다름. 자원을 격리하는 것이 맞음.
- 드릴이 고장 나서 프로세스가 죽어도, 컨베이어 벨트는 계속 돌아갈 수 있음 (격리).
- 외부에서는 "드릴 프로세스의 3번 스레드"를 알 필요가 없으며, 오직 정의된 인터페이스(메시지 등)로만 통신해야 함.
- 이점: 재사용성과 격리
- 재사용(Scalability): 드릴이 큰 드릴, 작은 드릴 2개가 있다면? drill_process 코드는 하나만 짜고, 실행할 때 설정만 다르게 줘서 프로세스를 두 번 띄우면 됨. (spawn drill -big, spawn drill -small)
- 격리(Isolation): 스탬퍼가 고장 나서 프로세스가 죽어도, line_control 프로세스는 살아있음. UI에 경고등을 띄우고 작업자가 고친 뒤 스탬퍼 프로세스만 재시작하면 됨. 전체 라인을 껐다 킬 필요가 없음.
5. 메모리 레이아웃과 ASLR
프로세스의 가상 메모리 공간은 다음과 같이 구성됨.
- Thread Stacks: 각 스레드별 스택 공간.
- Code/Data: 프로그램 바이너리 및 전역 변수.
- Heap: 동적 할당 메모리 (malloc).
- Shared Libs: libc.so 등 공유 라이브러리.

ASLR (Address Space Layout Randomization):
- 보안을 위해 메모리 블록들의 위치를 실행할 때마다 랜덤하게 섞음. (기본값: On)
- Safety 관점의 딜레마: Safety 시스템은 "항상 똑같이 동작함"을 증명해야 하는데, ASLR은 매번 주소를 바꾸므로 Safety 인증 시에는 끄는 것을 고려할 수도 있음.


💡 정리 및 인사이트
- 프로세스는 '보호막'이다: 자원을 담고, 외부의 간섭을 차단하며, 죽으면 자원을 자동 청소해주는 안전장치임.
- 스레드는 '실행기'이다: 자원을 공유하며 실제 코드를 수행함. 하지만 멀티스레딩은 "혼돈(Chaotic)"을 불러오므로, 꼭 필요한 경우(데드라인 준수, 병렬 처리)에만 써야 함.
- 설계 원칙: 기능 단위로 프로세스를 쪼개라. (Microkernel 철학). 내부가 스레드로 구현됐는지 아닌지는 외부에 숨기고(Opaque), 인터페이스로만 통신하라.
'운영체제 > QNX' 카테고리의 다른 글
| QNX RTOS: 4-3. Processes - Detecting Termination (0) | 2025.11.26 |
|---|---|
| QNX RTOS: 4-2. Processes - Creation (0) | 2025.11.26 |
| QNX RTOS: 3. Security Policies (0) | 2025.11.26 |
| QNX RTOS: 2-1. Running and Debugging (0) | 2025.11.26 |
| QNX RTOS: 2. Momentics Development (0) | 2025.11.26 |