[unix] 새로운 프로세스를 생성하기 위해 분기해야하는 이유는 무엇입니까?

유닉스에서는 새로운 프로세스를 만들고 싶을 때마다 현재 프로세스를 분기하여 부모 프로세스와 정확히 동일한 새 자식 프로세스를 만듭니다. 그런 다음 exec 시스템 호출을 수행하여 상위 프로세스의 모든 데이터를 새 프로세스의 데이터로 바꿉니다.

처음에 상위 프로세스의 사본을 작성하고 새 프로세스를 직접 작성하지 않는 이유는 무엇입니까?



답변

짧은 대답은 fork당시의 기존 시스템에 맞추기 쉽고 버클리이전 시스템 이 포크 개념을 사용 했기 때문에 Unix에 있습니다 .

에서 유닉스 시간 공유 시스템의 진화 (관련 텍스트가되었습니다 강조 ) :

현대적인 형태의 프로세스 제어는 며칠 내에 설계 및 구현되었습니다. 기존 시스템에 얼마나 쉽게 적용되는지 놀랍습니다. 동시에 , 디자인에서 약간 특이한 특징 중 일부가 존재하는 것에 대한 작고 쉽게 코딩 된 변경 사항을 나타 내기 때문에 어떻게 정확하게 나타나는지 쉽게 알 수 있습니다 . 좋은 예는 포크와 실행 함수의 분리입니다. 새로운 프로세스 생성을위한 가장 일반적인 모델은 프로세스가 실행할 프로그램을 지정하는 것입니다. 유닉스에서, 분기 프로세스는 명시 적 실행을 수행 할 때까지 상위 프로세스와 동일한 프로그램을 계속 실행합니다. 기능의 분리는 확실히 유닉스 고유의 것이 아니며, 실제로는 톰슨에게 잘 알려진 버클리 시간 공유 시스템에 존재했습니다.. 여전히 Unix에 존재 한다고 가정하는 것이 합리적 입니다. 포크를 쉽게 변경하지 않고도 쉽게 구현할 수 있기 때문입니다 . 시스템은 이미 여러 프로세스 (즉, 두 개)를 처리했습니다. 프로세스 테이블이 있었고 프로세스는 기본 메모리와 디스크간에 교환되었습니다. 포크의 초기 구현에만 필요

1) 공정 테이블 확장

2) 기존 스왑 IO 프리미티브를 사용하여 현재 프로세스를 디스크 스왑 영역에 복사하고 프로세스 테이블을 약간 조정 한 포크 호출 추가.

실제로 PDP-7 포크 호출에는 정확히 27 줄의 어셈블리 코드가 필요했습니다. 물론 운영 체제와 사용자 프로그램의 다른 변경이 필요했으며 그 중 일부는 다소 흥미롭고 예상치 못한 결과였습니다. 그러나 exec가 존재하지 않았기 때문에 결합 된 fork-exec는 훨씬 더 복잡 했을 것입니다. 이 기능은 명시 적 IO를 사용하여 셸에서 이미 수행되었습니다.

그 논문 이후, 유닉스는 발전했다. fork다음에 exec더 이상 프로그램을 실행할 수있는 유일한 방법입니다.

  • vfork 는 새로운 프로세스가 포크 바로 다음에 exec를 수행하려는 경우보다 효율적인 포크로 만들어졌습니다. vfork를 수행 한 후 상위 프로세스와 하위 프로세스는 동일한 데이터 공간을 공유하며 하위 프로세스가 프로그램을 실행하거나 종료 될 때까지 상위 프로세스가 일시 중단됩니다.

  • posix_spawn 은 새 프로세스를 작성하고 단일 시스템 호출로 파일을 실행합니다. 호출자의 열린 파일을 선택적으로 공유하고 신호 처리 및 기타 속성을 새 프로세스에 복사 할 수있는 많은 매개 변수가 필요합니다.


답변

[내 답변의 일부를 여기 에서 반복 하겠습니다 .]

처음부터 새로운 프로세스를 만드는 명령이없는 이유는 무엇입니까? 바로 교체 될 제품을 복사하는 것이 터무니없고 비효율적이지 않습니까?

실제로 몇 가지 이유로 효율적이지 않을 수 있습니다.

  1. fork()커널이 COW ( Copy-On-Write) 시스템을 사용하기 때문에 “복사” 는 약간 추상화 된 것입니다 . 실제로 만들어야하는 것은 가상 메모리 맵입니다. 복사가 즉시을 호출 exec()하는 경우 프로세스의 활동에 의해 수정 된 경우 복사 된 대부분의 데이터는 프로세스에서 사용이 필요한 작업을 수행하지 않으므로 실제로 복사 / 생성 할 필요가 없습니다.

  2. 하위 프로세스 (예 : 환경)의 다양한 중요한 측면은 컨텍스트 등의 복잡한 분석을 기반으로 개별적으로 복제되거나 설정 될 필요가 없습니다. 단지 호출 프로세스와 동일하다고 가정합니다. 이것은 우리에게 익숙한 상당히 직관적 인 시스템입니다.

# 1을 조금 더 설명하기 위해, “복사”되었지만 이후에 액세스되지 않는 메모리는 적어도 대부분의 경우 실제로 복사되지 않습니다. 이 컨텍스트에서 예외 는 프로세스를 분기 한 다음 하위 프로세스가로 대체되기 전에 상위 프로세스를 종료 한 경우 일 수 있습니다 exec(). 내가 말할 힘을 여유 메모리가 충분한 경우 부모의 대부분은 캐시 할 수 있기 때문에, 나는 (운영 체제 구현에 달려있는) 어느 정도이 악용 될 것이 확실하지 않다.

물론, 표면에없는 것은 빈 슬레이트를 사용하는 것보다 복사본을 효율적으로 사용하는 것입니다. “빈 슬레이트”는 말 그대로 아무것도 아니며 할당을 포함해야합니다. 시스템은 동일한 방식으로 복사하는 일반 빈 / 새 프로세스 템플릿을 가질 수 있지만 , 1 은 복사 중 작성 포크와 비교하여 아무것도 저장하지 않습니다. 따라서 # 1은 “새”빈 프로세스를 사용하는 것이 더 효율적이지 않다는 것을 보여줍니다.

포인트 # 2는 포크 사용이 더 효율적인 이유를 설명합니다. 자식 환경은 완전히 다른 실행 파일이더라도 부모 환경에서 상속됩니다. 예를 들어, 상위 프로세스가 쉘이고 하위가 웹 브라우저 인 $HOME경우 여전히 둘 다 동일하지만 둘 중 하나가 변경 될 수 있으므로 두 개의 별도 사본이어야합니다. 아이의 것은 원본에 의해 만들어집니다 fork().

1. 말 그대로 의미가없는 전략이지만 프로세스를 만들려면 디스크에서 이미지를 메모리에 복사하는 것 이상의 작업이 필요합니다.


답변

유닉스가 fork새로운 프로세스를 만드는 기능 만 가지고있는 이유 는 유닉스 철학 의 결과 라고 생각합니다

그들은 하나의 일을 잘하는 하나의 기능을 만듭니다. 자식 프로세스를 만듭니다.

새로운 프로세스로하는 일은 프로그래머에게 달려 있습니다. 그는 exec*함수 중 하나를 사용 하고 다른 프로그램을 시작할 수 있거나 exec를 사용하지 못하고 동일한 프로그램의 두 인스턴스를 사용할 수있어 유용 할 수 있습니다.

사용할 수 있기 때문에 더 큰 자유를 얻습니다

  1. exec가없는 포크 *
  2. exec *가있는 포크 또는
  3. 포크없이 그냥 exec *

그리고 추가로 당신은 단지 암기해야 fork하고, exec*1970 년대에 당신이해야 할 일을했을 함수 호출을.


답변

프로세스 생성의 두 가지 철학이 있습니다. 상속을 가진 포크와 인수로 만드는 것입니다. 유닉스는 분명히 포크를 사용합니다. (예를 들어, OSE 및 VMS는 create 메소드를 사용합니다.) Unix에는 상속 가능한 특성이 많으며 더 정기적으로 추가됩니다. 상속을 통해 기존 프로그램을 변경하지 않고도 이러한 새로운 특성을 추가 할 수 있습니다! 인수로 작성 모델을 사용하여 새 특성을 추가하면 작성 호출에 새 인수를 추가해야합니다. 유닉스 모델이 더 간단합니다.

또한 프로세스가 여러 조각으로 나눌 수있는 매우 유용한 포크없는 실행 모델을 제공합니다. 이것은 비동기 I / O 형식이 없을 때 중요했으며 시스템에서 여러 CPU를 활용할 때 유용합니다. (사전 스레드) 나는 수년 동안, 심지어 최근에도 이것을 많이 해왔습니다. 본질적으로 여러 ‘프로그램’을 단일 프로그램으로 컨테이너화 할 수 있으므로 손상이나 버전 불일치 등이 전혀 없습니다.

포크 / 실행 모델은 또한 특정 아동이 포크와 실행 사이에 설정된 근본적으로 이상한 환경을 상속받을 수있는 능력을 제공합니다. 특히 상속 된 파일 디스크립터와 같은 것들. (stdio fd ‘s의 확장) create 모델은 create call 생성자가 상상하지 않은 것을 상속하는 기능을 제공하지 않습니다.

일부 시스템은 고유 코드의 동적 컴파일을 지원할 수 있으며, 여기서 프로세스는 자체 고유 코드 프로그램을 작성합니다. 다시 말해, 소스 코드 / 컴파일러 / 링커주기를 거치지 않고 디스크 공간을 차지하지 않고 즉시 자체 작성하는 새 프로그램을 원합니다. (이 작업을 수행하는 Verilog 언어 시스템이 있다고 생각합니다.) 포크 모델이이를 지원하지만 작성 모델은 일반적으로 지원하지 않습니다.


답변

fork () 함수는 아버지 프로세스를 복사 할뿐 아니라 프로세스가 아버지 또는 아들 프로세스임을 나타내는 값을 리턴합니다. 아래 이미지는 fork ()를 아버지로서 사용하는 방법을 설명합니다. 아들:

여기에 이미지 설명을 입력하십시오

프로세스가 아버지 인 경우에 표시되는 것처럼 fork ()는 아들 프로세스 ID를 리턴하고 PID 그렇지 않으면 리턴합니다.0

예를 들어 요청을 수신하는 프로세스 (웹 서버)가 있고 요청마다이 요청 son process을 처리 할 프로세스를 작성하는 경우이를 사용할 수 있습니다. 여기에서 아버지와 아들은 다른 작업을 갖습니다.

따라서 프로세스 복사본을 실행하지 않는 것은 fork ()와 정확히 일치하지 않습니다.


답변

I / O 리디렉션은 포크 후 및 실행 전에 가장 쉽게 구현됩니다. 자식은 자식임을 알고 파일 부모를 닫지 않고 파일 설명자를 닫고, 새 설명자를 열고, dup () 또는 dup2 () 올바른 fd 번호 등으로 가져올 수 있습니다. 그렇게 한 다음, 원하는 환경 변수 변경 (부모에 영향을 미치지 않음)을 수행 한 후에는 조정 된 환경에서 새 프로그램을 실행할 수 있습니다.


답변

나는 여기의 모든 사람들이 포크가 어떻게 작동하는지 알고 있다고 생각하지만 포크를 사용하여 부모의 정확한 복제본을 만들어야하는 이유는 무엇입니까?
답변 ==> 서버의 예를 들어 (포크없이), client-1이 서버에 액세스하는 동안, 동시에 client-2가 도착하여 서버에 액세스하려고하지만 서버가 새로 도착한 서버에 권한을 부여하지 않는 경우 client-2는 서버가 client-1을 서비스하기 때문에 바쁘기 때문에 client-2는 기다려야합니다. client-1에 대한 모든 서비스가 완료된 후 client-2는 이제 서버에 액세스 할 수 있습니다. client-3가 도착하면 client-3는 client-2에 대한 모든 서비스가 완료 될 때까지 기다려야합니다. 수천 명의 클라이언트가 동시에 서버에 액세스해야하는 시나리오를 수행하십시오. 대기 (서버가 바쁘다 !!).

이것은 (포크를 사용하여) 서버의 정확한 복제본 (즉, 하위)을 생성함으로써 피할 수 있습니다. 여기서 각 하위 (상위 즉 서버의 정확한 복제본)는 새로 도착한 클라이언트 전용이므로 모든 클라이언트가 동시에 액세스합니다 섬기는 사람.