Process Vs Thread
Process와 Thread에 대하여
이전 포스팅의 정리해둔 내용이 존재하여
다소 간략하게 다뤄본 후,
추가적인 내용을 적어보려 한다
Process
- OS 입장에서 실행 중인 프로그램
- 독립적인 가상 메모리 공간(Code/Data/BSS/Heap/Stack 등) 보유
- 한 프로세스의 오류가 다른 프로세스에 영향을 주지 않음 (메모리 독립성)
- 프로세스 문맥 전환 시, 높은 비용 필요
- 레지스터 저장 / 복원
- Page Table 교체 등
- 레지스터 저장 / 복원
Thread
- 프로세스의 실행 단위
- 독립적인 Stack 영역을 가지고, 나머지는 프로세스의 자원 공유
- 하나의 스레드에서 문제 발생 시, 해당 프로세스가 종료될 수 있음
- 스레드의 문맥 전환은 같은 프로세스 내의 스레드 전환이기에
비교적 저렴
- Page Table을 그대로 사용
- Page Table을 그대로 사용
TMI
IPC (Inter-Process Communication)
서로 다른 프로세스가 데이터를 주고 받기 위한 방법
-
프로세스들은 독립된 메모리 공간(가상 주소 공간)을 가지므로
직접 서로의 메모리에 접근 불가 -
그렇기에 OS가 중재하는 다양한 방식을 통해 데이터를 공유함
IPC의 종류들
-
- Pipe
- 부모 자식 관계 처럼, 서로 연관이 있는 프로세스들이 사용
(fork() 이후)
- 커널 버퍼를 통해 데이터가 흐르게 됨
- Write / Read
- 2개의 파일 디스크립터 이용
- 쉘 파이프라인 , 간단한 부모-자식 데이터 전달에 주로 사용
- Pipe
-
- Named Pipe
- 파일 시스템에 ‘이름’이 있는 파이프
mkfifo()로 생성하여, open/read/write 가능하게 함- 서로 관계없는 프로세스끼리 통신 가능
- 여러 프로세스 간의 ‘간단한 요청 - 응답’ 통신에 사용
- Named Pipe
-
- Message Queue
- OS 커널이 관리하는 ‘큐’ 구조에 메시지를 넣고 뺴는 방식
- ‘타입 + 데이터’를 통해 메시지 전달
- 송/수신자의 시간 일치가 필요 없기에, 비동기 통신 가능
(커널 공간의 버퍼 사용) - 여러 클라이언트가 ‘서버’에 요청을 던지거나,
프로세스 간, 구조화된 데이터 전달에 주로 사용
- Message Queue
-
- Shared Memory
- 여러 프로세스가 동일한 물리 메모리 영역을 ‘가상 주소 공간’에 매핑하여 공유
- 단순 메모리 접근이며, 커널을 거의 거치지 않아 가장 빠름
- 다만, 여러 프로세스가 동시에 건들 수 있기에 ‘경쟁 상태’ 발생 가능
(동기화 도구(세마포어 / 뮤텍스 등)와 같이 사용 필수) - 대용량 데이터, 고빈도 갱신이 필요한 경우 사용
- Shared Memory
-
- Semaphore / Mutex
- 동기화용 IPC
- 공유 자원을 안전하게 쓰기 위함(위쪽의 Shared Memory와 같이 사용함)
- 동기화 자체는 IPC가 아니지만, ‘자원 공유’와 연관된 개념이기에 같이 분류 됨
- Semaphore / Mutex
-
- Socket
- 네트워크 통신의 단위
(같은 머신 뿐 아니라 다른 머신에도 통신함)
- TCP/UDP 프로토콜을 이용한 통신
- 로컬/원격 에 상관없는 데이터 교환 모델
- 느리지만 확장성이 좋은 방식
- 전반적인 Client-Server 모델에서 사용
- Socket
-
- Signal
- 프로세스에 ‘이벤트/인터럽트’를 보내는 방식
- 단순히, ‘발생 여부’를 확인하는 방식
(Delegate 같은 느낌) - 프로세스 종료/정지 와 같은 상황 확인에 사용
- Signal
-
- Memory-mapped File (mmap)
- 파일을 메모리에 그대로 매핑하고
Read/Write 연산을 파일 I/O 대신 메모리 접근으로 처리
- 여러 프로세스가 같은 파일을 mmap 하면 Shared Memory처럼 사용 가능
- 파일과 메모리 연결에 따른 I/O 의 효율적 처리
- 대용량 파일 처리 및 결과 저장을 원하는 경우 사용
- Memory-mapped File (mmap)
Stack Overflow 예방 방법?
StackOverflow는 스택이 Guard Page를 침범하여
Page Fault를 발생시키고
OS가 이를 복구 불가한 오류로 판단하여 프로세스를 종료시킴
따라서 이러한 상황을 예방하기 위해
다양한 방식이 존재함
- 무한 재귀 및 깊은 재귀를 막기
- 종료 조건 검증 및 호출 구조 점검
(순환 호출 등의 경우를 방지하기) - DP 등을 통한 로직 재사용
- 반복문의 사용
(꼬리 재귀(tail Recursion)의 경우 C++ 최적화가 먹히지 않을 수 있음)
- 종료 조건 검증 및 호출 구조 점검
- 지나치게 큰 지역 변수 사용 X
- Heap으로 할당하는 동적 할당을 사용하기
- Heap으로 할당하는 동적 할당을 사용하기
댓글남기기