커널 모듈의 데이터를 사용자 공간에 저장

커널 모듈의 데이터를 사용자 공간에 저장

나는 한동안 커널 프로그래밍을 조사해 왔으며 일부 맞춤형 하드웨어를 사용하여 이 간단한 데이터 수집 인터페이스를 만들고 싶었습니다. 이식성과 재사용성을 위해 모든 작업은 Raspberry Pi에서 수행했습니다.

프로젝트의 어려운 부분은 고속 ADC(병렬)를 GPIO에 연결하고 ADC의 하드웨어 인터럽트를 사용하여 각 샘플을 가져와서 chardevice를 통해 액세스할 수 있는 버퍼에 저장하는 커널 모듈을 갖는 것입니다. 버퍼.

현재 작동하는 설정은 다음과 같습니다.

  • SPI를 통해 하드웨어를 제어하는 ​​사용자 공간 C 프로그램이 있습니다. 필요한 명령을 보내면 아날로그 데이터 수집이 시작되어 ADC로 전송됩니다.
  • ADC가 변환을 완료할 때마다 GPIO에서 해당 신호를 "낮음"으로 설정하고 커널 모듈(해당 GPIO에 바인딩됨) 내에서 인터럽트가 발생합니다. ISR은 다른 12개의 GPIO(12비트 ADC)에서 값을 수집하여 버퍼에 넣은 다음 /dev/mydevice를 통해 액세스합니다.
  • /dev/mydevice에서 데이터를 읽고 "out_data.dat"(사용자 공간 파일)에 쓰는 동안 끝없는 루프를 실행하는 별도의 사용자 공간 프로그램이 있습니다.
  • 이 조잡한 설정(2개의 사용자 공간 프로그램과 커널 모듈 로드)을 사용하면 초당 130,000개 이상의 샘플을 파일에 쓸 수 있습니다(아무 것도 잃지 않고).

이제 얼마나 빨리 달릴 수 있는지 확인하고 싶습니다. 고려해야 할 두 가지 사항이 있습니다.

  1. 위에서 설명한 설정이 이와 같은 작업을 수행하는 "일반적인" 방법입니까? 커널이 직접 파일 I/O를 권장하지 않는다는 내용을 어디에서나 읽었으므로 그렇게 하지 않습니다. 물론, ISR 동안 "영구적인" 위치에 기록하는 것이 가능해야 합니다. 이것은 일부 하드웨어에서 컴퓨터로 데이터를 전송하기 위해 인터럽트를 사용하려고 할 때 흔히 발생하는 문제인 것 같습니다.

  2. 위의 설정을 변경하지 않고 다른 인터럽트를 비활성화하여 최대한 원활하게 만들 수 있는 방법이 있습니까? 데이터 수집 프로세스 중에는 실제로 아무것도 필요하지 않으며 이를 차단하는 방법만 있으면 됩니다. 데이터 수집은 몇 분 동안만 실행되므로 다른 모든 인터럽트(무선, 모니터 새로 고침 등)는 비활성화할 수 있습니다. 그 후에는 모든 것이 복구되고 더 까다로운 Python 코드를 실행하여 데이터를 분석하고 시각화할 수 있습니다(적어도 이것이 저의 간단한 보기입니다).

답변1

사용자 공간 데이터 수집 프로그램의 무한 루프 문제는 무엇입니까? 시스템 호출을 사용하는 한 poll효율적이어야 합니다.https://stackoverflow.com/questions/30035776/how-to-add-poll-function-to-the-kernel-module-code/44645336#44645336?

영구 데이터 저장

최선의 접근 방식이 무엇인지 잘 모르겠습니다. 설문 조사에서 userland의 파일을 직접 작성하면 어떨까요? 너무 많은 데이터가 도착하면 데이터가 손실될 것이라는 우려가 있으신가요?

하지만 이 경우 제한 요인은 커널과 사용자 공간 간 통신이 아니라 영구 저장 장치의 속도 저하인 것으로 생각되므로 사용자 공간에서 실행해도 아무런 영향을 미치지 않을 것 같습니다. 그럼에도 불구하고 커널 전용 솔루션에는 다음과 같은 강력한 문제가 있습니다.https://stackoverflow.com/questions/1184274/how-to-read-write-files-within-a-linux-kernel-module여기서는 더 나은 해결책을 얻을 수 없을 것 같습니다.

인터럽트 비활성화

특히 병목 현상이 발생할 가능성을 고려할 때 이것이 영향을 미칠 것이라고 확신하십니까? 귀하의 장치가 실제로 많은 인터럽트를 생성하는 경우 해당 인터럽트가 어떤 경우에도 다른 인터럽트를 지배할 것으로 예상됩니다. 다른 하드웨어의 상태를 손상시키는 위험을 감수할 가치가 있습니까? 하드웨어 장치의 사양에 따르면 실제로 현재 보유하고 있는 것보다 더 큰 데이터 대역폭을 제공할 수 있습니까?

나 스스로 어떻게 해야 할지 모르겠지만, 대답을 원한다면 "Linux 커널 모듈에 대한 모든 인터럽트를 비활성화하는 방법"이라는 제목으로 별도의 질문을 하는 것이 가장 좋습니다. LDD2에서 언급된 cli()기능http://www.xml.com/ldd/chapter/book/ch09.html그러나 더 이상 사용되지 않는 것 같습니다.https://notes.shichao.io/lkd/ch7/#no-more-global-cli그런 다음 텍스트는 local_irq_disable및 를 제안합니다 local_irq_save.

나는 또한 당신이 찾은 방법으로 인터럽트를 비활성화하려고 시도하고 좋은 방법이 존재하는지 자세히 알아보기 전에 더 효율적이되는지 확인합니다.

시뮬레이터에서 빠르게:

static int myinit(void)
{
    pr_info("hello init\n");
    unsigned long flags;
    local_irq_save(flags);
    return 0;
}

실패:

returned with disabled interrupts

분명히 v4.16부터 do_one_initcall전용 오류 처리 기능이 있습니다!

그런 다음 나는 순진하게 작업자 스레드에서 이 작업을 수행하려고 했습니다.

static int work_func(void *data)
{
    unsigned long flags;
    local_irq_save(flags);
    return 0;
}

static int myinit(void)
{
    kthread = kthread_create(work_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

하지만 여전히 어떤 효과도 관찰할 수 없으므로 인터럽트는 다음에서 추론할 수 있는 다른 것에 의해 활성화되어야 합니다.

watch -n 1 grep i8042 /proc/interrupts

tty 또는 muse/keyboard 인터럽트를 지속적으로 업데이트합니다.

fops와 같은 다른 진입점이나 raw를 시도하는 경우에도 마찬가지입니다 asm("cli"). 좀 더 교육받은 접근 방식이 필요합니다.

관련 정보