[ServerProgramming] 운영체제 복습 - 프로세스와 스레드

2024. 10. 4. 08:57CS/ServerProgramming

1. Process의 기본 개념과 생성과 종료

Process : 프로그램을 실행한 인스턴스. 인스턴스라는 것은 복제본이 여러 개 돌아갈 수 있다는 것이다.

Program : 순서있는 명령어 집합. 하드웨어에 a.exe로 저장되어 있을 것이다.

Task, Job : 프로세스와 유사한 개념.

  • 프로세스는 Conrtrol flow를 가지고 있다. 맨 위에부터 차례로 실행한다는 뜻이다. 내부에 변수가 바뀐다던지 control flow가 바뀌는 등 dynamic 하다.
  • 운영체제 내부에서는 프로세스를 관리하기 위해 PID를 가지고 있다.
  • 프로세스는 CPU context, OS resource, PID, 상태값, 소유자 등의 정보를 가지고 있다.
  • 프로그램은 data와 code를 가지고 있고 이를 실행시키면 메모리에 할당돼서 메모리를 쓰게 된다.
  • 운영체제는 프로세스가 hierarchy를 가지게 한다. 따라서 par, child 관계가 항상 만들어 진다. 이런 계층 구조를 프로세스 그룹이라 한다.

프로세스의 생성은 운영체제가 fork 시스템 콜을 사용하므로써 이루어진다. 태초에는 운영체제가 1번 프로세스를 만든다.

프로세스를 만들게 되면 parent는 child에게 자신이 가진 리소스와 권한을 모두 물려준다. 소유주, parent가 열어놓고 작업하던 파일이나 소켓을 모두 물려주게 된다. parent는 child가 끝날 때 까지 기다릴 수 있고 동시에 실행될 수 있다.

각 프로세스는 자신만의 어드레스 스페이스를 가진다. 특히 child는 기본적으로 parent의 어드레스 스페이스를 그대로 가지게 된다.

프로세스의 종료는 4가지 케이스가 있다.

  1. Normal exit : 프로세스가 return 0를 만나서 자발적으로 종료하는 경우
  2. Error exit : return -1을 만나서 에러 코드를 리턴하는 것이다. 프로그래머가 의도적으로 넣어놓는 것이다.
  3. Fatal error : 프로그램을 잘못짜서 강제 종료되는 경우이다.
  4. Killed by another process : 권한이 있는 다른 프로세스가 죽이는 경우이다.

fork() 는 시스템 콜이다. 시스템 콜은 운영체제가 제공하는 일종의 API이다.

fork는 메모리 어드레스 스페이스를 그대로 복제해서 자식에게 넘겨주고 par에게는 child의 pid를 리턴하고 child에게는 0을 리턴한다.

왜 fork()를 사용할까?

parent와 같이 일하는 여러 개의 프로세스를 만들어야 할 때 주로 사용한다. 웹 서버를 예시로 들면 소켓을 열고 기다리는 역할도 필요하고 클라이언트가 접속하면 클라이언트의 요청을 처리하는 역할도 필요하다. 따라서 fork를 사용해서 par은 다시 소켓을 listen하고 child는 클라이언트의 요청을 처리하고 죽는다.


2. Zombie & Orphan Process

parent 프로세스가 종료되기 전에 child가 종료되면 child는 좀비 프로세스가 된다. parent는 child의 종료 상태를 알아야 하기에 운영체제가 테이블에 child를 제거하지 않고 좀비 상태로 두는 것이다.

물론 parent는 child가 죽었음을 확인하면 테이블에서 제거하지만 복잡한 프로세스를 만들다 보면 어떤 child의 정보를 확인하지 못했는지 놓치는 경우가 많다. 이 부분을 잘 신경써야 한다.

Orphan process는 부모가 먼저 종료된 child 프로세스를 말한다. 운영체제는 child 프로세스의 부모 PID를 1로 바꿔준다. 이런 경우는 의도적으로 만들기도 한다.


3. 프로세스의 상태 전이


4. Threads

부모 프로세스와 자식 프로세스는 별도의 어드레스 스페이스를 가진다. 그럼 똑같은 코드와 데이터, 권한, 파일, 소켓 등 자원을 가지지만 다른 어드레스 스페이스에 저장한다.

부모와 자식 프로세스가 다른 것은 PC, 레지스터, SP, stack 만 다르다. 따라서 좀 더 효율적으로 새로운 형태의 프로세스를 만들 수 있다.

이런식으로 쓰레드가 각자의 스택, execution state를 가진다는 것을 주의하자.

 

사실 웹서버를 만들 때 쓰레드로 만드는게 훨씬 유용하다.

멀티 스레드의 장점은 다음과 같다.

  • 멀티 프로세스보다 병렬성을 만들기 훨씬 쉽다. 스레드가 훨씬 빨리 만들어지고 메모리도 훨씬 적게 소모한다.
  • 프로그램의 구조도 좋아진다.
  • resource를 share하기 쉽다. 프로세스가 다르면 서로의 메모리를 절대 볼 수 없다. 힙 덩어리 마저도 공유할 수 있다.

5. pthreads

스레드를 사용하기 위해서는 pthread_create, pthread_exit, pthread_join 정도만 알면 된다.

  • int pthread_create (pthread_t *tid, pthread_attr_t *attr, void *(start_routine)(void *), void arg);*start_routine : 실행할 함수이다.
  • arg : 함수를 실행하는데 필요한 파라미터이다.
  • : *tid : 생성된 스레드 아이디를 받기 위한 파라미터이다. *attr : 환경 변수이다.

동기화 문제를 위해 여러 함수도 알아야하긴 한다.


6. Thread 이슈

프로세스의 한 스레드에서 fork, exec를 부르면?

  • 모든 스레드를 다 복제할건지, fork를 부른 single 스레드만 복제해서 single 스레드로 만들건지가 문제가 된다. 운영체제의 설계 문제이지만 스레드에서 fork를 부르지 말자. UNIX에서는 fork, fork1을 지원해서 둘 다 가능하다. pthread에서는 부른 스레드만 복제해서 single 프로세스를 만든다.
  • 그냥 이렇게 하지 말자!

스레드가 exec를 부르면?

  • 다른 스레드도 다 날아간다.

스레드가 exit을 부르면?

  • 다 날아간다.

child 스레드가 종료되기 전에 메인 스레드가 return, exit을 만나면?

  • return은 해당 스레드만 종료, exit은 프로세스 전체가 싹 다 종료된다.

'CS > ServerProgramming' 카테고리의 다른 글

[ServerProgramming] Make  (0) 2024.10.08
[ServerProgramming] Vi Editor, GCC  (0) 2024.10.07
[ServerProgramming] UNIX의 BASIC COMMAND를 배워보자!  (0) 2024.10.05