Process Management (1), (2)

🔊 이화여자대학교 반효경 교수님의 KOCW 2014년 1학기 운영체제 강의를 들으며 정리한 노트입니다.
      캡쳐한 이미지 중 따로 출처 명시를 하지 않은 이미지 또한 반효경 교수님 강의 자료에 있음을 밝힙니다.

프로세스 생성 (Process Creation)

  • 부모 프로세스(Parent process)가 자식 프로세스(children process) 생성

    부모 프로세스 하나가 자식을 복제해서 만듬, 여러 개를 만들 수도 있음
    밑에 갈수록 삼각형 형태의 족보가 만들어짐

  • 프로세스의 트리(계층 구조) 형성

  • 프로세스는 자원을 필요로 함

    • 운영체제로부터 받는다
    • 부모와 공유한다
  • 자원의 공유

    • 부모와 자식이 모든 자원을 공유하는 모델
    • 일부를 공유하는 모델
    • 전혀 공유하지 않는 모델

    원칙적으로 자원을 공유하지는 않는다.
    부모 프로세스가 자식을 하나 낳아놓으면 그때부터는 별도의 프로세스이기 때문에 서로 경쟁하는 사이가 된다. (각자 CPU, 메모리를 서로 얻으려고 경쟁)

    보통 공유하는 모델보다는 공유하지 않는 모델이 일반적이다.

    그럼 자원 공유는? 자원 공유에 대한 얘기…
    공유라는 얘기가 왜 나오냐하면 자식 프로세스가 부모 프로세스의 code, data, stack을 메모리에 그대로 copy
    그러면, 메모리에 두 카피가 올라가기 때문에 메모리 낭비가 되는 것이다.
    원래 부모하고 자식이 내용이 똑같다고 하면 (쓰레드와 마찬가지 이유로) 당장 copy를 할 필요는 없을 것이다.

    그래서, (부모 프로세스를 copy해서 code, data, stack.. 즉, 주소 공간이 만들어지는게 원칙이지만) 리눅스나 좀 더 효율적인 일부 운영체제에서는 부모 프로세스의 모든 것을 copy하는 것이 아니라 자식이 그 부모의 주소 공간을 공유하고 있다. (program counter만 하나 copy해서 똑같은 위치를 가리키고 있게 될 것이다.)

    그런 상태로 실행을 하다가 별개의 프로세스이기 때문에 부모하고 자식이 내용이 달라질 수 있을 것이다. (예를 들면, code, data, stack 중에서 data에 있는 변수의 값이 달라진다거나, 서로 각자 CPU를 잡고 instruction을 실행하기 때문에 함수 호출이나 이런게 달라져서 stack에 쌓이는 내용도 달라지는 등) 결국에는 각자의 길을 가게 되는데, 각자의 길을 가게 되면 그제서야 부모의 공유하던 메모리 공간의 일부를 자식이 copy해서 갖게 된다. 그러한 기법을…

    • Copy-on-write(COW) 기법 이라고함
      • write가 발생했을 때 copy를 하겠다는 뜻
      • write가 발생한다는 것은 원래 있던 내용을 바꾸는 거임
        내용이 바뀔 때 그것을 copy해서 새로운 것을 만들고, (내용이 똑같은)그 이전까지는 그냥 부모꺼를 그대로 공유하고 있는 것

    그래서, 공유를 할 수 있으면 공유를 하는게 효율적이다 라는 얘기이고,
    그렇지만 별개의 프로세스이기 때문에 그래서 독립적으로 가지고 있는게 원칙이다 라는 얘기

  • 수행 (Execution)

    • 부모와 자식은 공존하며 수행되는 모델
    • 자식이 종료(terminate)될 때까지 부모가 기다리는(wait) 모델
  • 주소 공간 (Address space)

    • 자식은 부모의 공간을 복사함 (binary and OS data)

    • 자식은 그 공간에 새로운 프로그램을 올림

      자식 프로세스는 부모의 주소 공간을 그대로 복사하고, 운영체제에 있는 데이터들(PCB, 자원들)도 똑같이 복사

      일단 복제를 해놓은 다음에 그 복제된 곳에 새로운 프로그램을 덮어씌울 수가 있음. 그렇게 해서 서로 다른 프로그램들이 컴퓨터 내에 존재할 수가 있는 것임.

  • 유닉스의 예

    • fork () 시스템 콜이 새로운 프로세스를 생성

      • 부모를 그대로 복사 (OS data except PID + binary)
      • 주소 공간 할당
    • fork 다음에 이어지는 exec () 시스템 콜을 통해 새로운 프로그램을 메모리에 올림

      복제 생성한다고 말한 것이 fork() 라는 시스템 콜의 역할이 되고,

      복제 생성을 한 다음에 다른 프로그램으로 덮어씌우는 것이 exec() 이라는 시스템 콜이 되겠다.

      그래서 프로세스의 생성은 보통 2단계를 거치는데,
      하나는 부모 프로세스를 복제하는 단계
      또 하나는 거기다가 새로운 프로그램을 덮어씌우는 단계
      이게 독립적이기 때문에 복제만 해놓고 덮어씌우지 않을 수도 있고,
      자식 프로세스를 만들지 않고 그냥 exec()을 하면 완전히 새로운 프로세스로 바뀔 수도 있음.(지금까지 살아온게 맘에 안들면 다른 프로그램을 완전히 덮어씌워서 새로운 인생을 사는…그게, exec()이라는 시스템 콜)

      프로세스의 생성은 부모 프로세스가 자식 프로세스를 생성한다고 말했지만, 프로세스를 사용자 프로세스가 직접 다른 프로세스를 생성하는게 아니고,
      운영체제를 통해서만 생성이 가능하다.
      fork()나 exec() 같은 것이 시스템 콜이기 때문에 운영체제한테 자식을 낳아달라고 부탁을 해서 운영체제가 대신 낳아주는 그런 개념이 되겠다.

fork() 시스템 콜

  • A process is created by the fork() system call.
    • creates a new address space that is a duplicate of the caller.

image

빨간색으로 fork() 라고 함수를 호출하고 있는데, 이게 바로 시스템 콜.
운영체제한테 새로운 프로세스를 하나 만들어달라는 요청

fork()라는 함수가 호출되는 순간 자식 프로세스는 실행되게 된다. fork()를 실행하면 부모 프로세스는 fork()의 결과값이 양수이고(pid로 양수를 받고), 자식 프로세스는 fork()를 한 결과값으로 0을 받는다(pid로 0을 받음).
이렇게 부모 프로세스와 자식 프로세스를 구분할 수 있다.

fork의 결과 값이 다르기 때문에 세상에 있는 모든 프로세스가 같은 일을 해야되는건 아닐 것이다. 실제로는 다른 프로그램들을 실행시킬 수가 있어야 할 것이다. 그래서 존재하는 시스템 콜이 exec() 시스템 콜이다.

exec() 시스템 콜

  • A process can execute a different program by the exec() system call.
    • replaces the memory image of the caller with a new program.

image

exec() 시스템 콜은 어떤 프로그램을 완전히 새로운 프로세스로 태어나게 하는 역할을 해준다.

위와 같은 코드를 가진 프로그램이 실행이 되다가 exec() 시스템 콜(빨간색execlp부분)을 만나게 되면 이러한 기억은 완전히 잊어버리고, 소개되는 새로운 프로그램("/bin/date")으로 완전히 덮어 씌우는 것
exec()을 하게되면 이 "/bin/date" 프로그램의 (메인 함수)시작 부분부터 실행을 함

execlp에다가 위와 같은 식으로 프로그램 이름을 따옴표 안에다가 2번 넣어주고, 그 다음에 프로그램한테 전달할 argument를 쭉 적어준 다음, 맨 마지막에 null pointer(0)를 넣어주게 되면 이 프로그램을 실행 시키게 되는 것이다.

한번 exec() 하면 절대로 다시 되돌아올 수 없다. 예를 들어,

int main()
    printf("1");
    execlp("echo", "echo", "3", (char *) 0);
    printf("2");
}

만약 위와 같이 코딩을 하고 실행하게 되면 printf("2");부분은 영원히 실행되지 않는다.

프로세스 종료 (Process Termination)

  • 프로세스가 마지막 명령을 수행한 후 운영체제에게 이를 알려줌 (exit)

    • 자식이 부모에게 output data를 보냄 (via wait).

    • 프로세스의 각종 자원들이 운영체제에게 반납됨

      프로세스의 세계에서는 부모 프로세스가 자식 프로세스를 낳아놓으면 자식이 먼저 종료가 되어야 한다.

      프로세스가 종료될 때는 wait 시스템 콜을 통해서 부모한테 데이터를 전달

  • 부모 프로세스가 자식의 수행을 종료시킴 (abort)

    비자발적으로 프로세스가 종료되는 경우, 그것을 abort라고 한다…강제종료

    • 자식이 할당 자원의 한계치를 넘어섬

      (어떤 자원의 할당치를 넘어선 그런 요청을 할 때)
      (자식을 낳아놨더니 너무 펑펑쓰니까, 안되겠다 싶어서 부모가 강제로 죽임)

    • 자식에게 할당된 태스크가 더 이상 필요하지 않음

      (더 이상 자식에게 시킬 일이 없을 때)

    • 부모가 종료(exit)하는 경우

      (부모 프로세스가 종료되는 경우)

      • 운영체제는 부모 프로세스가 종료하는 경우 자식이 더 이상 수행되도록 두지 않는다

      • 단계적인 종료

        부모 프로세스가 하나 종료될 때 그 자식의 자식 즉, 손자의 자손들까지 다 먼저 죽이고 나서 그 위에꺼 죽고, 그 다음 위에꺼가 죽고 이렇게 단계적으로 종료가 이루어짐

wait() 시스템 콜

  • 프로세스 A가 wait() 시스템 콜을 호출하면

    • 커널은 child가 종료될 때까지 프로세스 A를 sleep시킨다 (block 상태)

    • child process가 종료되면 커널은 프로세스 A를 깨운다 (ready 상태)

      자식 프로세스가 종료되기를 기다리면서 block 상태가 됨

      그랬다가 자식 프로세스가 종료되면 부모 프로세스가 block에서 ready 상태로 바뀌면서 CPU를 얻을 수 있게 되는 것

image

(else 부분)… 부모 프로세스라면, wait()을 실행하도록
이렇게 wait() 시스템 콜을 호출하게 되면 부모 프로세스는 잠들게 된다. block 상태가 되서 CPU를 얻지 못하고 기다리게 된다는 뜻.

언제까지 기다리느냐?
if(childPID == 0)안에 정의되있는 코드를 다 실행하고, 자식 프로세스가 종료가 되면 그때, 이 부모 프로세스는 wait() 시스템 콜 불러오는 else부분을 빠져나가서 다음 코드를 실행할 수 있게 된다.

exit() 시스템 콜

  • 프로세스의 종료
    • 자발적 종료
      • 마지막 statement 수행 후 exit() 시스템 콜을 통해
      • 프로그램에 명시적으로 적어주지 않아도 main 함수가 리턴되는 위치에 컴파일러가 넣어줌
    • 비자발적 종료
      • 부모 프로세스가 자식 프로세스를 강제 종료시킴
        • 자식 프로세스가 한계치를 넘어서는 자원 요청
        • 자식에게 할당된 태스크가 더 이상 필요하지 않음
      • 키보드로 kill, break 등을 친 경우
        (사람이 (강제로) 종료시키는 경우)
      • 부모가 종료하는 경우
        • 부모 프로세스가 종료하기 전에 자식들이 먼저 종료됨

프로세스 간 협력

  • 독립적 프로세스(Independent process)
    • 프로세스는 각자의 주소 공간을 가지고 수행되므로 원칙적으로 하나의 프로세스는 다른 프로세스의 수행에 영향을 미치지 못함
  • 협력 프로세스(Cooperating process)
    • 프로세스 협력 메커니즘을 통해 하나의 프로세스가 다른 프로세스의 수행에 영향을 미칠 수 있음
  • 프로세스 간 협력 매커니즘(IPC: Interprocess Communication)
    • 메시지를 전달하는 방법
      • message passing: 커널을 통해 메시지 전달
    • 주소 공간을 공유하는 방법
      • shared memory: 서로 다른 프로세스 간에도 일부 주소 공간을 공유하게 하는 shared memory 메커니즘이 있음
      • thread:
        thread는 사실상 하나의 프로세스이므로 프로세스 간 협력으로 보기는 어렵지만 동일한 process를 구성하는 thread들 간에는 주소 공간을 공유하므로 협력이 가능

Message Passing

  • Message system
    • 프로세스 사이에 공유 변수(shared variable)를 일체 사용하지 않고 통신하는 시스템
  • Direct Communication
    • 통신하려는 프로세스의 이름을 명시적으로 표시
      image
  • Indirect Communication
    • mailbox (또는 port)를 통해 메시지를 간접 전달
      image

어쨋든, 메시지를 전달하려면 운영체제 커널을 통해서 전달할 수 있다는 것은 둘다 똑같다.
프로세스 대 프로세스로 사용자 프로세스끼리 뭔가를 전달하는 것은 불가능하기 때문에, 항상 중간에 운영체제 커널이 들어가있어야 되는 것이다.

다만, 인터페이스 측면에서 Direct Communication인 경우에는 메시지를 전달하는 프로세스가 전달한다고 하고서 그 메시지 내용, 누구한테 전달할지 프로세스를 직접 명시하는 것

Indirect Communication은 메시지를 보내는 측에서 누가 받아볼지를 명시하는게 아니라 커널에 존재하는 우체통(Mailbox)에다가 메시지를 집어 넣는다. 만약 꺼내볼 수 있는 대상이 혼자밖에 없다면 Process Q 라는 친구가 당연히 꺼내보게 될텐데…
경우에 따라서는 메시지를 집어넣으면서 아무나 꺼내가라고 할 수도 있다.

둘다 커널을 통해서 메시지를 전달하지만,
메시지를 받아 볼 프로세스를 명시하느냐, 아니면 메일박스에다가 메시지를 집어넣느냐가 이게 두 가지 방법의 차이이다.

image

Shared Memory

위 이미지 참고

원칙적으로 프로세스들은 독자적인 주소 공간을 가지고 있기 때문에(code, data, stack을 각자 가지고 있어서) 자기 주소 공간만 볼 수 있는데,
shared Memory라는건 그럼에도 불구하고 일부 주소 공간을 갖다가 두 프로세스가 공유하는 방법

물리적인 메모리에 매핑할 때 일부 영역은 공유되도록 매핑을 해 놓은 것

이렇게 되면 예를 들어, 프로세스 A가 shared memory영역에 어떤 내용을 적으면 프로세스 B는 그것이 자신의 주소 공간에도 포함이 되있기 때문에 그 내용을 바로 전달받아서 볼 수가 있다.

Shared Memory도 프로세스끼리 직접 당장 메모리 주소 공간을 공유할 수 있는게 아니라 kernel한테 Shared Memory를 쓴다는 시스템 콜을 해서, 매핑을 해놓고 공유를 하게 해놓은 다음에 그때부터 사용자 프로세스끼리 Shared Memory영역은 서로 한 쪽에서 쓰면 다른 쪽에서 보이는 영역이 된다.

일단 처음에는 kernel의 도움을 받지만, kernel이 한번해주면 그때부터는 사용자 프로세스 둘이서 shared memory에서 작업을 하는거기 때문에 둘이서 신중하게 해야 될 것이다. (둘이서 점잖게 shared memory를 잘 써야지만 원하는 결과가 나오지, 한쪽 프로그램이 이상하게 뭔가 작업을 해버리면(잘못하면) 원하지 않는 잘못된 결과가 나올 수도 있음)

그러므로, shared memory를 쓰려면 두 프로세스는 상당히 신뢰할 수 있는 관계여야만 된다.

맨 위로 이동하기

Leave a comment