전공/시스템 프로그래밍(운영체제)

4. 시스템 콜: buffer 정리

문정훈 2021. 10. 16. 15:06

0. buffer 기본 개념

버퍼라는 것의 역할은 프로세스1 과 프로세스2 사이 속도의 차이를 완화해주는 것이다.

 

우리가 키보드를 통해 내용을 치는데 모니터에 안나오고 갑자기 모니터에 쭉 나오는 현상이 발생한다.

컴퓨터를 관리하는 것이 OS인데 OSCPU 자원을 할당해서 프로세스를 구동시키는 역할을 한다.

우리 컴퓨터를 보면 엄청나게 많은 프로세스가 실행되고 있고 CPU1개이다.(코어 1개라고 가정) 운영체제가 여러 프로세스를 동시에 실행하는 것처럼 보이지만 실제로는 프로세스를 한번에 하나씩 조금씩 실행해서 차례로는 CPU에서 하나하나 실행된 결과이다.

 

키보드로부터 입력하는 데이터를 화면에 출력하는 프로세스 A 가 있을 것이다.

사용자의 키보드 타이핑은 A프로그램이 스케줄링되는 떄에 맞춰 타이핑하지 않고 막 입력할 것이다. 따라서 스케줄링되지 않을때도 사용자가 입력하는 값을 어딘가에 저장해야한다.

그것이 컴퓨터 메인 메모리 안에 있는 버퍼라는 저장 공간이다.

 

버퍼링이란 것은 영상을 본다할 때 영상의 데이터는 버퍼에 저장하는데 프로세스가 버퍼를 가져와 화면에 띄워줘야하는데 버퍼링은 이 버퍼에 영화의 내용이 다 차지 않았으면 영화를 보여주는 프로세스는 버퍼를 비우지 않기 때문에 버퍼가 어느 정도 찰때까지 지연되는 현상이 버퍼링이다.

네트워크는 CPU보다 훨씬 느리기 때문에 발생하는 문제이다.

대부분의 프로세스는 자신과 연결된 버퍼가 하나씩 대부분 있다. (없는 것은 거의 없다)

 

 

1. open System call 관점에서 버퍼 정리

open으로 파일을 열게되면 파일에 접근(읽고 ,쓰는) 작업을 할 수 있게 파일 디스크립터를 반환받는다. 

여기까지만 일단은 우린 알고있다. 그럼 파일 디스크립터를 사용해 실제 하드디스크(HDD)에 접근해 파일을 가리키는 것일까? => 아니다. 

 

open을 하게 되면 HDD에 있는 파일을 메인 메모리(RAM)에 버퍼를 생성 시키고 그 버퍼 안에 파일의 내용을 load한다. 그리고 open 된 이 후 read, write 작업은 이 RAM 안의 버퍼에 올라온 파일의 내용을 읽고 또는 수정하는 작업을 하는 것이다 .

 

 

2.  write System call 관점에서 버퍼 정리

write 시스템 콜은 RAM의 버퍼에 기록을 수행하는 시스템 콜이다. 

write라는 연산은 쓰기를 실제 파일 디스크립터가 가리키는 HDD에 있는 파일에 바로 쓰지 않는다.!!

이렇게 write로 버퍼에 있는 내용을 수정하고 나면 당연히 버퍼의 내용과 HDD의 파일의 내용은 서로 일치하지 않을 것이다. 이러한 buffer를 dirty buffer라고 한다.  이제 아래에서 dirty buffer의 내용을 HDD에 쓰는 과정에서의 용어와 그 과정을 설명한다. 

 

 

 

3. Delayed Write의 개념

1) synchronized IO의 개념

open이라는 것은 HDD에 파일을 램의 버퍼로 파일의 내용을 올리는 것을 의미하고 write는 이 버퍼에 있는 메모의 내용을 수정하는 것이다.

이때 램에 올라와 있는 파일의 내용과 HDD의 내용이 달라지는 Dirty 현상이 발생하는데 Dirty buffer를 맞추는 synchronized IO 작업을 해야한다. synchronized IO 과정은 OS가 담당하고 OS도 하나의 프로세스 이기 때문에 OS가 스케줄링 될 때 OS가 램에 있는 파일 내용을 HDD의 파일로 쓰게 되는 것이다.

OS의 스케줄링은 예측할 수 없기 때문에 언제 synchronized IO가 수행될지 개발자는 알 수 없다. 

 

정리=>

synchronized IO 라는 것은 램 버퍼에 있는 파일의 내용과 HDD의 파일의 내용을 맞추는 것을 의미한다.

 

2) OS 스케줄링으로 synchronized IO 작업 수행하는 내용

HDD에 데이터를 기록하기 위해선 IO 작업을 수행하는데 이 작업은 시간이 길다.

따라서 OSHDD에 무엇인가 쓸 일이있으면 한 번에 쓸려고 한다.

각각의 프로세스 마다 각각의 램의 버퍼를 가진다했다. 그 버퍼들에 저장된 값들 중 HDD에 기록해야하는 버퍼(Dirty한 내용 ) 가 있으면 OS는 이 버퍼들을 한꺼번에 보낼려고 한다.

여러 프로세스가 가진 버퍼를 살펴보고 Dirty한 내용이 있다면 OS가 한번에 HDD로 기록하는데 이것을 delayed write라고 한다.

 

3) 이러한 방식의 문제점

문제점1) OS가 synchronized IO 작업을 수행할 때..

프로세스1이 작업한 내용A가 있고 프로세스2가 작업한 내용B가 있을 때 두 작업다 synchronized IO 되야한다면,

A의 내용이 먼저 기록되고 B가 기록되길 원하는데 램의 버퍼에 A작업과 B작업이 있는데 OS는 이 순서를 모른다. 따라서 B가 먼저 기록될 수 있다.

 

문제점2) fit의 개념

os가 OS가 synchronized IO 작업을 수행할 때 fit이라는 개념으로 동작하기 때문에 즉 fit이란 개념 때문에 우리는 A->B->C 순서대로 synchronized IO를 하고싶은데 순서가 뒤바뀌어 실행된다. 

fit의 개념을 설명하면, 

위 RAM 버퍼에 push된 순서가 노랑(A) -> 초록(B) -> 파랑(C)인데 그럼 OS가 수행하는  
synchronized IO작업 역시 A->B->C 순서로 실행될거 같지만 그렇지 않다. 

위 상황에서 만약에 A는 HDD의 첫 번째 공간에 맞으니 push 되고, B는 두 번째 큰 공간에 들어가지니 push 되면 C는 자동으로 작은 3번째 공간에 할당되야하는데 크기가 맞지 않아 할당되지 못한다. 따라서 이렇게 수행되지 않고

RAM에서 가장 큰 버퍼부터 차례로 들어간다. 따라서 저장 램에 push순서는 A ->B ->C 이지만 HDD에 기록되는 순서는 C ->A ->B가 된다

(참고로 위 설명은 무엇을 설명하는 것이냐? 실제 저렇게 동작하지 않는다. 단지 OS의 동작 원리인 fit의 개념을 이해하고자 위 설명을 든 것이다. )

 

 

※ OS의 추가 지식

위 그림에서와 같이 HDD에 왜 크기가 다른 공간이 생기냐? 

하드디스크의 파일을 휴지통으로 지우면 하드디스크의 빈공간이 생기고 이 빈 공간은 일종의 깃발을 꽂게 되고 아무나 와서 써도되는 공간이 됨을 알린다.
만약 저 깃발을 뽑으면 원래 무슨 데이터가 있었는지 읽을 수 있다.

(참고로 깃발을 뽑는 것을 하드디스크에서 사용할 수 있는 도구 중 “Forensics Tool”이 있는데 이것을 이용해 깃발을 뽑을 수 있음

 

4. Synchronized IO System Call : int fsync(int fd);

1) fsync(int fd) 시스템 콜

#include <unistd.h>

int fsync(int fd);

사용자가 원할 때 OS에서 HDD로 쓰고 싶다!!! 이때 쓰는 함수가 fsync() 라는 System call이다. System call은 데이터가 디스크까지 쓰도록 강제하는 함수이다.

필요한 것이 매개변수로 fd만 전달하면 된다.

이 메소드는 OS에게 매개변수로 주어진 디스크립터가 가리키는 곳에 있는 버퍼에 있는 내용들을 HDD로 기록해라 라는 함수이다.

 

근데 기록을 하는 것도 절차가 있다.

wrire라는 것은 파일의 내용을 작성하는 것 뿐만 아니라 파일 디스크립터가 가지고 있는 파일에 대한 정보(권한 등..) 역시 HDD로 업데이트가 되야한다.

fsync 시스템 콜은 System call파일 내용도 기록하고 메타 데이터 역시 HDD 파일로 기록하는 것이다.

 

fsync 시스템 콜은 HDD에서 기록이 돼서 HDD가 다 됐어!” 알리기 전까지 fsync 함수에서 다음 명령으로 넘기지 않는다. 얼마나 걸리는지는 말할 수 없다. 느리다.. 이때 나오는 개념이 HDD의 캐쉬이다 잠깐 아래 sub 주제 내용을 보고 다시 본론으로 가자.

 

<서브 주제: HDD에도 캐쉬가 존재한다.>

그리고 HDD 에도 캐쉬가 존재한다. 캐쉬란 것은?

검색 키워드를 통해 검색함면 서버에 DB에 있는 여러 가지 내용을 조합해 웹페이지를 만들어준다. 하지만 매번 검색할때마다 DB가 내용을 조회하고 뿌리기엔 오래걸린다. 따라서

사람들이 자주 검색하는 키워드에 해당하는 조회 내용은 어딘가에 저장하고 검색할때마다 그 내용을 뿌려주는 것이다. “어딘가를 캐시라고 한다.

다시 본론으로,

우리가 짠 소스코드를 보면 소스 코드가 전체적으로 골고루 실행되기 보다 어느 특정 구간에서 자주 실행되는 패턴이 많다. 이 자주 실행되는 구간을 지역성이라고한다.

우리 프로그램의 일부가 HDD에서 RAM으로 계속 이동한다. 램에서 HDD로 데이터를 요청하는데 이러한 지역성 떄문에 HDD의 특정 구간에서 반복적으로 램에서 호출한다면 HDDHDD내부의 캐시에 이 지역성 구간의 내용을 저장하고 RAM에게 빠르게 전달한다.

 

<정리>

synchronized IO 작업을 수행할 때 RAM의 dirty buffer를 HDD로 OS가 스케줄링 될 때 fit이란 개념 때문에 dirty Buffer가 생성된 순사가 아닌 다른 순서로 HDD에 저장한다고 하였다. 이때 개발자가 내가 원하는 dirty buffer가 언제  synchronized IO될지 모르기 때문에 개발자가 fsync(int fd) 시스템 콜을 사용해 바로 해당 dirty buffer를 synchronized IO 작업을 할 수 있다고 하였다. 

이때 실제로 HDD에 쓰는 것이 아니라 HDD의 캐시에 쓰는 것이다. 

또한 HDD의 캐시에서 HDD로 언제 기록되는지는 아무도 모른다보통은 캐시에 있다가 아주 짧은 시간 뒤에 기록된다고 한다.

 

 

5.Synchronized IO System Call : fdatasync(int fd)

1) fdatasync(int fd) 시스템 콜

#include <unistd.h>

int fdatasync(int fd);

이 메소드는 매개변수 fd가 가리키고 있는 램의 버퍼의 내용의 실제 데이터와 파일 사이즈만 우선 HDD(HDD의 캐시)로 기록하라는 System call이다.

만약 write로 버퍼에쓰고 바로 다시 HDD에서 이 수정된 내용을 읽으려할 때

만약 fdatasync하지 않는다면 만약 OS가 아직 버퍼에서 HDD로 동기화 하지 않은 상황이라면 읽기 작업은 수정된 내용을 읽을 수 없다. 따라서 빠르게 파일의 내용과 함께 파일의 크기(메타 데이터)를 저장하기위해 사용하는 System call이다.

 

 

 

 

 

'전공 > 시스템 프로그래밍(운영체제)' 카테고리의 다른 글

6. Blocking, non-Blocking  (0) 2021.10.17
5. 추가적인 System Call  (0) 2021.10.17
3. System Call: read, write  (0) 2021.10.16
2. System Call: open  (0) 2021.10.12
1. 시스템 구조와 시스템 라이브러리  (0) 2021.10.12