Search
Duplicate
😵

아니 그래서 printf 버퍼 관리가 뭔데…!!

간단소개
Don’t implement the buffer management of the original printf()!
팔만코딩경 컨트리뷰터 (Library DB (속성)에 관계됨)에 관계됨
ContributorNotionAccount
주제 / 분류
C
42cursus
운영체제
태그
I/O
Scrap
8 more properties
때는 바야흐로 2022년 7월,
libft를 끝내고 호기롭게 ft_printf를 다음 프로젝트로 정한 7기 카뎃이 있었다.
평소와 다름없이 서브젝트를 읽어나가는데….
Don’t implement the buffer management of the original printf()!
그는 위와 같은 문장을 마주하게 된다!!!
이때까지만 해도 “버퍼”란 실재하는 메모리 공간이라기 보단 프로그램 내에서(알고리즘 내에서) 임시로 사용하는 데이터를 저장하는 공간인 줄 알았습니다.
사실은 그냥 제 OS 지식이 부족했던 거죠.
printf는 buffer라고 불리는 임시 공간(실제 하드웨어 메모리)에 결과물을 보냅니다. 그리고 일정 조건이 충족되면, 그 결과물을 스크린에 출력합니다.
버퍼에 있는 데이터가 스크린에 출력되는 표준 C 조건은 매우 명확합니다.
1.
버퍼가 가득 차거나 (stdio에 정의 된 BUFSIZE는 디폴트로 256바이트)
2.
개행이 들어오거나
3.
임박한 input처리가 있을 경우
가 이에 해당하죠.
위 창이 코드이고 아래 창이 실행 결과입니다.
실제론 실행결과의 첫 줄과 아래 줄이 출력되기까지 usleep(2000000)만큼의 기다림이 있었던 거죠.
printf가 개행(’\n’)을 만나기 전까지의 데이터가 buffering되었다가, sleep 코드를 실행하고 개행을 만난 뒤 버퍼를 flush한 것입니다.

1. ‘버퍼’란 ?

컴퓨터 사이언스에서 버퍼란 단어는 다양한 용도로 사용되는데, 일반적으로 일정 크기의 데이터가 가공되거나 복사되기 전에 임시로 저장되는 공간입니다.
흔히 버퍼는 아래의 세 범주로 분리됩니다.
1.
Hardware buffers: These are buffers where data is stored before being moved to a HW device. Or buffers where data is stored while being received from the HW device until it is processed by the application. This is needed because the I/O operation usually has memory and timing requirements, and these are fulfilled by the buffer. Think of DMA devices that read/write directly to memory, if the memory is not set up properly the system may crash. Or sound devices that must have sub-microsecond precision or it will work poorly.
2.
Cache buffers: These are buffers where data is grouped before writing into/read from a file/device so that the performance is generally improved.
3.
Helper buffers: You move data into/from such a buffer, because it is easier for your algorithm.
저는 오직 3번 버퍼만을 알고 있었습죠,,

2. 왜 버퍼를 사용하는데?

출력할 때 사용되는 버퍼는 최적화의 기능을 합니다.
write, read와 같은 시스템콜은 값이 비싼 동작이므로 모든 바이트 하나하나에 시스템 콜을 호출하는 것보다,
모아두었다가 한 번에 출력하는 것을 선택한 것입니다.

3. stdio에서의 버퍼링

버퍼링에는 다음과 같은 종류가 있고 스트림의 종류와 상황에 따라 사용되는 버퍼링 모드가 달라 집니다.
Fully Buffered: 버퍼에 데이터가 꽉 찼을 때 입/출력을 수행 Line Buffered: 버퍼에 개행문자가 들어왔을 때 입/출력을 수행 Unbuffered: 버퍼에 데이터를 저장하지 않고 곧바로 입/출력을 수행
Plain Text
stdio의 라이브러리 함수들은 기본적으로 line buffering을 수행합니다.
stdio functions store the written data in a buffer before really writing it, to avoid calling too much write() syscall which is pretty heavy in terms of execution time. This buffer is emptied when:
The buffer size limit is reached (typically 1024 bytes);
The filestream is attached to a terminal (stdout, stderr) and a newline is reached (so printing to the screen line by line);
The user calls the function fflush() that force-writes the buffer to the file;
The filestream is closed (either at the end of the program or by calling fclose().
사실 우리는 setbuf()등 함수를 통해 버퍼링 여부를 지정하거나 flush 계열의 함수를 통해 버퍼에 있는 데이터가 flush 되는 타이밍도 조절할 수 있습니다.
하지만 이는 이번 과제에서 요구하는 바가 아닙니다. 이러한 동작을 구현하는 것은 (fd나 버퍼, fd 상태를 유지하는 과정에서) 전역변수를 필요로 할 수도 있습니다. 만약 버퍼를 사용하지 않아 성능 저하가 우려된다면, 우리는 하나의 배열을 선언해둔 뒤 그곳에 결과물을 저장하고 write함수를 한번만 호출함으로써 효율적인 코드를 짤 수 있습니다.

4. 결론

신경쓰지 말고 원하는 방식으로 구현하자, 어차피 버퍼 관리를 할 수 있는 함수도 허용함수가 아니거든요..!!
이상 문장 하나에 꽂혀 과몰입한 jiwahn이었습니다.