브라이언 커니건(Brian Kernighan)이 설명합니다.이 비디오초기 Bell Labs가 소규모 언어/프로그램에 매력을 느낀 이유는 메모리 제약 때문이었습니다.
큰 기계는 64킬로바이트(M이나 G가 아닌 K)를 가지므로 개별 프로그램은 매우 클 수 없으므로 작은 프로그램을 작성하는 자연스러운 경향이 있으며 기본적으로 입력 출력인 파이프 메커니즘이 있습니다. 리디렉션을 사용하면 한 프로그램을 다른 프로그램에 연결할 수 있습니다.
그러나 프로그램 간에 전송하려면 데이터가 RAM에 저장되어야 한다는 점을 고려하면 이것이 메모리 사용량을 어떻게 제한하는지 알 수 없습니다.
~에서위키피디아:
대부분의 유닉스 계열 시스템에서는파이프라인의 모든 프로세스가 동시에 시작됩니다., 해당 스트림은 적절하게 연결되고 머신에서 실행되는 다른 모든 프로세스와 함께 스케줄러에 의해 관리됩니다. Unix 파이프를 다른 파이프 구현과 구별하는 중요한 측면은 버퍼링의 개념입니다. 예를 들어 송신자는 초당 5000바이트를 생성할 수 있는 반면 수신자는 데이터 손실 없이 초당 100바이트만 허용할 수 있습니다. 대신 송신 프로그램의 출력이 버퍼에 저장됩니다. 수신 프로그램이 데이터를 읽을 준비가 되면 파이프라인의 다음 프로그램이 버퍼에서 데이터를 읽습니다. Linux에서 버퍼 크기는 65536바이트(64KB)입니다. 필요한 경우 bfr이라는 오픈 소스 타사 필터를 사용하여 더 큰 버퍼를 제공할 수 있습니다.
이는 애플릿의 목적을 완전히 무너뜨리기 때문에 나를 더욱 혼란스럽게 합니다(비록 특정 규모에 따라 모듈화될지라도).
첫 번째 문제(메모리 제한이 데이터 크기에 따라 달라지는 문제)를 해결하기 위해 제가 생각할 수 있는 유일한 방법은 대규모 데이터 세트가 당시 전혀 계산되지 않고 실제 문제는 파이프라인이 해결 방법은 프로그램 자체에 필요한 메모리 양입니다. 그러나 Wikipedia 인용문에 굵은 글씨로 표시된 내용을 보면 이조차도 나를 혼란스럽게 합니다. 왜냐하면 프로그램은 한 번에 하나씩 구현되지 않기 때문입니다.
이 모든 것은 임시 파일을 사용하는 경우 의미가 있지만 파이프는 (스왑을 사용하지 않는 한) 디스크에 쓰지 않는다는 것입니다.
예:
sed 'simplesubstitution' file | sort | uniq > file2
sed
파일을 한 줄씩 읽고 뱉어내는 것이 분명합니다 . 그러나 sort
링크된 비디오에서 BK가 언급한 대로 마침표이므로 모든 데이터를 메모리로 읽어야 합니다(아니면 그렇습니까?). 그런 다음 에 전달해야 합니다. uniq
(제 생각에는) 한 번에 한 줄이 됩니다. 프로그램. 하지만 첫 번째 파이프와 두 번째 파이프 사이에는 모든 데이터가 메모리에 있어야 합니다. 그렇죠?
답변1
데이터를 RAM에 저장할 필요는 없습니다. 독자가 거기에 없거나 따라갈 수 없는 경우 파이프는 기록기를 차단합니다. Linux(및 제가 생각하는 대부분의 다른 구현)에는 약간의 버퍼링이 있지만 필요하지는 않습니다. 말한 바와 같이추적 장치그리고제이드 BP(바라보다후자의 대답), Unix의 초기 버전은 디스크에 대한 파이프를 버퍼링했으며 이것이 메모리 사용을 제한하는 데 도움이 되는 방법입니다. 처리 파이프는 작은 프로그램으로 분할될 수 있으며, 각 프로그램은 디스크 버퍼 제한 내에서 일부 데이터를 처리합니다. 작은 프로그램은 메모리를 덜 차지하며 파이프를 사용한다는 것은 처리가 직렬화될 수 있음을 의미합니다. 첫 번째 프로그램이 실행되고 출력 버퍼를 채우고 일시 중지된 다음 두 번째 프로그램이 예약되고 버퍼를 처리하는 등의 작업을 수행합니다. 최신 시스템은 초기 Unix 시스템보다 훨씬 더 크고 여러 파이프라인을 병렬로 실행할 수 있지만 대량의 데이터에 대해서는 여전히 유사한 효과를 볼 수 있습니다(이 기술의 변형은 "빅 데이터" 처리에 사용됩니다).
귀하의 예에서는
sed 'simplesubstitution' file | sort | uniq > file2
sed
데이터는 필요에 따라 읽힌 file
다음 sort
읽을 준비가 되자마자 기록됩니다. sort
준비되지 않은 경우 쓰기가 차단됩니다. 데이터는 메모리에 저장되지만 이는 구체적 sort
이며 sort
모든 문제를 처리할 준비가 되어 있습니다(정렬할 데이터 양이 너무 많으면 임시 파일을 사용함).
다음을 실행하여 차단 동작을 볼 수 있습니다.
strace seq 1000000 -1 1 | (sleep 120; sort -n)
이는 대량의 데이터를 생성하여 읽을 준비가 되지 않은 프로세스로 전송합니다.아무것처음 2분. 많은 write
작업이 진행되는 것을 볼 수 있지만 seq
곧 중지되고 커널에 의해 차단되어 2분이 지나기를 기다립니다(시스템 write
호출 대기).
답변2
그러나 프로그램 간에 전송하려면 데이터가 RAM에 저장되어야 한다는 점을 고려하면 이것이 메모리 사용량을 어떻게 제한하는지 알 수 없습니다.
이것은 당신의 근본적인 실수입니다. 이전 버전의 Unix는 파이프 데이터를 RAM에 보관하지 않았습니다. 디스크에 저장합니다. 파이프에는 다음과 같이 표시되는 광 디스크 장치의 i-노드가 있습니다.배관 설비. 시스템 관리자는 /etc/config
어떤 디스크의 어떤 볼륨이 파이프 장치이고 어떤 볼륨이 파이프 장치인지 지정하기 위해 호출되는 프로그램을 실행합니다.루트 장치,안에덤프 장치.
처리할 데이터의 양은 다음 사실에 의해 제한됩니다.직접 차단디스크의 i-노드는 저장용으로 사용됩니다. 이 메커니즘은 파이프에서 데이터를 읽는 데 사용되는 알고리즘이 일반 파일을 읽는 데 사용되는 알고리즘과 거의 동일하기 때문에 코드를 더 간단하게 만듭니다. 파이프는 검색할 수 없고 버퍼는 순환이라는 사실로 인해 일부 조정이 이루어졌습니다.
이 메커니즘은 1980년대 중후반에 다른 메커니즘으로 대체되었습니다. SCO XENIX는 i-노드를 코어 버퍼로 대체하는 "고성능 파이프라인"을 확보했습니다. 4BSD는 이름 없는 파이프를 소켓 쌍으로 만듭니다. AT&T는 STREAMS 메커니즘을 사용하여 파이프를 다시 구현했습니다.
물론, sort
프로그램은 32KiB 입력 블록(또는 32KiB를 사용할 수 없는 경우 할당할 수 있는 더 작은 양의 메모리)에 대해 제한된 내부 정렬을 수행하고 정렬 결과를 중간 stmX??
파일 에 쓴 /usr/tmp/
다음 외부 병합 정렬을 수행합니다. 최종 결과를 제공하기 위해 중간 파일에 있습니다. 산출.
추가 읽기
- 스티브 D. 페이트(1996). "프로세스 간 통신".UNIX 내부: 실용적인 접근 방식. 애디슨-웨슬리. ISBN 9780201877212.
- 바흐, 모리스 J. (1987). "파일 시스템 시스템 호출". 유닉스 운영체제의 설계. 프렌티스 홀. ISBN 0132017571.
- 스티븐 V. 에어하트(1986). "
config
(1M)". 유닉스 프로그래머 매뉴얼: 3. 시스템 관리 기능. 홀트, 라인하르트, 윈스턴. ISBN 0030093139. 23~28페이지. - 아비짓 메논-센(2020-03-23).Unix 파이프는 어떻게 구현됩니까?. toroid.org.
답변3
당신은 부분적으로 정확하지만우연히.
귀하의 예에서 모든 데이터는 실제로 파이프 "사이"에서 읽어야 하지만 메모리(가상 메모리 포함)에 상주할 필요는 없습니다. 일반적인 구현에서는 sort
임시 파일을 부분적으로 정렬하고 병합하여 RAM에 맞지 않는 데이터 세트를 정렬할 수 있습니다. 그러나 각 요소를 읽기 전에 정렬된 시퀀스를 출력할 수 없다는 것은 주어진 사실입니다. 이것은 분명합니다. 그렇습니다. sort
두 번째 파이프로의 출력은 첫 번째 파이프의 모든 내용을 읽은 후에만 시작될 수 있습니다(그리고 모든 작업을 완료하고 임시 파일을 부분적으로 정렬할 수도 있음). 하지만 그것은아니요모두 RAM에 보관해야 합니다.
그러나 이는 파이프 작동 방식과는 아무런 관련이 없습니다. 파이프는 이름이 지정될 수 있습니다(전통적으로 이름이 지정됨). 이는 파일과 마찬가지로 파일 시스템에 위치가 있음을 의미합니다. 이것이 바로 파이프라인, 즉 파일이었습니다(최적화로 허용되는 물리적 메모리 가용성에 따라 쓰기가 병합되었습니다).
오늘날 파이프는 데이터가 복사되는 작고 유한한 크기의 커널 버퍼입니다.개념적으로발생하다. 커널이 도움이 될 수 있다면 가상 머신 트릭을 사용하여 복사본을 제거할 수 있습니다(예: 파일에서 파이핑하면 일반적으로 다른 프로세스에서 동일한 페이지를 읽을 수 있게 되므로 두 개의 복사본이 아닌 단 한 번의 읽기 작업으로 끝나게 됩니다. 어떤 경우에는 버퍼 캐시에서 이미 사용하고 있는 것보다 더 많은 메모리가 필요할 수도 있습니다.
파이프라인이 작고 크기가 제한된 경우 이를 알 수 없는(아마도 큰) 양의 데이터에 어떻게 적용합니까? 간단합니다. 더 이상 공간이 없으면 공간이 다시 생길 때까지 블록을 씁니다.
기억력이 매우 부족할 때 많은 간단한 프로그램의 원리가 매우 유용했던 때가 있었습니다. 왜냐하면, 한 번에 한 조각씩, 단계별로 작업을 수행할 수 있기 때문입니다. 요즘은 약간의 유연성을 제외하고는 장점이 더 이상 크지 않다고 감히 말씀드립니다.
그러나 파이프라인은 매우 효율적으로 구현되므로(그래야 합니다!) 단점이 없으며 잘 작동하고 사람들이 익숙해지는 것이 확립된 것이므로 패러다임을 변경할 필요가 없습니다.