
Unix에서는 새 프로세스를 만들고 싶을 때마다 현재 프로세스를 분기하여 상위 프로세스와 정확히 동일한 새 하위 프로세스를 만듭니다. 그런 다음 exec 시스템 호출을 수행하여 상위 프로세스의 모든 데이터를 새 프로세스의 데이터로 대체합니다.
처음에 상위 프로세스의 복사본을 만들고 새 프로세스를 직접 만들지 않는 이유는 무엇입니까?
답변1
짧게 대답하자면, fork
Unix는 당시 기존 시스템에 쉽게 적응할 수 있었고,버클리의 전신 시스템포크라는 개념을 사용했습니다.
에서유닉스 시분할 시스템의 진화(관련 텍스트는강조 표시됨):
현대적인 형태의 프로세스 제어는 며칠 내에 설계되고 구현되었습니다. 기존 시스템에 얼마나 쉽게 적용되는지는 놀랍습니다. 동시에 방법을 쉽게 알 수 있습니다.디자인의 약간 특이한 특징 중 일부는 존재했던 것에 대한 작고 쉽게 코딩된 변경 사항을 나타내기 때문에 정확하게 존재합니다.. 좋은 예는 포크와 실행 기능의 분리입니다. 새로운 프로세스 생성을 위한 가장 일반적인 모델은 실행할 프로세스에 대한 프로그램을 지정하는 것입니다. Unix에서 분기된 프로세스는 명시적인 실행을 수행할 때까지 상위 프로세스와 동일한 프로그램을 계속 실행합니다. 기능의 분리는 확실히 Unix에만 국한된 것이 아니며,사실 그것은 톰슨에게 잘 알려진 버클리 시분할 시스템에 존재했습니다.. 그래도 그렇게 생각하는 것이 타당해 보인다.다른 것을 많이 변경하지 않고도 포크를 쉽게 구현할 수 있기 때문에 유닉스에 존재합니다.. 시스템은 이미 여러(예: 두 개) 프로세스를 처리했습니다. 프로세스 테이블이 있었고 프로세스는 주 메모리와 디스크 간에 교환되었습니다. 포크의 초기 구현에만 필요
1) 프로세스 테이블 확장
2) 기존 스왑 IO 프리미티브를 사용하여 현재 프로세스를 디스크 스왑 영역에 복사하고 프로세스 테이블을 일부 조정하는 포크 호출을 추가했습니다.
실제로 PDP-7의 포크 호출에는 정확히 27라인의 어셈블리 코드가 필요했습니다. 물론 운영 체제와 사용자 프로그램의 다른 변경도 필요했으며 그 중 일부는 다소 흥미롭고 예상치 못한 변경이었습니다. 하지만결합된 포크 실행은 훨씬 더 복잡했을 것입니다., exec가 존재하지 않았기 때문에; 그 기능은 쉘에 의해 명시적 IO를 사용하여 이미 수행되었습니다.
그 논문 이후 유닉스는 진화했습니다. 더 fork
이상 exec
프로그램을 실행하는 유일한 방법이 아닙니다.
vfork새로운 프로세스가 포크 직후에 실행을 수행하려는 경우를 위해 보다 효율적인 포크로 만들어졌습니다. vfork를 수행한 후 상위 프로세스와 하위 프로세스는 동일한 데이터 공간을 공유하며 상위 프로세스는 하위 프로세스가 프로그램을 실행하거나 종료할 때까지 일시 중지됩니다.
posix_spawn새로운 프로세스를 생성하고 단일 시스템 호출로 파일을 실행합니다. 호출자의 열린 파일을 선택적으로 공유하고 해당 신호 처리 및 기타 속성을 새 프로세스에 복사할 수 있는 여러 매개변수가 필요합니다.
답변2
[내 답변의 일부를 반복하겠습니다.여기.]
처음부터 새 프로세스를 생성하는 명령만 있으면 안 되는 이유는 무엇입니까? 당장 교체만 할 것을 복사하는 것은 터무니없고 비효율적이지 않나요?
실제로 이는 다음과 같은 몇 가지 이유로 효율적이지 않을 수 있습니다.
커널이 다음을 사용하기 때문에 생성된 "복사본"은
fork()
약간의 추상화입니다.기록 중 복사체계; 실제로 생성되어야 하는 것은 가상 메모리 맵뿐입니다. 그런 다음 복사본이 즉시 를 호출하는 경우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()와 정확히 일치하지 않습니다.