Unix의 라인 버퍼링에 대한 두 가지 고려 사항은 무엇입니까?

Unix의 라인 버퍼링에 대한 두 가지 고려 사항은 무엇입니까?

APUE 5.4장의 라인 버퍼에 대한 내용:

  1. 라인 버퍼링. 이 경우 표준 I/O 라이브러리는 입력 또는 출력에서 ​​개행 문자가 발견될 때 I/O를 수행합니다. 이를 통해 (표준 I/O fputc 함수를 사용하여) 한 번에 한 문자를 출력할 수 있으며, 실제 I/O는 각 줄 쓰기가 완료될 때만 발생한다는 것을 알 수 있습니다. 라인 버퍼링은 일반적으로 스트림이 표준 입력 및 표준 출력과 같은 터미널을 참조할 때 사용됩니다. 행 버퍼링에는 두 가지 주의 사항이 있습니다. 첫째, 각 줄을 수집하기 위해 표준 I/O 라이브러리에서 사용하는 버퍼 크기가 고정되어 있으므로 개행 문자를 쓰기 전에 해당 버퍼를 채우면 I/O가 발생할 수 있습니다. 둘째, (a) 버퍼링되지 않은 스트림 또는 (b) 라인 버퍼링 스트림(커널에서 데이터를 요청해야 함)에서 표준 I/O 라이브러리를 통해 입력이 요청될 때마다 모든 라인 버퍼링 출력 스트림이 플러시됩니다. (b) 상위 한정자를 사용하는 이유는 요청된 데이터가 이미 버퍼에 있을 수 있기 때문에 커널에서 데이터를 읽을 필요가 없기 때문입니다. 분명히 버퍼링되지 않은 스트림(항목 (a))의 모든 입력에는 커널에서 데이터를 가져와야 합니다.

나는 이 두 가지 경고를 잘 이해하지 못합니다. 누구든지 예를 들어 줄 수 있습니까?

답변1

  1. 기본적으로 예를 들어 프로그램이 터미널에 텍스트를 한 번에 한 문자씩 매우 느리게 쓰고 있고 줄 길이가 520자라면 라이브러리는 프로그램의 터미널에 처음 512자를 쓸 수 있습니다. 행 쓰기(또는 생성)를 마치기 전에.
  2. 이는 프로그램이 한 번에 한 문자(또는 다른 작은 조각)씩 터미널에 텍스트를 쓴 다음 터미널(예: 키보드)에서 읽는 경우 라이브러리가 터미널에 다음 부분을 기록한다는 의미입니다. 프로그램이 지금까지 수행한 결과 출력 라인이 생성되었습니다. 터미널에 작성된 일부 줄은 입력을 요구할 수도 있지만 때로는 그렇지 않을 수도 있기 때문에 이것은 일반적으로 원하는 것입니다.

답변2

두 가지 주의 사항을 살펴보기 전에 명심해야 할 세 가지 사항이 있습니다.

버퍼링되지 않은 스트림 호출 시스템 호출모든 read또는 write. 그리고 라인 버퍼링된 스트림은 각각에 있습니다.철사(버퍼가 개행 문자를 만날 때마다)

또한 stdin및 는 stdout둘 다 표준 I/O 스트림이며 일반적으로 라인 버퍼링됩니다. 이러한 스트림은 프로세스가 시작될 때 자동으로 열립니다.

마지막으로, APUE는 UNIX 학습을 위한 성경이지만 일부 부분은 시대에 뒤떨어져 있습니다. 나는 내 답변이 최신이라고 주장하지 않습니다. 나는 여러분의 질문이 교과서의 맥락 내에서 적절하게 대답될 수 있도록 책의 예를 제시하려고 노력할 것입니다.

첫 번째 경고

귀하의 견적에 명시된 바와 같이 제한 사항이 있습니다.얼마나 오래한 줄이면 됩니다. 한도를 초과하면 래핑과 동일한 효과가 발생합니다.

첫 번째 경고의 예를 시작하기 전에 write시스템 호출이 실제로 대기열에 있다는 점을 기억해야 합니다(APUE 3장 섹션 6, p86).

Jasmine30초마다 문자를 쓰는 프로그램이 있다고 가정해 보겠습니다 .stdout

Jasmine"Hello!\n"(7바이트) 쓰기를 완료하는 데 210초가 걸립니다.

버퍼에는 제한이 있다는 점을 명심하세요. 버퍼가 10바이트로 제한되면 커널은 210초를 기다렸다가 write마지막에(개행 문자를 만난 후) 단일 호출을 발행할 수 있습니다. 이는 예상된 동작입니다.

그러나 버퍼가 4바이트로 제한되면 커널은발급됩니다4바이트 write("Hell") 다음에 한 번 호출하고, 개행 문자("\n") 다음에 또 한 번 호출합니다.

이것이 첫 번째 경고입니다.

사용자는 write이 메시지가 한 번만 나타날 것으로 예상할 수 있습니다(터미널에 "Hello!\n"이 인쇄됨). 예제에서 보았듯이 이는 사실이 아닙니다. 시스템 사용량에 따라 사용자는 210초 이내에 터미널에 인쇄된 두 개의 출력을 볼 수 있습니다. 이는 stream( )이 라인 버퍼임에도 불구하고 stdout해당 라인의 내용이 제한(4바이트 제한)을 초과하기 때문입니다. 표준 I/O 라이브러리는 write마치 개행 문자가 발생한 것처럼 4바이트마다 호출됩니다. 이 예에는 총 7바이트("Hello!\n")가 있으므로 두 번의 쓰기 호출이 발생합니다.

두 번째 경고

두 번째 경고의 핵심은 다음과 같습니다.상호 작용그리고 kernel. kernel상호 작용에 따라 다르게 동작하여 행 버퍼에 또 다른 "예기치 않은" 쓰기가 발생합니다.

이것은 당신이 인용한 두 번째 경고입니다.

kernel모든 행 버퍼 스트림은 두 번 플러시됩니다.

  1. 버퍼링되지 않은 표준 I/O를 통해 입력이 요청되는 경우.
  2. 라인 버퍼링된 표준 I/O를 통해 입력이 요청되는 경우.

다음과 같이 다시 작성할 수 있습니다.

kernel다음 전제 조건에 따라 모든 행 버퍼 스트림이 플러시됩니다.

  1. 입력을 요청합니다 kernel.
  2. 요청은 버퍼링되지 않거나 라인 버퍼링된 표준 I/O 스트림을 통해 이루어집니다.

첫 번째 요구 사항은 "커널에서 데이터를 요청해야 합니다"입니다. 이는 라인 버퍼 표준 I/O에 대한 예외를 허용합니다.

예외는kernel ~하지 않을 것이다요청에 의 데이터가 필요하지 않으면 플러시합니다 kernel. 이는 행 버퍼에 버퍼에 저장된 모든 데이터가 있는 경우일 수 있습니다.

간단한 예로 라인 버퍼 스트림의 버퍼에 "World" 문자가 있다고 가정합니다. 사용자(호출자)는 스트림에서 1바이트 읽기를 요청합니다. 요청된 단일 바이트("W")를 반환하는 스트림에 대해서는 도움이 필요하지 않습니다 kernel. 이 경우 kernel행 버퍼 스트림이 플러시되지 않습니다.

이 예외를 염두에 두고 첫 번째 경고의 예를 계속 진행하겠습니다.

(버퍼 제한을 10바이트로 유지해야 합니다)

"Hello!\n"을 출력한 후 프로그램은 Jasmine"Name?"을 stdout.

"이름?"은 줄 바꿈이 없으며 10바이트 제한보다 작습니다. 표준 I/O 라이브러리가 아직 호출을 발행하지 않았습니다 write.

그런 다음 Jasmine(다른 라인 버퍼 스트림)에서 입력이 요청 되고 stdin다음이 발생합니다.

  1. 스트림의 행 버퍼 stdin가 비어 있으므로 데이터를 요청하고 있습니다 kernel.
  2. kernel모든 열린 라인 버퍼 스트림( "Name?" 포함 stdout)을 플러시합니다.
  3. 결과적으로 "이름?"이 터미널에 출력됩니다.
  4. kernel터미널(키보드)에서 사용자 입력을 기다립니다.

두 번째 경고입니다.

사용자 입장에서는 write아직 정보가 공개되지 않았기 때문에 단말기에서 "이름?"이라는 메시지를 보는 것은 혼란스러울 수 있습니다. 불행하게도 kernel문제는 write요청된 데이터를 얻으려고 시도하기 전에 현재 열려 있는 모든 행 버퍼 스트림이 호출된다는 것입니다. 이는 라인 버퍼 스트리밍 및 상호 작용을 위한 두 가지 전제 조건이 이미 충족되었기 kernel때문입니다 .kernel

관련 정보