리눅스 커널의 time 을 나타내는 자료형은 왜 signed 인가?

Taeyoon Kim·2025년 4월 8일

들어가며


리눅스 디바이스 드라이버를 작성하는 중에, 어떤 I/O 동작을 수행하는 시간을 측정하기 위해, 리눅스 커널 내의 시간을 측정하는 함수들의 구현을 찾아보게 되었다.

가장 처음 발견한 것이 ktime_get() 계열의 함수들이었다. 다음의 커널 공식 문서의 링크를 참고 바란다.

이 함수를 보면, 현재 시각을 반환하는 함수들의 반환형이 참 다양한 것을 볼 수 있다.

ktime_t, u64, struct timespec, sturct timespec64
그리고 timespec이면 timespec 이지 timespec64는 또 왜 만들었는가…

이러한 의문점들에 대한 해답을 조금씩 찾아나가며 이 포스팅에 추가해 나가고자 한다.

커널의 시간 자료형들 - ktime_t


ktime_t 자료형이 typedef 되는 곳은 커널의 버전마다 다르다. 5.15 커널의 경우에는 include/linux/ktime.h 라는 헤더 파일에 있는 걸 확인했었는데, 이 글을 쓰는 시점(2025. 4. 8.)의 linux 깃허브 저장소에서 Linux 6.15-rc1 커밋 태그 0af2f6b 에서는 include/linux/types.h 에 에 있다.

typedef s64 ktime_t

로 되어 있다.
(s64는 이름에서 예상할 수 있듯이 signed 64-bit 정수형과 동일하다. s64include/linux/types.h 내에 typedef 되어 있다.)

여기서 한 가지 의문점이 생겼다.
리눅스 커널은 (리눅스만이 아니라 UNIX 전체에서 인가?) 시간을 1970년 1월 1일 0시 0분 0초 (UTC) 를 기준(이 시각을 Unix epoch 라고 한다.)으로 경과 시간을 초 단위(및 나노초 단위?)로 환산하여 표현한다.

근데 왜 경과한 시간을 나타내는 자료형을 signed 자료형으로 만들었을까? Epoch 를 0으로 하여 unsigned로 증가하는 자료형으로 표현하는 게 쉽고 좋지 않나?

그리고 왜 1970년 1월 1일 자정을 Linux의 시간의 기준점으로 삼은걸까? 이는 Unix time 에 대한 영문 위키의 History 파트에서 설명하고 있다. 그 당시 엔지니어들이 이용하기 편리한 시간이었기 때문이라고 서술되어 있다.

ktime_t 라는 자료형은 왜 만들어진건지? 그리고 왜 opaque 타입으로 지정된 건지?

C 표준?, POSIX 표준?에서는 time_t 라는 자료형을 구현할 것을 명시하고 있고, 그것을 구현한 glibc 등의 실제 라이브러리 및 헤더파일에서는 typedef 등을 이용해 time_t 라는 자료형을 유저 애플리케이션을 개발하는 유저들에게 제공한다.

이러한 time_t 와 같은 자료형을 opaque type 이라고 부르는데, 각 libc 의 구현들은 현재 어떤 타겟 시스템에서 사용될 것인가에 따라 signed 32-bit integer 로 만들 수도 있고, signed 64-bit integer 로 만들 수도 있다. 이러한 opaque type 을 쓰는 이유는 인터페이스를 제공할 때, 그 자료형이 나중에 바뀌어도 문제없이 사용되도록 하기 위함이다.(예전에 LDD 였나, 리눅스 커널 심층 분석에서였나 opaque 타입이 필요한 이유에 대한 서술이 있었다. 나중에 발견하면 첨부하겠다.)

예를 들어, 당신이 int funcA(int arg) 의 함수를 만들었는데, 나중에 어떤 이유로 float funcA(float arg) 로 바꾸었다고 생각해 보자. 이 함수를 이용하던 사람들은 기존에

…
int ret;
int arg=3;

ret = funcA(arg);
… 

처럼 사용했을 것이다.
근데 반환값과 매개변수가 float 자료형으로 바뀌면서 기존에 이용하던 코드들은 그대로는 정상 동작을 못하고, 저 함수를 쓴 곳은 모두 float 로 바꿔줘야 한다. 이럴 때 opaque type 을 썼다면 코드 변경 없이 다시 컴파일만 해주면 될텐데 말이다.

아무튼 ktime_t 는 유저 애플리케이션에서의 time_t 처럼 커널 내에서의 시간을 나타내기 위한 opaque type 으로서 만들었다고 볼 수 있는 것 같다.
그럼 ktime_t 타입이 없던 시절의 커널에서는 시간을 나타내기 위해 어떤 타입을 썼던 거지??

-> include/uapi/linux/time.h 를 보면 struct timespec 의 정의부분에 __kernel_old_time_t 라는 자료형의 멤버 변수가 있는 걸 볼 수 있다. 추측해보건대 옛날에는 유저 애플리케이션에서 처럼 time_t 라는 이름의 자료형을 32-bit 로 만들어서 쓰고 있다가 time_t 를 64-bit 로 바꿔야겠다! 라고 생각했지만 기존에 32-bit 라고 가정하고 쓰던 코드가 너무 많기도 하고, 64-bit가 되었는지 알기도 어려우니 64-bit 용 자료형을 따로 만든 것 같다. time64_t 와 struct timespec64..
근데 ktime_t 는 그것과는 상관없을 것 같다. 왜냐면 time_t 와 time64_t 는 초 단위를 나타내기 위해 만든 자료형이고, ktime_t 는 나노초 단위를 나타내기 위해 만든 자료형이라고 쓰여 있기 때문이다.
나중에 패치 기록들을 보면서 언제 누가 어떤 목적으로 추가한 것인지 찾아봐야 할 것 같다…

리눅스 커널 공식 문서의 hrtimers 항목에 서술이 조금 있다!

2038년 문제


2038년 문제가 있는데.. 정확히 어떤 게 문제가 된다는 것일까
문제가 되는 사례가 무엇일지 찾아보자.
추후 추가예정…

kernel 에서와 user application 에서의 시각 읽기 방법의 차이


커널에서는 가장 위에서 설명했듯이, ktime_get() 을 사용하라고 나와있고, 유저 애플리케이션 작성 시에는 clock_gettime() 이라는 API 를 찾을 수 있다. clock_gettime()ktime_get() 은 어떤 과정을 거처 시간을 얻어오는 걸까?

먼저, clock_gettime() 은 vdso 라는 특수한 공유 라이브러리로 구현되어 있다고 한다.

그리고, CLOCK_MONOTONIC 은 system-wide clock 인데, 다중 코어에서 동시에 접근하려고 하면 어떻게 되지?

리눅스 Timekeeping 에 관한 좋은 자료


POSIX CLOCK 이란?


추후 추가 예정…

ktime_t, time64_t 관련 참고 패치


profile
Linux, Yocto Project, Embedded System, OS

0개의 댓글