연속적인 회전 sync() 호출이 어떻게 높은 IO 대기로 이어질 수 있습니까?

연속적인 회전 sync() 호출이 어떻게 높은 IO 대기로 이어질 수 있습니까?

내가 라이브러리 호출에서 이해하는 한 sync(), 프로세스는 모든 더티 버퍼를 디스크로 플러시할 수 있습니다.

sync() 시스템이 호출하는 서비스 루틴 sys_sync()는 일련의 보조 함수를 호출합니다.

wakeup_bdflush(0);
sync_inodes(0);
sync_supers( );
sync_filesystems(0);
sync_filesystems(1);
sync_inodes(1);

호출을 한 번 실행하면 sync()이후에는 버퍼에 아무 것도 없어야 합니다.

플래그를 stress실행할 때라는 도구를 사용하고 있습니다.-isync()에서 N 작업자 스레드 생성,

stress --i 1

이 명령은 호출을 계속 보내기 때문에 높은 IO 대기 시간을 소비한다고 가정합니다 sync(). 를 사용하여 확인합니다 ltrace.

sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0

연속 동기 호출이 많은 IO를 소비하는 이유를 설명할 수 있는 사람이 있습니까? 첫 번째 동기화 호출 후에는 버퍼가 비어 있어야 한다고 가정합니다.

답변1

sync()는 데이터베이스 커밋과 비슷하지만 디스크용입니다. (실제로 트랜잭션 데이터베이스의 경우 커밋에는 일반적으로 ACID를 확인하는 데 도움이 되는 sync() 호출이 포함됩니다... 이를 방지하기 위해 할 수 있는 모든 조치를 취해야 합니다.) 운영 체제의 일반적인 성능 최적화 채널을 거치는 대신 작업에 지시합니다. 시스템: "지금 하세요!" 회전과 마찬가지로 기계식 디스크 성능은 물리학에 달려 있습니다. 드라이브는 실제로 요청된 작업을 수행해야 하는 위치에 헤드를 배치하는 데 시간이 필요합니다. 지속적으로(또는 가끔씩) 동기화하면 버퍼링 및 예약을 통해 이 작업을 최적화할 수 있는 많은 기회를 우회하게 됩니다. 또한 sync()를 호출할 때도 그렇게 한다는 점에 유의하세요.보류 중인 모든 파일 시스템 쓰기, 개별 파일이나 파일 시스템뿐만 아니라 메타데이터도 포함됩니다.

디스크에 4개의 섹터(가장 안쪽 트랙(w1)에 하나, 가장 바깥쪽 트랙(w2)에 하나, 디스크의 중간 트랙(w3)에 하나)를 써야 하는 프로그램이 있는 간단한 시나리오를 살펴보겠습니다. )), 그리고 마지막으로 가장 안쪽 궤도(w4)로 돌아갑니다...

프로그램이 w1에 쓴 다음 sync()를 호출하면 시스템은 즉시 드라이브에 쓰기를 수행하라고 지시하며, 이로 인해 드라이브는 가장 안쪽 트랙까지 이동하여 섹터를 쓰게 됩니다. 그런 다음 w2가 발생하고 그 다음에 sync()가 발생하므로 이제 드라이브는 섹터를 쓰기 위해 외부 트랙까지 가야 합니다. 다음 w3이 발생하고 드라이브는 디스크 중앙으로 다시 이동하여 섹터를 써야 합니다. 마지막으로 w4가 발생하고 드라이브는 가장 안쪽 트랙으로 돌아가서 섹터를 써야 합니다. 드라이브 헤드가 움직일 때 CPU는 무엇을 하고 있나요? 말 그대로 기다리고 있습니다.

sync() 호출을 모두 수행하지 않은 경우 OS는 모든 쓰기를 버퍼링하고 즉시 프로그램으로 돌아가며("예, 예...결국에는 완료하겠습니다") 이제 w1-을 갖게 됩니다. w4 버퍼. 운영 체제와 드라이브 모두 드라이브가 각 섹터를 독립적으로 또는 순차적으로 쓰도록 강요하지 않고도 의미 있는 방식으로 요청을 예약할 수 있습니다. 따라서 OS가 w4가 들어온 후에 쓰기를 수행하기로 결정한다고 가정하면 4개의 쓰기를 모두 드라이브에 보내고 드라이브는 w1과 w4(즉, 가장 안쪽 트랙)가 먼저 수행되어야 하고 그 다음 w3이 수행되어야 함을 알 수 있습니다. done(중간 궤도)과 마지막으로 w2(가장 바깥쪽 궤도)입니다. 따라서 4개의 개별 조회(밀리초 단위) 대신 3개의 조회만 수행합니다. 그리고 조회당 선형 거리가 짧아서 sync() 시나리오보다 빠릅니다. 이 비효율성을 수백 배 이상으로 곱해 보면 지속적으로 동기화()할 때 왜 기다리는 데 시간을 허비하는지 알 수 있습니다. 플래시 드라이브는 비슷한 스케줄링 효율성(예: 동일한 블록에 여러 번 쓰기 등)을 갖고 있지만 기계식 드라이브만큼 영향을 미치지는 않습니다.

관련 정보