15P by neo with xguru 3달전 | favorite | 댓글 2개
  • Linux 커널 API 문서가 얼마나 불완전한지, 그리고 Rust가 그 문제의 일부를 어떻게 해결하는지에 대해 사람들이 충분히 인식하지 못하고 있다고 생각함
  • 여러개의 서브시스템에 대한 Rust 추상화를 작성했는데, API를 안전하게 사용하는 방법을 완전히 이해하기 위해 거의 모든 경우에 C 소스 코드를 읽어야 했음
  • 함수 시그니처와 관련 문서 주석 또는 명시적 문서만으로는 API의 안전한 사용 방법을 완전히 파악하기 어려움
    • 잠금을 유지해야 하는지
    • 참조 카운트 인자가 참조를 전달하는지 아니면 자체 참조를 가져가는지
    • 콜백 호출 시 잠금이 유지되는지 아니면 직접 획득해야 하는지
    • free 콜백에 특별한 점이 있는지
    • 의도된 잠금 순서가 무엇인지
    • 일부 연산이 경우에 따라 잠금을 사용할 수 있는 특수한 상황이 있는지
    • NULL 인자 허용 여부와 유효한 사용법
    • 오류 상황에서 참조 카운트에 어떤 일이 발생하는지
    • 반환된 참조 카운트 포인터가 이미 증가된 것인지 아니면 전달된 인자의 참조로부터 묵시적 빌림인지
    • 반환 값이 항상 유효한 포인터인지, NULL일 수 있는지, 아니면 ERR_PTR일 수도 있는지
    • 간접 인자를 통해 반환된 포인터가 오류 시 NULL로 정리되는지 그대로 두는지
    • 반환 포인터가 필요하지 않을 때 NULL ** 전달이 유효한지
  • 때로는 요구사항이 합리적이었지만 문서화되지 않았음
    • 때로는 요구사항이 너무 유연하거나 복잡해서 Rust 추상화 작성 시 안전한 사용법으로 좁히기 위해 주관적인 결정을 내려야 했음
    • 때로는 안전성을 확보하기 위해 추상화 내부에 추가적인 잠금을 도입해야 했음
    • 때로는 C 코드를 좀 더 직교적이고 논리적이며 사용하기 쉽게 만들기 위해 약간의 변경을 해야 했음 (예: 잠금이 걸린 상태에서 사용할 잠금 해제 함수 노출)
    • 때로는 잠금이 미묘해서 안전한 Rust 추상화를 작성할 수는 있었지만, 데드락 방지를 위해 사용법과 해제 순서에 주의해야 한다는 큰 문서 주석이 필요했음 (Rust는 본질적으로 데드락을 방지하지 않음)
    • 때로는 C 코드를 더 합리적으로 만들지 않으면 해결 불가능했음 (drm_sched의 경우)
  • 그러나 대부분의 경우 Rust 추상화 작성 시 타협점들은 C 코드 설계의 문제점과 개선 방향을 시사함
    • 일반적인 접근법은 "먼저 C 코드를 최대한 적게 변경하면서 Rust 코드를 작성하여 충돌을 피하고, 그 다음 배운 교훈을 바탕으로 C 코드 변경을 제안하는 것"임 (아직 두 번째 부분까지는 진행하지 못함)
  • 결과적으로 대부분의 경우 Rust API만 보고도 올바른 사용법을 알 수 있음
    • 참조 카운트, NULL 포인터, 결과 확인 누락, 오류 시 참조 해제 등에 대해 걱정할 필요가 없음
    • 올바른 잠금 사용, 참조 획득 누락이나 중복 해제 등에 대해 걱정할 필요가 없음
    • 오류 반환 값의 인코딩 방식에 대해 궁금해할 필요가 없음
    • 이런 것들을 잘못하면 코드가 컴파일되지 않기 때문
    • 물론 여전히 API를 잘못 사용할 수는 있지만, 최악의 경우 오류 반환이나 데드락이 발생할 뿐임 (데드락은 lockdep으로 쉽게 디버깅 가능하고, Arc<> 통합으로 해제/참조 해제 관련 잠금 오류를 잡을 수 있음)
  • 비교적 엄격하게 문서화된 OpenFirmware/DeviceTree API조차도 C에서는 모든 규칙을 따르는 것이 지루하고 오류가 발생하기 쉬움
    • 드라이버의 OF 코드를 보면 참조 누수 가능성이 높음
    • 대부분의 시스템은 OF_DYNAMIC으로 커널을 컴파일하지 않아 참조 카운트가 무시되므로 이는 발견되거나 수정되지 않음
    • 그러나 내가 작성한 OF Rust 추상화는 참조 카운팅을 자동으로 처리하므로 이에 대해 신경 쓰지 않아도 됨
  • C 대비 Rust로 커널 코딩하는 것의 장점
    • C로 커널 코딩 시에는 두 가지 선택지밖에 없음
    • 그냥 해보고 리뷰어가 잡아주기를 바라거나 디버깅으로 고생하기
    • 코드를 감히 사용하기 전에 몇 시간 동안 이해하고 모든 것을 잡아내기를 바라기
    • 이는 리뷰어와 관리자의 업무량도 증가시킴
      • 문서화되지 않은 숨겨진 규칙들을 모두 준수하는지 확인하기 위해 제출물을 검토해야 함
      • 때로는 문제점을 놓치기도 하고, 때로는 문제가 커서 코드를 대대적으로 리팩토링해야 함
  • Rust에서는 이 모든 것이 사라짐. 컴파일되면 안전하고 오동작이나 참조 누수가 없음 (unsafe 코드는 예외이지만, 그것만 검토하면 되고 문서화가 잘 되어 있어야 한다는 규칙이 있음)
    • 물론 여전히 코드 리뷰와 특정 서브시스템 전문가의 도움이 필요함. Rust가 마법처럼 코드를 완벽하게 만들어주지는 않음
    • 그러나 모든 어리석은 저수준 문제와 실수를 제거하므로 고수준 문제에 집중할 수 있음
  • Linux 개발자들에 대한 입장
    • 나는 Linux 개발자들의 불완전한 문서화를 비난하지 않음
    • Linux 커널은 매우 복잡하고 많은 미묘한 문제를 다뤄야 함
    • 대부분의 사용자 공간 API는 안전하게 사용하기 위해 훨씬 간단한 규칙을 가지고 있음
    • 커널은 어려움
    • 경험 많은 커널 개발자조차도 항상 이런 것들을 잘못 함
    • 기술의 문제가 아니라, 인간이 이 모든 복잡한 규칙을 머릿속에 넣고 매번 정확히 수행하는 것은 불가능함
  • 해결책
    • 우리는 도구가 필요함
    • 해결책은 Rust임. 모든 규칙을 한 번 코드와 타입 시스템에 인코딩하면 다시는 걱정할 필요가 없음
    • 코딩 스타일 논쟁의 해결책이 자동 포맷터에 모든 규칙을 인코딩하는 것과 같음
    • 그러면 우리는 모든 저수준 안전성, 소유권, 동기화 문제에 대한 걱정을 그만두고 고수준 드라이버와 서브시스템 설계와 같은 더 중요한 것에 대해 걱정할 수 있음
  • Rust for Linux 프로젝트의 코드 포맷팅
    • Rust for Linux 프로젝트는 실제로 제출물에 대해 rustfmt를 적용함
    • 커널 Rust를 작성할 때 코드 포맷팅이나 코드 리뷰에서의 불만을 걱정할 필요가 없음
    • 그냥 make rustfmt만 하면 됨

GN⁺의 의견

  • 이 글은 Linux 커널 개발에서 API 문서화와 안전성 문제를 잘 지적하고 있음. C언어의 한계와 Rust의 장점을 잘 보여줌
  • 그러나 'Rust가 유일한 해결책'이라는 표현은 다소 과장되어 보임. 정적 분석 도구 등 다른 방법으로도 일부 개선이 가능할 것임
  • Rust가 메모리 안전성 등 많은 문제를 해결해 주지만, 여전히 꼼꼼한 코드 리뷰와 테스트가 필요함. 은탄환은 아님
  • Rust로 전환하는 것이 기존 C 코드와의 호환성, 개발자 학습 곡선 등 여러 어려움이 있을 수 있음. 점진적 도입이 바람직해 보임
  • Linux 커널의 오래된 관행과 문화를 개선하려면 Rust 외에도 문서화, 멘토링, 커뮤니케이션 등 다방면의 노력이 필요할 것으로 보임
  • 전반적으로, 이 글은 Rust가 가진 Linux 커널 개발에서의 잠재력과 장점을 잘 보여주면서도, 과도한 기대나 맹신은 경계하고 있어 균형 잡힌 시각을 보여줌. Rust의 도입이 기술적으로나 문화적으로 어려움이 있겠지만 장기적으로는 커널 코드의 안전성과 유지보수성 개선에 기여할 수 있을 것으로 기대됨.
Hacker News 의견
  • Rust와 Swift 같은 언어는 표현력이 높아 데이터 타입이나 메서드의 스레드 안전성을 컴파일러가 알려줌

    • 코드 리뷰 시 포인터 사용의 안전성을 일일이 확인할 필요가 없으며, 메모리 안전 언어를 사용하면 비즈니스 로직 구현에 집중할 수 있음
  • Rust 라이브러리 중 문서화가 부족한 경우가 많음

    • Rust는 불완전한 API 문서 문제를 해결하지 않으며, API 문서화를 철저히 하는 개발자가 문제를 해결함
  • Rust를 C처럼 사용하려고 하다가 빌림 검사기 문제로 어려움을 겪음

    • 함수 시그니처를 읽고 '&self' 또는 '&mut self'를 확인하는 것이 중요함
    • '&mut self'가 있는 경우, 인스턴스를 스레드 간에 공유하려면 뮤텍스를 사용해야 함
  • Rust API를 보면 대부분의 경우 올바르게 사용하는 방법을 알 수 있음

    • 그러나 일부 Rust API는 함수 시그니처만으로는 타입을 생성하는 방법을 알 수 없어 구글링이 필요함
  • Rust의 구체적인 예시로, 데이터 보호를 위해 잠금을 사용하는 방식이 있음

    • Rust에서는 잠금이 보호된 데이터를 감싸고 있어 잠금을 해제하지 않고 데이터를 접근할 수 없음
  • 다른 언어에서도 API를 중복 구현하면 코드와 문서의 명확성을 높일 수 있음

    • 최근 경험으로, 네트워크 스택에서 잘못된 변경이 검토를 통과해 안정 버전에 포함된 사례가 있음
    • 이러한 문제를 피하기 위해 도구를 사용하는 것이 좋음
  • Python 확장에서 C를 사용할 때 호출 규약을 알아야 하는 문제가 있음

    • Rust와 PyO3를 사용하면 이러한 문제가 사라지고 진입 장벽이 낮아짐
    • C++도 비슷한 기능을 제공하지만, Rust만큼 안전하지 않음
  • 이러한 사람들은 영웅이며 훌륭한 작업을 수행함

  • 완전한 자체 문서화 코드를 구현하는 데 한 걸음 더 가까워짐

Rust... 개인적으로 공부해보긴 했지만, 아직 저희 회사에선 도입하고 있진 않네요. C++ 로 이미 작성한 게 산더미고, 기존 인력들이 rust 를 다시 배워야하는 문제가 있으니... 이미 rust를 프로덕션에 적용중인 회사가 한국에도 있다고 들었는데, 관련한 경험, 같은 게 공유되면 좋을 것 같아요.