Linux kernel v4.4에서 간단한 블록 장치 드라이버 만들어보기

bio 구조체

드라이버가 request-queue를 만들고, 커널이 IO 요청을 request로 만들어서 큐를 통해서 전달한다고 말씀드렸지요. 그런데 이번 장에서 바로 이 request 객체를 처리하는걸 만들어보지는 않을겁니다. 그것보다 더 간단한 자료구조인 bio를 소개하고, bio를 기준으로 IO를 처리하는걸 구현해보겠습니다.

bio가 뭐냐면 IO를 처리하는데 있어서 최소단위가 되는 구조체입니다. request 객체는 결국 여러개의 bio를 가지게됩니다. 왜 bio 처리를 먼저 만들어보냐면 IO의 최소단위이므로 커널이 IO를 처리하는 과정을 그대로 볼 수 있기 때문입니다. 나중에 request 단위로 처리하는걸 만들면 bio의 merge라는 등 더 복잡한 처리가 소개되니까 일단 가장 기본 단위부터 만들어보는게 이해하기 좋겠지요.

struct bio & struct bio_vec

디스크 IO가 일어나려면 기본적으로 얼마만큼의 데이터를 어디에서 가져와야된다는 정보가 있어야합니다. 그 정보가 바로 struct bio 구조체로 표현됩니다.

그리고 섹터라는게 나옵니다. 바로 디스크 IO 최소단위입니다.  디스크에 한번에 쓰고 읽을 수 있는 단위가 바로 섹터입니다. 하드디스크를 상상해볼까요. 둥그런 플래터가 빙빙돌고 있습니다. 바로 그 플래터의 한 부분을 섹터라고 부르고, 크기는 512바이트입니다.

https://ko.wikipedia.org/wiki/%ED%95%98%EB%93%9C_%EB%94%94%EC%8A%A4%ED%81%AC_%ED%94%8C%EB%9E%98%ED%84%B0

디스크를 둥근 플래터로 생각하지말고 섹터의 배열이라고 생각하봅시다. 그럼 몇번째 섹터부터 몇개의 섹터를 읽고 쓸거냐라는게 왜 IO의 기준이 되는지 이해가 될겁니다. 물론 하드디스크냐 SSD냐 등등 섹터만 기준이 되는게 아닙니다. 헤더니 실린더니 하드디스크에서 어느 위치냐를 지정하는데는 더 복잡한 방법이 사용됩니다. 실제 장치 드라이버를 만들려면 장치의 기계적인 특성을 모두 알아야겠지만 우리는 가상의 장치만 생각하겠습니다. 이 장치는 섹터가 한줄로 길게 늘어선 모양이고 섹터의 배열로 이루어져있습니다.

사용자 어플을 생각해보세요. read()/write() 시스템 콜을 보면 파일의 어느부분부터 몇 바이트를 읽고 쓰겠다는 명령을 내립니다. 드라이버는 몇번 섹터부터 몇개의 섹터를 읽고 쓰라는 명령을 받습니다. 중간에 있는 커널의 블럭 레이어가 바로 파일의 위치, 크기를 섹터위치, 섹터 갯수로 변환해주는 일을 합니다. 드라이버는 결국 섹터만 생각하면 됩니다.

그리고 섹터 정보가 모여있는게 bio입니다. 

http://www.makelinux.net/books/lkd2/ch13lev1sec3

여기에 좋은 그림이 있네요. 하나의 bio에는 여러개의 bio_vec가 들어있고, 각 bio_vec에는 어떤 페이지의 어떤 위치에 몇 바이트라는 정보가 있습니다. 이 bio_vec이 세그먼트를 표현하는 구조체입니다.

세그먼트니 섹터니 bio니 알아야될 개념이 많아지고 어지러워집니다. 그런데 코드를 보면 간단합니다. 복잡하게 생각할 것 없이 바로 코드를 보면서 이해하시면 됩니다.

몇번 섹터부터 - bio->bi_iter.bi_sector

몇개의 섹터를 - bio_sectors(bio)

읽을거냐 쓸거냐 - bio_rw(bio)

당연히 몇개의 섹터가 될지 모르지만 하나의 페이지 4096바이트보다 클 수 있겠지요? 그러니 bio_for_each_segment 매크로를 써서 각 bio_vec을 하나씩 꺼내오면 됩니다. 그러면 데이터를 읽고 써야할 페이지와 페이지 내부의 offset, 길이 정보를 알 수 있습니다. 설명도 깊고 많은 개념들이 나타나지만 코드로 보면 정작 중요한건 몇개 안된다는걸 알 수 있으실겁니다. 데이터 방향, 크기를 어떻게 표현하느냐입니다.

우리는 그냥 bio_vec의 정보만 출력했지만 사실은 뭐가 필요할까요? 바로 여기가 DMA 동작을 실행해야하는 부분입니다. DMA는 페이지 단위로 실행됩니다. 그래서 bio 구조체가 페이지 정보들을 가지고 있는 것입니다. 하나의 bio가 하나의 scatter-gatter DMA가 됩니다.

어쨌든 bio를 분석하면 어떤 페이지에 얼마만큼 읽기/쓰기를 해야하는지를 알 수 있습니다. 그리고 IO가 끝나면 해당 bio를 폐기처분해야합니다. bio객체도 어딘가에서 할당했을거니 당연히 해지가 필요하겠지요. 그런 일을 하는 함수가 bio_endio()입니다.

그리고 이 장치가 얼마만큼 읽기/쓰기를 했는지 등의 통계 정보를 갱신하는게 generic_start/end_io_acct()입니다. 통계 정보는 커널을 부팅해서 확인해보겠습니다.

마지막으로 BLK_QC_T_NONE을 반환합니다. 그냥 아무 문제 없었다는걸 알려주는 것입니다. 커널이 mybrd_make_request_fn을 호출했을테니 커널에게 문제 없음을 알려주는 것이지요.

더 자세한 설명은 https://www.kernel.org/doc/Documentation/block/biodoc.txt 를 참고하세요.

댓글

댓글 본문
작성자
비밀번호
버전 관리
gurugio
현재 버전
선택 버전
graphittie 자세히 보기