본문 바로가기
Kernel/Documentation

[Linux Kernel] 리눅스 커널 개발 가이드: How the development process works

by hyeyoo 2021. 8. 26.
※ 이 블로그의 글은 글쓴이가 공부하면서 정리하여 쓴 글입니다.
※ 최대한 내용을 검토하면서 글을 쓰지만 틀린 내용이 있을 수 있습니다.
※ 만약 틀린 부분이 있다면 댓글로 알려주세요.

2. How the development process works — The Linux Kernel  documentation

Linux kernel development in the early 1990’s was a pretty loose affair, with relatively small numbers of users and developers involved. With a user base in the millions and with some 2,000 developers involved over the course of one year, the kernel has s

www.kernel.org

1990년대 초반의 리눅스 커널 개발은 꽤 느슨했고, 상대적으로 적은 수의 사용자와 개발자만이 참여했다. 일년 동안 수백만의 사용자와 2000명의 개발자가 참여하게 되면서, 그때부터 리눅스는 개발을 순조롭게 하기 위해 개발 방식이 발전할 수 밖에 없었다. 따라서 이런 개발 프로세스를 이해하는 것이 개발 참여의 필수 요소이다.

2.1 The big picture

커널 개발자들은 느슨하게 2~3개월 마다 주요 커널 릴리즈를 만든다. 최근에는 아래처럼 진행되었다:

5.0 - 2019년 3월 3일
5.1 - 2019년 5월 5일
5.2 - 2019년 7월 7일
5.3 - 2019년 9월 15일
5.4 - 2019년 10월 24일
5.5 - 2020년 1월 6일

각각의 5.x 릴리즈는 모두 새로운 기능을 구현하고, 내부 API를 수정하는 등의 변화가 있었다. 릴리즈마다 보통 만여개의 패치로, 수십만의 코드를 수정한다. 5.x는 리눅스 커널 개발의 최전선에 있다. 커널은 rolling release 방식으로 개발된다.

매 개발 사이클 초기에는 “머지 윈도우 (merge window)”가 열린다. 이 기간에는 충분히 안정된 코드가 mainline으로 머지된다. 해당 개발 사이클의 번경 사항들이 이때 머지된다. (보통 하루에 1000여개의 패치가 머지된다.)

(참고로 이때 머지되는 패치들은 갑자기 생겨난게 아니라, 이후 설명하겠지만 충분히 미리 테스트하고 수집한 패치이다.)

머지 윈도우는 약 2주간 진행된다. 머지 윈도우가 끝날 때는 Linus Torvalds가 머지 윈도우가 끝났음을 알리고, “rc” 커널을 릴리즈한다. 예를 들어 5.6 커널이라면, 머지 윈도우가 끝난 후 5.6-rc1이 릴리즈된다. -rc1은 새로운 기능을 머지하는 시간은 끝났으며, 개발 사이클의 남은 기간동안에는 머지 윈도우에서 머지한 기능들을 안정화하는 것을 의미한다.

그 후 6~10주 동안은 (새로운 기능 추가가 아닌) 버그 수정 관련 패치들만 mainline으로 보내야한다. 종종 이 기간에도 새로운 기능을 추가할 때가 있지만 거의 드물다. 머지 윈도우 이후에 새로운 기능을 추가하려는 개발자들은 보통 환영받지 못한다. 일반적으로,머지 윈도우에 추가해야 할 기능을 놓쳤다면 가장 좋은 방법은 다음 개발 사이클을 기다리는 것이다. (종종 예외적으로 기존에 지원되지 않았던 하드웨어를 지원하는 경우도 있긴 하다. 기존에 존재하던 코드를 건드리지 않는다면 regression이 일어날 가능성이 없으므로 언제 머지해도 큰 문제가 없다.)

버그 수정 패치들이 메인라인으로 머지되면서, 패치의 수는 시간이 지나면서 적어진다. Linus는 매주마다 새로운 -rc 커널을 발표한다. 보통 -rc6에서 -rc9 사이에 커널이 충분히 안정화되면 개발 사이클이 끝나고, 다시 머지 윈도우부터 반복된다.

예를 들어, 5.4 개발 사이클은 아래처럼 흘렀다 (2019년):
9월 15일: 5.3 안정 릴리즈
9월 30일: 5.4-rc1, 머지 윈도우 끝
10월 6일: 5.4-rc2
10월 13일: 5.4-rc3
10월 20일: 5.4-rc4
10월 27일: 5.4-rc5
11월 3일: 5.4-rc6
11월 10일: 5.4-rc7
11월 17일: 5.4-rc8
11월 24일: 5.4 안정 릴리즈

언제 개발 사이클을 마무리할지 어떻게 판단할까? 가장 중요한 지표는 이전 릴리즈에서의 regression이다. 모든 버그는 환영받지 못하지만, 과거에는 작동했지만 현재는 시스템을 망가뜨리는 버그는 더 심각하게 여겨진다. 이 때문에 regression의 원인이 패치는 좋지 않은 평판을 갖게 되고 revert될 가능성이 높다.

개발자들의 목표는 모든 알려진 regression을 안정 릴리즈 전에 모두 고치는 것이다. 하지만 현실에선 달성하기 어려운 목표이다. (이 정도 규모의 프로젝트에선 변수가 너무 많다.) 결국 릴리즈를 늦추는 것이 더 안 좋은 선택인 시점이 온다: 머지 윈도우를 기다리는 패치가 많아지면서, 다음 개발 사이클에 더 많은 regression을 발생시킨다. 그래서 대부분의 5.x 커널은 소수의 알려진 regression은 그대로 안고 간다.

안정 릴리즈가 만들어지면, 그 이후의 관리는 Greg Kroah-Hartman의 “stable team”이 맡는다. Stable team은 종종 5.x.y 버전의 형태로 안정 릴리즈를 만든다. 안정 릴리즈에 포함되는 패치는 반드시 (1) 중요한 버그를 고치고, (2) 이미 mainline에 머지된 상태여야 한다. 안정 커널은 보통 하나의 개발 사이클보다 짧은 기간동안 업데이트가 된다. 따라서, 예를 들어 5.2 커널은 다음과 같이 개발이 되었다. (2019년)

7월 7일 - 5.2 안정 릴리즈
7월 14일 - 5.2.1
7월 21일 - 5.2.2
7월 26일 - 5.2.3
7월 28일 - 5.2.4
7월 31일 - 5.2.5

10월 11일 - 5.2.21 (마지막 안정 릴리즈)

몇몇 커널은 “long term” 커널로 지정이 된다. long term 커널은 일반적인 안정 커널보다 더 오랜 기간동안 관리가 된다. long term 커널의 선정은 순수하게 해당 커널이 필요하고, 관리할 시간이 있는 메인테이너에 의해 정해진다. 따라서 long term 커널은 주기적으로 선정이 되지는 않는다.

2.2 The lifecycle of a patch

패치는 개발자의 키보드에서 다이렉트로 mainline으로 머지되는 것이 아니다. 대신 각 패치의 품질을 검토하고, 각 패치가 mainline에 바람직한 수정을 하도록 리뷰 프로세스가 존재한다. 이 리뷰 프로세스는 작은 패치는 빠르게 진행되지만, 크고 논란이 많은 패치의 경우에는 수년동안 진행될 수가 있다. 이 프로세스를 제대로 이해하지 못하거나 우회하려고 할 때 많은 좌절이 생긴다.

그러한 좌절을 최소화하기 위해서, 이 파트에서는 패치가 어떻게 커널에 들어가게 되는지 설명할 것이다. 아래의 내용은 패치가 적용되기까지의 프로세스를 이상적으로 설명한 것이다. (더 디테일한건 나중에 다시 다룬다.)

패치가 일반적으로 밟는 순서는:
- 설계: 여기선 실제 요구사항과, 패치가 그 요구사항을 어떻게 해결할지가 결정된다. 설계는 커뮤니티와 별도로 진행되는 경우도 많지만, 가능하면 커뮤니티와 함께 진행하는 것이 좋다. 나중에 다시 설계하는 것을 방지하 수 있기 때문이다.
- 초기 리뷰: 패치가 관련 메일링 리스트에 보내지고, 해당 메일링 리스트의 개발자들이 자신의 코멘트로 남긴다. 이 단계에서는 중요한 문제가 있을 경우 캐치되어야 한다.
- 더 폭넓은 리뷰: 패치가 mainline에 포함될 정도에 가까워지면, 해당 서브시스템 메인테이너가 패치를 받아들어야 한다. 그럼 패치는 메인테이너의 트리와 -next 트리에서 확인할 수 있게 된다. (-next는 이후에 설명함) 이 단계에서는 해당 패치와 다른 개발자의 패치를 통합했을 때 발생하는 문제들을 해결한다.

대부분의 메인테이너는 각자의 직업이 있다는걸 유의하자. 따라서 패치가 즉각적으로 머지되지 않을 수 있다. 그리고 당신의 패치가 피드백을 받는다면, 피드백을 반영하거나 피드백이 올바르지 않다는걸 설득해야 한다. 만약 리뷰에서 불만 사항이 없는데도 머지가 되지 않는다면, 지속적으로 패치를 보내되, 해당 서브시스템의 git tree에 적용되도록 패치를 갱신해야 한다.

- mainline에 머지: 성공적인 패치는 mainline 저장소에 Linus Torvalds에 머지된다. 이 시점에서 추가적으로 코멘트나 문제가 생길 수도 있다. 패치를 보낸 개발자는 이런 이슈에 대해 지속적으로 응답을 하는 것이 중요하다.
- 안정 릴리즈: 안정 릴리즈 이후에는 사용자들이 실제로 커널을 사용하면서 발생하는 문제가 생길 수 있다.
- 머지 이후 코드를 오랫동안 AS하기: 머지 이후에 개발자가 코드에 관한걸 잊을 수도 있는데, 그러면 개발 커뮤니티에 좋지 않은 인상을 남긴다. mainline에 머지를 하면 API 변경으로 인한 문제들을 해결하므로 부담을 덜어주긴 하지만, 처음 코드를 제안한 개발자가 해당 코드에 대한 책임을 지는 것이 좋다.

커널 개발자들이 가장 많이 실수하는 것이 패치가 머지되는 것으로 끝났다고 생각하는 것이다. 이러한 접근은 같이 참여한 (리뷰어, 메인테이너, 사용자 등) 모든 사람들에게 좌절을 안겨준다.

2.3 How patches get into the Kernel

mainline 커널에 머지를 하는 사람은 Linus Torvalds 단 한명이다. 하지만, 2.6.38 커널을 예로 들면 9,500개 이상의 패치 중에서 Linus가 직접 선택한 패치는 112개 뿐이다. 커널은 규모가 너무 커져서 한 명의 개발자가 모든 패치를 관리할 수 없게 되었다. 커널 개발자들이 이런 성장에도 개발을 할 수 있게 된건, 서로의 신뢰 하에 구축된 대리인 시스템을 이용하는 것이다. (Linus Torvalds 대신 메인테이너들이 패치를 관리하고 그가 메인테이너들을 신뢰하는 것을 말하는듯 하다.)

커널 코드는 기능에 따라 몇 가지 서브시스템으로 분리된다 (네트워킹, 특정 아키텍처 지원, 메모리 관리, 비디오 디바이스 등등..) 대부분의 서브시스템은 메인테이너가 존재한다. 메인테이너는 해당 서브시스템의 코드에 대한 전반적인 채임을 지는 사람이다. 이러한 서브시스템 메인테이너들은 자신의 서브시스템에 들어오는 패치를 관리하는 문지기 같은 역할이다. 아까 말했듯이 mainline에 머지되는 패치들은 갑자기 생겨난게 아니라, 메인테이너들이 선정하고 테스트한 패치들이다.

서브시스템 메인테이너들은 자신만의 커널 소스 트리를 관리한다. 항상은 아니지만, 일반적으로 메인테이너는 git을 사용해 소스를 관리한다. git 등(quilt나 mercurial처럼 관련된 도구들도 있다.)의 도구로 누가 패치를 작성했는지와 관련 메타데이터를 관리한다. 이러한 툴로 언제든지 메인테이너는 자신의 저장소에 있는 패치가 mainline에 포함되었는지 아닌지를 알아낼 수 있다.

머지 윈도우가 열리면, 최상위 메인테이너들은 Linus에게 자신이 수집한 패치를 “pull”하라고 요청한다. Linus가 동의하면, 해당 패치들은 그의 저장소에 들어와서 mainline 커널의 일부가 된다. 그가 pull하는 패치들을 얼마나 심도있게 보느냐는 때마다 다르다. 하지만 종종 그가 패치를 매우 심도있게 본다는 것은 확실하다. 하지만, 일반적으로 Linus는 서브시스템 메인테이너들이 이상한 패치를 보내지 않을 것이라고 신뢰한다.

서브시스템 메인테이너들은 차례대로 다른 메인테이너로부터 패치를 pull한다. 예를 들어 네트워킹 트리는 자신의 하위에 있는 네트워크 디바이스 드라이버 트리와 무선 트리에서 패치를 받아온다. 이런 식으로 사슬처럼 트리와 트리가 연결되어있는 경우에는 연결된 트리의 수는 보통 2~3개를 넘지는 않는다. 그리고 상위 트리의 메인테이너가 하위 트리의 메인테이너를 신뢰하기 때문에, 이런 과정을 “chain of trust”라고 부른다.

이런 시스템에서 패치가 커널에 받아들여지기 위해선 적절한 메인테이너에게 패치를 보내야한다. Linus에게 직접 패치를 보내는건 일반적으로 올바른 길이 아니다.

2.4 Next trees

서브시스템 간의 체이닝은 패치가 어떤 흐름으로 커널에 들어가는지를 보여준다. 하지만 여기서 짚고 넘어갈 점이 있다: 만약에 누군가 다음 머지 윈도우에 포함될 패치를 한 곳에 모아서 보고 싶으면 어떡하나? 개발자들은 머지 윈도우에 포함될 서로 다른 패치들이 문제가 생기는지 알고싶어한다. 예를 들어 API를 수정하는 패치와 해당 수정하기 전의 API를 사용하는 패치가 동시에 머지 윈도우에 포함되면 충돌이 생긴다.

따라서 이런 문제에 대한 해답은 -next 트리에 있다. -next 트리는 서브시스템 트리들이 한 곳에 모여서 리뷰와 테스트가 진행되는 곳이다. -next 트리 중 오래된 것은 Andrew Morton이 관리하는 “-mm” 트리이다. (memory management에서 시작했기 때문에 mm이라고 부른다.) -mm 트리는 수많은 서브시스템 트리들의 패치를 수집해서 통합한다. 또한 -mm에는 디버깅을 돕기 위한 패치들도 있다.

그리고 -mm에는 Andrew Morton이 직접 수집한 수많은 패치들이 있다. 보통 패치가 보내질 적절한 서브시스템을 찾지 못한 경우 마지막에 Andrew Morton이 이를 수집하게 된다. 따라서 잡다한 패치들은 -mm에 머지된 후에 적절한 서브시스템으로 포워딩되거나, Linus에게 직접적으로 보내진다. 일반적으로 매 개발 사이클마다 5~10%의 패치는 -mm 트리를 통해 mainline으로 머지된다.

현재 실시간으로 -mm 트리에 수집된 패치를 보려면 “mmtom” (-mm of the moment)을 확인하면 된다: https://www.ozlabs.org/~akpm/mmotm/
MMTOM 트리를 사용하면서 좌절할 수도 있는데, 아예 컴파일이 되지 않을 수도 있다.

-next 트리 중 중요한 트리는 Stephen Rothwell이 관리하는 linux-next이다. linux-next는, 그 설계상, 다음 머지 윈도우가 끝났을 때 mainline 트리가 갖게 될 모습이다. linus-next 트리는 linux-kernel 메일링 리스트와 linux-next 메일링 리스트에 발표된다. 그리고 다음 링크에서 다운로드 할 수 있다: https://www.kernel.org/pub/linux/kernel/next/

linux-next는 현재 커널 개발 프로세스의 일부가 되었다. 머지 윈도우에 머지된 패치들은, 머지 윈도우가 열리기 전에 linux-next에서 찾을 수 있어야 한다.

2.5 Staging trees

커널 소스 트리에는 drivers/staging 디렉토리가 있다. 이 디렉토리는 드라이버나 파일시스템이 커널에 실제로 포함되기 전에 거치는 단계이다. 더 해야하는 작업이 있을 때 drivers/staging에 머무르다가, 작업이 모두 끝나면 커널로 이동하게 된다. 보통 코딩 스타일을 준수하지 않거나, 퀄리티가 기준에 미치지 못하지만, 개발의 진척을 파악하기 위해 drivers/staging에 상주한다.

Greg Kroah-Hartman이 현재 이 staging 트리를 관리한다. 아직 좀 더 작업이 필요한 드라이버들이 그에게 보내지고, drivers/staging에 해당 드라이버의 폴더가 생긴다. 그리고 각각의 드라이버의 폴더에는 TODO 파일이 존재해야 하는데, TODO에는 해당 드라이버가 커널에 포함되기 위해 해야하는 작업들을 적어놓는다. 또한 해당 드라이버를 수정할 때 누구를 CC해야하는지도 적혀있다. staging 트리에 포함되려면 최소한 제대로 컴파일은 되어야 한다.

staging은 비교적으로 새로운 드라이버가 mainline에 들어가기 쉬운 방법이다. 운이 좋으면 다른 개발자들이 관심을 갖고 개선을 해줄 것이다. 하지만 staging에 포함되었다고 끝난 건 아니다. 오랫동안 개발이 진척되지 않으면 staging에서 사라지게 된다. 배포판을 만드는 사람들은 staging에 존재하는 드라이버를 지원하는 것을 꺼려하는 편이다. 따라서 staging은, 새로운 드라이버가 mainline으로 들어가기 위한 과정으로서 여겨진다.

2.6 Tools

위에서 살펴봤듯 커널 개발 프로세스는 여러 곳에서 수집한 패치에 의존한다. 이러한 패치를 관리하는 강력한 툴이 없으면 개발을 관리하기가 어렵다. 이런 툴의 튜토리얼은 이 문서의 범위에 넘어가기 때문에, 간단한 소개만 하겠다.

리눅스 커널 커뮤니티에서 가장 많이 사용되는 코드 관리 시스템은 git이다. git은 버전 관리 시스템 중 하나이다. git은 커널 개발에 최적화되어있으며, 수많은 저장소와 수많은 패치에도 정상적으로 잘 작동한다. (그도 그럴게 git은 리눅스 커널 개발자 - 특히 Linus Torvalds가 처음 개발을 시작했다.) 커널 개발자들에게 git은 필수로 익혀야 할 도구이다. 자기가 개발할 때 git을 안 쓰더라도, 다른사람이 개발한 걸 보려면 써야한다.

git은 대부분의 리눅스 배포판에 포함되어있다. 홈페이지는 아래 링크를 참고하자. 링크에 접속하면 관련 문서와 튜토리얼을 볼 수 있다.
https://git-scm.com/

git을 사용하지 않는 개발자 중에서는, 가장 인기있는 것이 Mercurial이다.
https://www.selenic.com/mercurial/
Mercurial은 상당수의 기능ㅇ을 git과 공유하지만, 더 쉬운 인터페이스를 제공한다.

그 외에 알아둘만한 것은 Quilt이다.
https://savannah.nongnu.org/projects/quilt/

Quilt는 소스 코드 관리 시스템이라기 보다는 패치 관리 시스템이다. Quilt는 시간에 따라 코드가 어떻게 변경되었는지를 추적하는게 아니라, 특정 코드 베이스로부터 코드가 어떻게 바뀌는지를 추적하여 패치를 관리한다. 몇몇 주요 서브시스템 메인테이너들은 업스트림에 보낼 패치를 관리할 때 quilt를 사용한다. (예를 들어 -mm) quilt는 패치를 관리하기엔 제일 좋은 툴이다.

2.7 Mailing Lists

거의 모든 리눅스 커널 개발은 메일링 리스트를 통해 이뤄진다. 제대로 된 커뮤니티의 일원이 되기 위해선 최소한 하나의 메일링 리스트에 들어가야 한다.

대부분의 커널 메일링 리스트는 vger.kernel.org에서 운영된다. 메일링 리스트의 목록은 다음 링크에서 찾아볼 수 있다:

http://vger.kernel.org/vger-lists.html
vger.kernel.org가 아닌 곳에서 운영되는 리스트도 있는데, 몇개는 다음 링크에서 찾을 수 있다. redhat.com/mailman/listinfo

가장 핵심적인 메일링 리스트는 linux-kernel이다. 이 리스트는 매일 500여개의 메일이 오고간다. 노이즈가 크고 대화 주제는 매우 기술적이며, 대화에 참여하는 사람들이 꼭 예의를 차리는 것도 아니다. 하지만 linux-kernel 말고는 리눅스 커널 커뮤니티가 하나로 모이는 장소가 없다. linux-kernel을 구독하지 않으면 중요한 정보를 놓친다.

linux-kernel에서 생존하는 몇가지 팁이 있다:
- 이 메일링 리스트는 별도의 메일함으로 관리하자.
- 모든 메일을 읽고 이해하려고 하지 말자 - 그런 사람은 아무도 없다.
- 트롤한테는 관심을 끄는게 답이다.
- linux-kernel 이메일에 (다른 메일링 리스트도 똑같다) 답장할 때는 대화에 참여하는 사람들의 Cc를 그대로 유지하자. 이유가 없다면 메일 수신자를 지우면 안된다.
- 질문을 하기 전엔 메일링 리스트 아카이브에서 검색해보자.
- top-posting 방식(답장할 때 메일의 맨 위에 내용을 쓰는 것)을 피하자. (다들 bottom-posting 방식으로 대화를 하는데, 메일링 리스트를 보다보면 알 수 있다.)
- 적절한 메일링 리스트에 메일을 보내자.  linux-kernel이 다른 서브시스템과의 접점이기는 하지만, 특정 서브시스템 관련된 메일을 보내기엔 좋은 장소가 아니다.

마지막으로 언급한 적절한 메일링 리스트를 찾는 부분에서 사람들이 많이 실수한다. 네트워킹 관련된 질문을 linux-kernel에 보내면 아마 netdev 메일링 리스트에 다시 보내라고 답장이 올 것이다. 다른 SCSI, video4linux, IDE, filesystem 등 다른 서브시스템들도 각자의 메일링 리스트가 존재한다. 적절한 메일링 리스트를 찾는 방법은 커널 소스 트리의 MAINTAINER 파일을 참고하는 것이다.

2.8 Getting started with Kernel development

개인 또는 회사로부터 오는 흔한 질문이 어떻게 커널 개발을 시작하냐는 것이다. 개인과 회사 공통적으로 개발을 참여하기에 더 어렵게 만드는 실수가 있다.

회사는 이미 잘 알려진 개발자를 고용해서 리눅스 커널 개발을 시작하기도 한다. 이 방법은 효과적이긴 하지만 비싸고, 숙련된 커널 개발자 풀을 키우기에 좋은 방법은 아니다. 시간을 약간 투자하면 사내의 개발자들로 커널 개발 속도를 높일 수 있다. 그렇게 하면 커널과 회사 모두를 이해하는 개발자를 양성할 수 있다. (그리고 그 사람이 다른 사람들도 가르쳐줄 수 있다.) 중기적으로 이 방법이 더 이득이다.

개인 개발자들은 당연하게도 어디서 시작할지를 잘 모른다. 거대한 프로젝트를 시작하는 것이 어려울 수 있다. 어떤 사람은 작은 것부터 시작하려고 스펠링 오류를 고치거나 코딩 스타일을 개선한다. 하지만 불행하게도 이런 사소한 패치는 개발 커뮤니티 전체에 악영향을 미칠 수 있다. (필자의 개인적인 경험상 하나의 패치를 받아들이기로 결정하면 몇주, 또는 그 이상 관리를 해야하는데, 사소한 패치는 들이는 노력에 비해 얻을게 없다.) 그래서 이렇게 사소한 패치를 보내는 건 좋지 않다.

Andrew Morton은 야심찬 커널 개발자들에게 다음과 같이 조언한다:
커널 개발자들이 가장 중요하게 여겨야 할 목표는 “자신의 손이 닿는 모든 기기에서 커널이 항상 완벽하게 작동하도록 하는 것이다”라고 말한다. 일반적으로 이 목표를 달성하기 위한 방법은, 다른 사람과 협업하면서 문제를 해결하는 것이다. (이 일은 인내심과 지구력을 요구한다!) 괜찮다. 이것도 커널 개발의 일부이다.(https://lwn.net/Articles/283982/)

자신이 어떤 문제를 해결해야할지 모른다면, 현재 regression들을 해결하거나 알려진 버그를 해결하는 것을 권장한다. 수정이 필요한 이슈는 항상 넘쳐난다. 이런 이슈를 해결하면서 개발 프로세스에 경험을 쌓을 뿐만 아니라 개발 커뮤니티 내에서 평판도 얻게될 것이다.


댓글