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

그럼 어떤 시스템콜이 블럭 장치를 flush할까?

시스템콜이 끝날때 블럭 장치의 flush가 발생했다는건 알았으니 콜스택을 한번 따라가보겠습니다. 

WRITE: int_ret_from_sys_call
--> syscall_return_slowpath
--> exit_to_usermode_loop
--> task_work_run
--> __fput

exit_to_usermode_loop에서 곧바로 task_work_run을 호출하는게 아니라 do_signal -> get_signal -> task_work_run 순서로 호출됩니다.

task_work_run은 무한 루프를 돌면서 struct task_struct 구조체의 task_works 리스트에 저장된 callback_head 들을 찾아내서 콜백함수를 실행합니다. 우리는 어떤 콜백함수가 호출되었는지 미리 알고있습니다. ____fput이지요. 그러니 어디에서 task_work_add 함수를 호출해서 ____fput을 task_works 리스트에 추가하는지만 찾으면 됩니다.

그럴때는 태깅툴에서 역참조 기능을 사용하면 편리합니다. ____fput을 호출하는 함수는 유일하게 fput 뿐이네요. fput 함수 바디에 init_task_work 함수로 ____fput을 호출하는 callback_head를 만드는걸 확인할 수 있습니다.

그럼 fput은 또 언제 호출된걸까요? fput의 역참조를 확인해봅니다. 굉장히 많은 코드에서 fput을 호출합니다만 우리는 dd 툴이 open/read/write/close하는 굉장히 단순한 툴인걸 알기때문에 시스템콜중에 하나라고 추측이 가능합니다.

그래서 결론적으로 제 생각에 dd에서 mybrd 장치를 닫을때 close 시스템콜을 호출했고, 그때 fput이 호출되었다고 생각됩니다. 그리고 장치파일을 닫을 때 file->f_op->release를 호출할 것이고 지금까지 몇번을 반복했던 것처럼 def_blk_fops와 def_blk_aops에 정의된 콜백 함수들이 호출되었을 것입니다.

그리고 여기에서 우리는 장치 파일이 닫혀질때 기본적으로 해당 장치 파일의 페이지 캐시들이 모두 flush된다는걸 알 수 있습니다. 당연하겠지요. 더이상 장치 파일을 사용하지않는데 장치 파일의 데이터를 메모리에 가지고있어봐야 낭비겠지요.

일반 파일이 닫힐때는 어떨까요? ext2같은 파일시스템이 어떤 file_operations 테이블과 address_space_operations 테이블을 정의했는지 확인해서 함수들을 따라가보면 페이지캐시를 flush하는지 안하는지 확인이될 것입니다.

파일시스템마다 정책이 다를 수도 있겠지요. 어떤 파일시스템은 파일이 닫혀도 나중에 다시 열릴걸 대비해서 페이지캐시를 가지고 있을 수도 있을 것입니다. 그렇게 파일시스템마다 페이지캐시의 정책을 결정할 수 있도록 address_space_operations을 파일시스템마다 별도로 정의할 수 있도록 만든 것입니다.

커널은 코드 한줄한줄 그냥 만든게 없습니다. 이미 이십년이 넘게 적어도 수천명의 개발자들이 매일 업무시간 취미시간에 리뷰하고 또 리뷰한 코드들입니다. 그리고 수백만 수천만 몇개가 될지 모르는 머신에서 24시간 동작하는 코드이구요. 그런걸 생각하면 커널 코드를 분석할 때 의미를 따져보는게 재미있고, 배우는 것도 많습니다.

댓글

댓글 본문
작성자
비밀번호
graphittie 자세히 보기