Bursty가 SD/USB에 쓰기 때문에 임베디드 Linux에서 시간이 중요한 앱이 정지됩니다.

StackOverflow https://stackoverflow.com/questions/78157

문제

저는 ARM9을 하드웨어 비디오 인코더 칩에 연결하고 비디오를 SD 카드나 USB 스틱에 기록하는 임베디드 Linux 프로젝트를 진행 중입니다.소프트웨어 아키텍처에는 데이터를 버퍼 풀로 읽는 커널 드라이버와 마운트된 이동식 장치의 파일에 데이터를 쓰는 사용자 영역 앱이 포함됩니다.

특정 데이터 속도(약 750kbyte/초) 이상에서는 사용자 영역 비디오 작성 앱이 약 5초마다 약 0.5초 동안 멈추는 것을 보기 시작합니다.이는 커널 드라이버의 버퍼가 부족해지는 원인이 되기에 충분합니다. 버퍼 수를 늘릴 수 있더라도 비디오 데이터는 실시간으로 진행되는 다른 작업과 동기화되어야 합니다(이상적으로는 40ms 이내).이 5초의 "지연 스파이크" 사이에 쓰기는 40ms 이내에 완료됩니다(앱에 관한 한 - OS에 의해 버퍼링된다는 점에 감사드립니다).

나는 이 지연 급증이 Linux가 데이터를 디스크로 플러시하는 방식과 관련이 있다고 생각합니다. pdflush는 5초마다 깨어나도록 설계되었으며 이것이 쓰기 작업을 수행한다는 것을 이해합니다.지연이 끝나자마자 사용자 영역 앱은 (오버플로되지 않은) 버퍼의 백로그를 신속하게 서비스하고 작성할 수 있습니다.

제가 쓰고 있는 장치는 합리적인 궁극적인 처리량을 가지고 있다고 생각합니다.메모리 fs에서 15MB 파일을 복사하고 동기화가 완료될 때까지(그리고 USB 스틱의 표시등이 깜박임을 멈출 때까지) 기다리는 동안 약 2.7MBytes/초의 쓰기 속도를 얻었습니다.

나는 두 가지 종류의 단서를 찾고 있습니다.

  1. 프로세스 우선 순위, 실시간 패치 또는 버스트가 아닌 지속적으로 쓰기 위해 파일 시스템 코드 조정 등 버스트 쓰기로 인해 앱이 정지되는 것을 어떻게 막을 수 있습니까?

  2. 쓰기 백로그 및 카드/스틱 처리량 측면에서 파일 시스템에 무슨 일이 일어나고 있는지 내 앱이 인식하도록 하려면 어떻게 해야 합니까?하드웨어 코덱에서 즉시 비디오 비트 전송률을 변경할 수 있는 기능이 있는데, 이는 프레임을 삭제하거나 최대 허용 비트 전송률에 인위적인 제한을 가하는 것보다 훨씬 낫습니다.

추가 정보:이것은 현재 Montavista 2.6.10 기반 커널을 실행하는 200MHz ARM9입니다.

업데이트:

  • 파일 시스템 SYNC를 마운트하면 처리량이 너무 낮아집니다.
  • 이동식 미디어는 FAT/FAT32 형식이며 미디어를 모든 Windows PC에 연결하여 읽을 수 있도록 설계되어야 합니다.
  • 정기적으로 sync() 또는 fsync()를 호출하면 매초마다 정기적인 지연과 허용할 수 없을 정도로 낮은 처리량이 발생합니다.
  • fopen() 등이 아닌 write() 및 open(O_WRONLY | O_CREAT | O_TRUNC)을 사용하고 있습니다.
  • 언급된 "Linux 실시간 파일 시스템"에 대해 온라인에서 즉시 찾을 수 없습니다.연결?

이것이 의미가 있기를 바랍니다.stackoverflow에 대한 첫 번째 임베디드 Linux 질문이 있습니까?:)

도움이 되었습니까?

해결책

기록에 따르면, 가장 극단적인 경우를 제외하고는 모든 문제를 해결한 것으로 보이는 두 가지 주요 측면이 있는 것으로 나타났습니다.이 시스템은 아직 개발 중이며 아직 철저한 고문 테스트를 거치지는 않았지만 꽤 잘 작동하고 있습니다(나무를 만져보세요).

큰 승리는 사용자 영역 작성기 앱을 다중 스레드로 만드는 것에서 나왔습니다.때때로 차단되는 것은 write()에 대한 호출입니다.다른 프로세스와 스레드는 계속 실행됩니다.장치 드라이버를 서비스하고 실행 중인 다른 앱과 동기화하기 위해 프레임 수 및 기타 데이터를 업데이트하는 스레드가 있는 한 데이터는 마감 기한을 위반하지 않고 몇 초 후에 버퍼링되고 기록될 수 있습니다.먼저 간단한 탁구 이중 버퍼를 시도했지만 충분하지 않았습니다.작은 버퍼는 압도당하고 큰 버퍼는 파일 시스템이 쓰기를 소화하는 동안 더 큰 일시 중지를 유발했습니다.스레드 사이에 대기 중인 10개의 1MB 버퍼 풀이 현재 잘 작동하고 있습니다.

또 다른 측면은 물리적 미디어에 대한 궁극적인 쓰기 처리량을 주시하는 것입니다.이를 위해 저는 Dirty 통계를 주시하고 있습니다./proc/meminfo에 의해 보고되었습니다.더러운 경우 인코더를 조절하기 위한 대략적이고 준비된 코드가 있습니다.특정 임계값 이상으로 올라가면 막연하게 작동하는 것 같습니다.나중에 더 많은 테스트와 조정이 필요합니다.다행히도 나는 백로그가 쌓이고 부드럽게 조절되는 것을 확인하기 위해 몇 초 동안 사용할 수 있는 RAM(128M)이 많습니다.

이 문제를 해결하기 위해 다른 조치를 취해야 할 경우 다시 돌아와 이 답변을 업데이트하도록 노력하겠습니다.다른 답변자에게 감사드립니다.

다른 팁

나는 몇 가지 제안을 버릴 것입니다. 조언은 저렴합니다.

  • 디스크에 쓰기 위해 더 낮은 수준의 API를 사용하고 있는지 확인하고 다음과 같은 사용자 모드 캐싱 기능을 사용하지 마십시오. fopen, fread, fwrite 낮은 수준의 기능을 사용하십시오 open, read, write.
  • 통과하다 O_SYNC 파일을 열 때 플래그를 지정하면 디스크에 기록될 때까지 각 쓰기 작업이 차단되어 쓰기의 폭주 동작이 제거되고 각 쓰기 비용이 느려집니다.
  • 비디오 데이터 덩어리를 가져오기 위해 장치에서 읽기/ioctls를 수행하는 경우 응용 프로그램과 커널 사이에 공유 메모리 영역을 할당하는 것을 고려할 수 있습니다. copy_to_user 커널 공간에서 사용자 공간으로 비디오 데이터 버퍼를 전송할 때 호출됩니다.
  • USB 플래시 장치가 데이터를 쓰기에 지속적인 전송이 가능할 만큼 빠른지 확인해야 할 수도 있습니다.

몇 가지 생각만 하면 도움이 되기를 바랍니다.

여기 쓰기가 많은 작업을 위해 pdflush를 조정하는 방법에 대한 정보입니다.

Linux 실시간 파일 시스템을 찾고 있는 것 같습니다.이에 대해서는 Google 등을 검색해 보세요.

XFS에는 실시간 옵션이 있지만 직접 사용해본 적은 없습니다.

hdparm을 사용하면 캐싱을 완전히 끌 수 있습니다.

파일 시스템 옵션을 조정하면(필요하지 않은 추가 파일 속성을 모두 끄기) 플러시해야 하는 항목이 줄어들어 플러시 속도가 빨라질 수 있습니다.하지만 그게 별로 도움이 될지는 의문이다.

하지만 내 제안은 스틱을 파일 시스템으로 사용하지 않고 대신 원시 장치로 사용하는 것입니다.'dd'를 사용하는 것처럼 데이터를 입력하세요.그런 다음 다른 곳에서 해당 원시 데이터를 읽고 베이킹 후에 기록합니다.

물론 그것이 당신을 위한 선택인지는 모르겠습니다.

디버깅 지원 기능이 있으므로 strace를 사용하여 어떤 작업에 시간이 걸리는지 확인할 수 있습니다.FAT/FAT32에는 놀라운 점이 있을 수 있습니다.

단일 파일에 쓰나요, 아니면 여러 파일에 쓰나요?

대기열에 쓸 준비가 된 비디오 버퍼 풀을 유지 관리하는 읽기 스레드를 만들 수 있습니다.프레임이 수신되면 큐에 추가되고 쓰기 스레드에 신호가 전달됩니다.

공유 데이터

empty_buffer_queue
ready_buffer_queue
video_data_ready_semaphore

읽기 스레드 :

buf=get_buffer()
bufer_to_write = buf_dequeue(empty_buffer_queue)
memcpy(bufer_to_write, buf)
buf_enqueue(bufer_to_write, ready_buffer_queue)
sem_post(video_data_ready_semaphore)

쓰레드

sem_wait(vido_data_ready_semaphore)
bufer_to_write = buf_dequeue(ready_buffer_queue)
write_buffer
buf_enqueue(bufer_to_write, empty_buffer_queue)

커널을 기다리면서 쓰기 스레드가 차단된 경우 이 방법이 작동할 수 있습니다.그러나 커널 공간 내부에 막혀 있다면 2.6.10보다 최신 커널을 찾는 것 외에는 할 수 있는 일이 별로 없습니다.

귀하의 특정 상황에 대해 더 자세히 알지 못하여 다음과 같은 추측만 드릴 수 있습니다.

커널이 저장 장치에 데이터를 더 자주 플러시하도록 하려면 fsync()/sync()를 사용해 보십시오.커널이 모든 쓰기를 버퍼링한 다음 버스를 묶거나 실제 쓰기를 수행하는 동안 시스템을 정지시키는 것처럼 들립니다.fsync()를 주의 깊게 호출하면 시스템 버스를 통한 쓰기 일정을 보다 세밀하게 예약할 수 있습니다.

인코딩/캡처(비디오 캡처에 대해 언급하지 않았으므로 여기서는 가정을 하고 있습니다. 더 많은 정보를 추가하고 싶을 수도 있음) 작업이 자체 스레드에서 실행되고 사용자 영역에서 출력을 버퍼링합니다. 그러면 두 번째 스레드가 장치에 대한 쓰기를 처리할 수 있습니다.이렇게 하면 인코더가 항상 차단 없이 쓰기를 완료할 수 있도록 평활화 버퍼가 제공됩니다.

의심스럽게 들리는 한 가지는 이 문제가 특정 데이터 속도에서만 발생한다는 것입니다. 이것이 실제로 버퍼링 문제라면 낮은 데이터 속도에서는 문제가 덜 자주 발생할 것으로 예상하지만 여전히 이 문제가 나타날 것으로 예상합니다. 문제.

어쨌든 더 많은 정보가 유용할 수 있습니다.시스템 아키텍처는 무엇입니까?(매우 일반적인 용어로.)

귀하가 제공한 추가 정보를 고려할 때 작은 쓰기 및 빈번한 플러시로 인해 장치 처리량이 다소 부족한 것 같습니다.더 큰 쓰기 작업에 대해 충분한 처리량을 얻을 수 있다고 확신하는 경우(그렇다는 것은 확실하지 않지만 파일 시스템이 모든 쓰기 후에 FAT를 업데이트하는 등 어리석은 작업을 수행할 수 있음) 인코딩 스레드 파이핑 데이터를 사용합니다. 지연을 방지하기 위해 쓰기 스레드에서 충분한 버퍼링을 갖춘 쓰기 스레드로 이동합니다.나는 과거에 이런 종류의 체계를 구현하기 위해 공유 메모리 링 버퍼를 사용했지만, 버퍼가 가득 차지 않는 한 작성자가 지연 없이 I/O 프로세스에 쓸 수 있도록 허용하는 모든 IPC 메커니즘이 그 트릭을 수행해야 합니다.

유용한 Linux 기능이자 sync 또는 fsync의 대안은 sync_file_range입니다.이를 통해 커널 내부 버퍼 시스템이 작동할 때까지 기다리지 않고 데이터 쓰기를 예약할 수 있습니다.

긴 일시 중지를 방지하려면 IO 대기열을 확인하세요(예:/sys/block/hda/queue/nr_requests)는 충분히 큽니다.해당 큐는 데이터가 메모리에서 플러시되고 디스크에 도착하는 사이에 이동하는 곳입니다.

sync_file_range는 이식 가능하지 않으며 커널 2.6.17 이상에서만 사용할 수 있습니다.

호스트가 명령을 보낸 후 MMC와 SD 카드는 "0~8바이트 내에서 응답해야 한다"고 들었습니다.

그러나 사양에 따르면 이러한 카드는 작업이 완료될 때까지 "사용 중"으로 응답할 수 있으며 카드가 사용 중이라고 주장할 수 있는 기간에는 제한이 없는 것 같습니다(제한이 있는 경우 알려주십시오).

M25P80과 같은 일부 저가형 플래시 칩의 "최대 단일 섹터 삭제 시간"은 3초로 보장되지만 일반적으로 "단지" 0.6초만 필요합니다.

그 0.6초는 "아마도 0.5초 동안 멈춘 것"과 의심스러울 정도로 유사하게 들립니다.

나는 저렴하고 느린 플래시 칩과 비싸고 빠른 플래시 칩 사이의 균형이 USB 플래시 드라이브 결과의 광범위한 변화와 관련이 있다고 생각합니다.

플래시 섹터를 지운 다음 다시 프로그래밍할 때마다 지난 번보다 시간이 조금 더 걸린다는 소문을 들었습니다.

따라서 시간이 중요한 애플리케이션이 있는 경우 (a) SD 카드와 USB 스틱을 테스트하여 최소 대기 시간, 대역폭 등을 충족하는지 확인해야 할 수 있습니다.(b) 이러한 메모리 장치를 주기적으로 재테스트하거나 사전에 교체합니다.

우선, 파일을 플러시하도록 명시적으로 지시해 보셨나요?또한 나는 이를 수행하는 데 사용할 수 있는 ioctl이 있을 것이라고 생각하지만 솔직히 C/POSIX 파일 프로그래밍을 많이 해본 적이 없습니다.

당신이 Linux 커널을 사용하고 있다는 것을 알면 커널을 당신의 필요에 더 잘 맞는 것으로 조정하고 재구축할 수 있을 것입니다.훨씬 더 자주 발생하지만 영구 저장소에 대한 플러시도 더 적습니다.


내 매뉴얼 페이지를 빠르게 살펴보면 다음과 같은 내용을 찾을 수 있습니다.

SYNC(2)                    Linux Programmer’s Manual                   SYNC(2)

NAME
       sync - commit buffer cache to disk

SYNOPSIS
       #include <unistd.h>

       void sync(void);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       sync(): _BSD_SOURCE || _XOPEN_SOURCE >= 500

DESCRIPTION
       sync() first commits inodes to buffers, and then buffers to disk.

ERRORS
       This function is always successful.

자신만의 플러시() 작업을 수행하는 것이 나에게 딱 맞는 것 같습니다. 일반 버퍼 레이어의 변덕스러운 상황에 맡기지 않고 제어할 수 있기를 원합니다.

이는 명백할 수 있지만 write()를 너무 자주 호출하지 않는지 확인하십시오. 모든 write()에 기록할 데이터가 충분하여 syscall 오버헤드를 가치 있게 만들 수 있는지 확인하십시오.또한 다른 방향에서는 너무 자주 호출하지 마세요. 그렇지 않으면 문제가 발생할 만큼 오랫동안 차단됩니다.

재구현이 더 어려운 트랙에서 비동기 I/O로 전환해 보셨나요?aio를 사용하면 쓰기를 실행하여 한 버퍼 세트에 전달할 수 있으며, 동시에 비디오 데이터를 다른 세트로 빨아들이고, 쓰기가 완료되면 버퍼 세트를 전환할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top