Unix에서는 새 프로세스를 만들고 싶을 때마다 현재 프로세스를 분기하고 상위 프로세스와 완전히 동일한 새 하위 프로세스를 만든 다음 exec 시스템 호출을 수행하여 상위 프로세스의 모든 데이터를 다음으로 바꿉니다. 새로운 프로세스 데이터.
새 프로세스를 직접 생성하는 대신 먼저 상위 프로세스의 복사본을 생성하는 이유는 무엇입니까?
답변1
짧은 대답은 fork
Unix입니다. 그 이유는 당시 기존 시스템에 쉽게 적합했기 때문입니다.버클리의 전신 시스템포크(fork)라는 개념이 사용됩니다.
~에서유닉스 시분할 시스템의 진화(관련 텍스트는가장 밝은 부분):
현대적인 형태의 프로세스 제어가 며칠 내에 설계되고 구현됩니다. 기존 시스템에 얼마나 쉽게 적용되는지는 놀랍습니다.디자인의 약간 특이한 특징 중 일부는 기존 콘텐츠에 대한 작고 코딩하기 쉬운 변경 사항을 나타내기 때문에 정확하게 나타났습니다.. 좋은 예는 포크와 실행 기능의 분리입니다. 새 프로세스를 생성하는 가장 일반적인 모델에는 실행할 프로세스를 지정하는 프로그램이 포함됩니다. Unix에서 분기된 프로세스는 명시적인 실행을 수행할 때까지 상위 프로세스와 동일한 프로그램을 계속 실행합니다. 기능 분리는 확실히 Unix에만 국한된 것이 아니며,실제로 톰슨이 알고 있던 버클리 시분할 시스템에도 존재했었다.. 그럼에도 불구하고 가정하는 것이 타당해 보인다.포크는 다른 것을 많이 변경하지 않고도 쉽게 구현할 수 있기 때문에 유닉스에 존재합니다.. 시스템은 여러(예: 두 개) 프로세스를 처리했습니다. 프로세스 테이블이 있고 프로세스가 주 메모리와 디스크 간에 교환됩니다. 처음에는 포크 구현만 필요합니다.
1) 프로세스 테이블 확장
2) 기존 스왑 IO 프리미티브를 사용하여 현재 프로세스를 디스크 스왑 영역에 복사하는 포크 호출을 추가하고 프로세스 테이블을 일부 조정했습니다.
실제로 PDP-7의 포크 호출에는 정확히 27라인의 어셈블리 코드가 필요합니다. 물론 운영 체제와 사용자 프로그램에 대한 다른 변경도 필요했는데, 그 중 일부는 매우 흥미롭고 예상치 못했던 것이었습니다. 하지만결합된 포크 실행이 더 복잡합니다., exec 자체가 존재하지 않기 때문에 해당 기능은 명시적 IO를 사용하여 셸에서 이미 수행되었습니다.
그 논문 이후 유닉스는 계속 발전해왔습니다. fork
두 번째는 exec
더 이상 프로그램을 실행하는 유일한 방법이 아닙니다.
포크보다 효율적인 포크로 생성되며, 새 프로세스가 포크 직후에 exec를 실행하려고 할 때 사용됩니다. vfork가 수행된 후 상위 프로세스와 하위 프로세스는 동일한 데이터 공간을 공유하며 하위 프로세스가 프로그램을 실행하거나 종료할 때까지 상위 프로세스는 일시 중지됩니다.
posix_spawn단일 시스템 호출로 새 프로세스를 만들고 파일을 실행합니다. 여러 매개변수를 사용하며 호출자가 연 파일을 선택적으로 공유하고 해당 신호 구성 및 기타 속성을 새 프로세스에 복사할 수 있습니다.
답변2
[내 대답의 일부를 반복하겠습니다.여기.]
처음부터 새 프로세스를 생성하는 명령을 사용하면 어떨까요? 곧 교체될 것을 복사하는 것은 터무니없고 비효율적이지 않을까요?
실제로 이는 다음과 같은 여러 가지 이유로 그다지 효과적이지 않을 수 있습니다.
fork()
커널이 사용하는 것처럼 결과 "복사본"은 다소 추상적입니다.쓰기 중 복사체계; 실제로 생성되어야 하는 것은 가상 메모리 맵뿐입니다. copy 가 즉시 호출되는 경우exec()
, 프로세스의 활동이 데이터를 수정하더라도 대부분의 데이터는 실제로 복사/생성될 필요가 없습니다. 프로세스는 데이터 사용에 필요한 작업을 수행하지 않기 때문입니다.하위 프로세스의 중요한 측면(예: 해당 환경)은 개별적으로 복사하거나 컨텍스트 등의 복잡한 분석을 기반으로 설정할 필요가 없습니다. 그들은 호출 프로세스와 동일하다고 가정하며 이는 우리에게 친숙한 상당히 직관적인 시스템입니다.
#1을 더 자세히 설명하자면, "복사"되었지만 액세스되지 않은 메모리는 적어도 대부분의 경우 실제로 복사되지 않습니다. 이 경우에는 예외가 있습니다가능한프로세스를 분기하는 경우 하위 프로세스가 이를 대체하기 전에 상위 프로세스가 종료되도록 하십시오 exec()
. 나는 말했다가능한사용 가능한 메모리가 충분하면 대부분의 부모가 캐시될 수 있기 때문에 이것이 어느 정도 악용될지는 확실하지 않습니다(OS 구현에 따라 다름).
물론 표면적으로 이것은 사본을 사용한다는 의미는 아닙니다.더빈 슬레이트를 사용하는 것보다 훨씬 더 효율적입니다. 단, "빈 슬레이트"는 실제로 아무것도 아니며 할당이 필요합니다. 시스템은 1 과 동일한 방식으로 복제하는 일반 빈/새 프로세스 템플릿을 가질 수 있지만 이는 기록 중 복사 분기에 비해 실제로 아무것도 저장하지 않습니다. 따라서 #1은 "새로운" 빈 프로세스를 사용하는 것이 더 효율적이지 않다는 것을 증명합니다.
포인트 2에서는 포크를 사용하는 것이 더 효율적인 이유를 설명합니다. 하위 환경은 완전히 다른 실행 파일이더라도 상위 환경에서 상속됩니다. 예를 들어 상위 프로세스가 셸이고 하위 프로세스가 웹 브라우저인 경우 $HOME
여전히 동일하지만 어느 쪽이든 나중에 변경할 수 있으므로 두 개의 별도 복사본이 되어야 합니다. 상자에 있는 것이 원본입니다 fork()
.
1. 이 전략은 문자 그대로 이해가 되지 않을 수도 있지만, 제가 요점은 프로세스를 생성하는 데에는 디스크에서 메모리로 이미지를 복사하는 것 이상이 포함된다는 것입니다.
답변3
fork
유닉스가 새로운 프로세스를 생성하는 능력만 가지고 있는 이유는 다음과 같습니다 .유닉스 철학
그들은 한 가지 일을 매우 잘 수행하는 기능을 구축합니다. 자식 프로세스를 생성합니다.
새로운 프로세스를 처리하는 방법은 프로그래머에게 달려 있습니다. 그는 exec*
함수 중 하나를 사용하고 다른 프로그램을 시작할 수 있습니다. 또는 exec를 사용하고 동일한 프로그램의 두 인스턴스를 사용할 수는 없습니다. 이는 유용할 수 있습니다.
따라서 다음을 사용할 수 있기 때문에 더 많은 자유를 얻을 수 있습니다.
- exec가 없는 포크*
- exec*를 사용한 포크 또는
- 포크 없이 그냥 실행*
게다가, 1970년대에 해야 했던 호출을 기억 fork
하고 기능하기 만 하면 됩니다 .exec*
답변4
fork() 함수는 상위 프로세스를 복사할 뿐만 아니라 해당 프로세스가 상위 프로세스인지 하위 프로세스인지를 나타내는 값도 반환합니다. 아래 이미지는 상위 및 하위 프로세스로 fork()를 사용하는 방법을 설명합니다. 아들:
그림에서 볼 수 있듯이, fork()는 프로세스가 부모 프로세스일 때 자식 프로세스 ID를 반환하고 PID
, 그렇지 않으면 반환합니다.0
예를 들어, 요청을 수신하는 프로세스(웹 서버)가 있고 각 요청에 대해 해당 요청을 처리하는 프로세스를 생성하는 경우 이 방법을 사용할 수 있습니다. son process
여기서 아버지와 아들은 서로 다른 작업을 수행합니다.
따라서 프로세스 복사본을 실행하지 않는 것은 fork()와 다릅니다.