나는 "장치 드라이버"를 작성했습니다(여기에서 소스 코드 참조:https://bitbucket.org/wothke/websid/src/master/raspi/websid_module/)는 대부분의 경우 잘 작동합니다(참조:https://www.youtube.com/watch?v=bE6nSTT_038) 그러나 때때로 장치가 무작위로 충돌하는 것이 가능한 것 같습니다.
"장치 드라이버"는 여러 GPIO 핀을 통해 연결된 일부 오디오 칩을 제어하는 간단하지만 타이밍이 중요한 재생 루프를 수행하는 kthread를 시작합니다. 이 kthread는 일반 커널 사용에서 대부분 제외되어야 하는 "격리된" CPU 코어(kthread_bind 사용)에서 실행됩니다(아래 커널 구성에 대한 세부 정보 참조). sched_set_fifo를 통해 kthread에 높은 우선순위를 부여합니다. kthread는 서브루틴 호출을 수행하지 않으며 이전에 커널에 할당되지 않은 메모리를 필요로 하지 않습니다. (스레드는 또한 get_cpu, local_irq_save 및 local_bh_disable을 사용하여 타이밍을 방해할 수 있는 모든 것을 일시적으로 비활성화합니다. 그러나 비활성화가 되지 않은 경우에도 충돌이 재현될 수 있으므로 이러한 것이 간헐적인 충돌의 근본 원인으로 보이지는 않습니다. 사용된.)
일반 "Raspberry OS" "Desktop" 커널을 컴파일했지만 특별히 NO_HZ_FULL(예: "Full Dynamic System(No Tick)")을 활성화했습니다. 또한 cmdline.txt를 통해 코어 #3을 구체적으로 격리했습니다: isolcpus=3 rcu_nocbs=3 rcu_nocb_poll=3 nohz_full=3 (이것은 예상대로 대부분의 IRQ를 CPU 코어 #3에서 멀리 유지하는 것 같습니다. 따라서 위의 kthread는 다음과 같아야 합니다. 좋아요 코어에 혼자 #3)
가장 일반적인 용의자는 아마도 앞서 언급한 "재생" kthread와 "사용자 공간"의 데이터 생성자 간의 모든 통신에 사용되는 "공유 커널 메모리" 버퍼일 것입니다. 나는 잠재적인 경쟁 조건을 피하기 위해 내가 생각할 수 있는 모든 예방 조치를 취했지만 아마도 일종의 CPU 캐시 효과가 있거나 내가 간과하고 있는 다른 것이 있을 수 있습니다. "공유 버퍼"에는 이러한 방식으로 설정/사용되는 4개의 페이지 정렬 영역이 포함되어 있습니다.~해야 한다안전한 통신/동기화를 보장합니다.
첫 번째 페이지에는 u32 또는 uint32_t(기본적으로 원자성이어야 함)로 액세스할 수 있는 32비트 플래그만 포함되어 있습니다. kthread는 이 플래그가 0일 때만 업데이트하고 0이 아닌 값으로만 설정합니다. Userland 코드는 이 플래그를 0으로 재설정하고 0이 아닌 값이 있는 경우에만 kthread에 의해 설정된 0이 아닌 값을 수신했음을 승인합니다.
두 번째 페이지에는 1)과 유사한 플래그가 포함되어 있지만 반대 방향입니다. 즉 여기서 kthread는 "userland"에서 0이 아닌 콘텐츠를 수신합니다.
그런 다음 세 번째(+ 후속) 페이지에는 간단한 이중 버퍼링 시나리오를 위한 첫 번째 버퍼가 포함됩니다. 이 버퍼는 "userland" 생산자에 의해서만 작성되고 kthread에서만 읽혀집니다. 2개의 플래그를 통해 구현된 "ping/pong" 프로토콜은 버퍼가 동시에 "절대로" 사용되지 않도록 설계되었습니다. kthread는 버퍼 중 하나가 채워질 수 있다는 신호를 보내 시퀀스를 시작한 다음 "userland" 신호가 이를 반환합니다. 해당 버퍼 채우기를 완료합니다. 즉, kthead는 생산자로부터 신호를 본 후에만 버퍼에서 읽기를 시작합니다(이제 이렇게 하는 것이 안전합니다)("userland" 생산자가 해당 신호를 방출하기 전에 msync(start_page, len, MS_INVALIDATE)를 사용했습니다. ) 공유 메모리 영역의 어느 부분이 업데이트되었는지 보고합니다).
그러면 n번째 (+) 페이지에는 두 번째 버퍼가 포함됩니다((3)에 명시된 모든 내용이 여기에도 적용됩니다).
그런데 위와 같은 문제가 발생하더라도 kthread나 해당 사용자 모드 프로세스를 차단할 수 있습니다. 그런데 왜 이것이 시스템 전체에 충돌을 일으키는지 모르겠습니다.
나에게 가장 논리적인 설명은 "공유 버퍼"가 무작위로 재배치되어(따라서 임의의 메모리 손상이 발생하는 경우), 그러나 다음을 통해 할당된 버퍼에서는 이런 일이 발생해서는 안 된다고 생각합니다.
_raw_buffer = kmalloc(AREA_SIZE + 2*PAGE_SIZE,
GFP_KERNEL & ~__GFP_RECLAIM & ~__GFP_MOVABLE);
또는 코어 #3에서 무언가를 기다리는 것을 특별히 차단하는 일부 커널 기능이 있는 경우(내 kthread가 해당 CPU의 다른 모든 것을 잡아먹기 때문에 이런 일이 발생하지 않을 것입니다...).. 하지만 왜 그런 일이 일어나는지 놀랄 것입니다. 문제는 항상 기계를 충돌시키는 대신 가끔 발생합니다.
어떤 아이디어가 있나요?
답변1
코드의 모든 합리적인 지점에 "메모리 장벽"을 추가해도 상황이 개선되지 않은 후 마침내 작동하는 해결 방법을 찾았습니다. 문제는 공유 메모리와 전혀 관련이 없는 것 같습니다. 대신 스케줄러에 의해 트리거되는 것으로 보이며 장기 실행 kthread에 "schedule()"에 대한 호출을 추가하면 시스템이 정지되는 것을 방지하는 것 같습니다.
불행하게도 이 해결 방법은 나에게 실행 가능한 솔루션이 아니었고, 그 방향을 더 자세히 살펴보기 위해 별도의 스레드를 만들었습니다.Schedule()을 호출하지 않고 장기 실행 kthread를 사용할 수 있는 방법이 있습니까?