운영체제/쓰레드와 스케쥴링

운영체제 공부 1 - Process와 Thread

Recfli 2023. 12. 21. 03:25

[공부 이유]

 운영체제 전공이 끝난 지 1년 정도가 되었고 대부분의 내용을 까먹은 것 같다는 생각이 든다. 모든 내용을 다 정리하는 것은 시간적으로 불가능하다고 생각이 되고 학교에서 배운 교안과 인터넷 검색 내용 중 나에게 반드시 필요하다고 생각되는 부분만 조금 정리를 해보려고 한다. 이게 끝나면 Java Thread 프로그래밍을 따로 공부하는 중인데 상관 관계를 적는 방식으로 사용하기에 좋을 것 같다고 생각이 돼서 정리를 남긴다.

 

운영체제와 관련된 내용은 모두 C언어로 진행이 되었어서 코드는 C를 사용할 것 같다.

 

[프로세스]

 제일 먼저 알아야 될 것은 프로세스에 관해서 알아야 하지 않을까 싶다. 사전적 의미는 "컴퓨터에서 연속적으로 실행되고 있는 컴퓨터 프로그램", "메모리에 올라와 실행되고 있는 프로그램의 인스턴스(독립적인 개체)"라고 하는데 이게 확 와닿지는 않는다. "Everything is a File"이라는 리눅스의 철학에 맞게 작동하고 있는 모든 파일이 프로세스가 아닐까 싶다.

 

 여기서 중요한 부분은 메모리 구조라고 생각을 한다. 왜냐하면 프로세스와 쓰레드의 차이를 알 수 있고 메모리 때문에 모든 문제가 시작된다.

 

 

 프로세스 내부는 Code, Data, Stack, Heap를 개별로 가지고 있다. 또한 위의 그림에는 생략이 되어있고 순서도 잘못되어 있지만 Code영역이 가장 낮은 주소이고 Data영역, Heap영역, Stack영역으로 갈 수록 높은 주소에 할당되어있다. 각각의 영역이 담당하는 것은 아래와 같다.

 

Code영역: 실행할 프로그램의 코드 영역 --> 작성한 코드가 올라가는 공간이라고 생각을 하면 된다.

Data영역: 실행할 프로그램의 전역 변수와 정적 변수를 저장하는 영역이다.

Heap영역: C언어를 기준으로는 malloc 같이 사용자가 동적으로 할당한 영역으로 작동 시기에 크기가 결정이 된다.

Stack영역:  실행할 프로그램의 지역 변수와 매개변수가 저장된 공간으로 컴파일 타임에 크기가 결정된다는 특징이 있다.

 

또한 프로세스는 최소 1개의 스레드를 가지고 있다. 프로세스는 별도의 메모리 공간을 가지고 있기 때문에 자원에 접근하기 위해서는 파이프, 파일, 소켓 등의 방식을 사용을 해야 한다. 이 말은 프로세스 간의 데이터 이동은 쉽지 않다는 것을 의미한다.

 

[쓰레드]

 쓰레드는 "프로세스 내에서 실행되는 여러 흐름의 단위", "프로세스의 특정한 수행 경로" 등의 의미를 가지고 있다고 한다.

 

 

이미지를 보면 알 수 있듯이 프로세스를 표현할 때에는 가장 바깥 껍데기가 OS였는데 지금은 Process가 가장 바깥 껍데기이고 Thread마다는 개별의 Stack영역을 가진다. 이것이 의미하는 것은 개별지역 변수가 아닌 다른 부분은 쓰레드 간에 모두 공유할 수 있다는 것을 의미한다.

 

 이 말은 다르게 말하자면 Process간의 데이터를 주고 받는 것보다 Thread 간의 데이터를 주고 받는 것이 더 쉽다는 것을 의미한다. 이것이 가지는 장점도 있지만 손해도 있다. 서로 간의 공유된 자원을 동시에 변경하는 행동을 하면 원치 않는 결과가 나올 수도 있다. 예를 들면 이런 것이다. 굳이 코드로 적지는 않겠지만 전역변수 하나를 설정해서 Thread 2개를 생성한 다음에 그 전역변수 값을 1씩 더하는 걸 100만번 해보면 알 수 있다. 결과가 200만이 나올 수도 있지만 아닐 수도 있다. 

 

[멀티 프로세스와 멀티 쓰레드]

 멀티프로세스를 이용한 프로그래밍의 예시는 Linux 프로그래밍을 해보면 알겠지만 쉘이 대표적인 예시이다. 쉘창에서 명령어를 입력하면 보통 결과를 즉시 가지고 오지만 실제에서는 쉘창에서 해당하는 파일을 exec하여 새로운 Process를 만들고 거기에 사용자가 말한 명령어에 해당하는 파일과 인자로 덮어씌워버리는 방식으로 동작을 하는데 이런 경우가 아니면 쓰는 경우를 본 적이 없는 것 같다.

 

  안쓰는 이유는 간단하다. 멀티 프로세스 프로그래밍의 단점은 Context Switching에서의 오버헤드가 굉장히 크다. 이 말은 프로세스 간 switching이 일어날 때마다 Context 정보를 저장해야되는데 공유하는 영역이 하나도 없는 프로세스는 이 오버헤드가 상당히 크다. 그래서 운영체제 자체에서도 Scheduling의 기준을 프로세스가 아닌 쓰레드 단위로 둔다.

 

 멀티쓰레드는 공유하는 자원이 많고 별 개로 가진 자원은 상대적으로 적기 때문에 상대적으로 프로세스에 비해서 가볍다. 공유하고 있는 영역도 있기 때문에 Context Switching 과정이 상대적으로 가볍다. 멀티프로세스가 사용되는 경우는 Java 프로그래밍을 하다보면 함수를 실행시킬 때 생성되는 Thread를 보았다면 알 수 있듯이 가비지 컬렉터랑 Main 둘 다 실행되는 걸 볼 수 있다. 이런 경우 아니면 안드로이드를 해보면 코루틴이라고 계산이 복잡한 건 나중에 결과를 보내고 미리 화면을 띄울 수 있게 해주는 경우가 있다.

 

 멀티쓰레드 프로그래밍은 자원의 효율성, 처리 비용 감소 및  상대적으로 쉬운 쓰레드 간 통신 방식을 얻은 대신 반대로 설계 단계에서의 프로그래머의 자원 공유 문제에 대해서 주의를 기울여야 한다. 프로그래밍의 안정성과 효율성 간의 교환 같은 것으로 생각을 하면 좋지 않을까 싶다.

 

https://gmlwjd9405.github.io/2018/09/14/process-vs-thread.html

 

[OS] 프로세스와 스레드의 차이 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io