- 이식성이란, 특정 시스템 아키텍처의 코드가 (가능하다면) 얼마나 쉽게 다른 아키텍처로 이동할 수 있는지를 의미한다.
- 이 장에서는 핵심 커널 코드나 디바이스 드라이버를 개발할 때 이식성 있는 코드를 작성하는 방법에 대해서 알아본다.
- 리눅스는 인터페이스와 핵심 코드는 아키텍처 독립적인 C로 작성됐고, 성능이 중요한 커널 기능은 각 아키텍처에 특화된 어셈블리로 작성해 최적화시켰다.
- 좋은 예로 스케줄러가 있다. 스케줄러 기능의 대부분은
<kernel/sched.c>
파일에 아키텍처 독립적으로 구현돼있다. - 하지만, 스케줄링의 세부 과정인 context switching과 memory management를 책임지는
switch_to()
,switch_mm()
함수는 아키텍처별로 따로따로 구현돼있다.
- 좋은 예로 스케줄러가 있다. 스케줄러 기능의 대부분은
- 1-WORD는 시스템이 한 번에 처리할 수 있는 데이터의 길이를 의미하며 보통 범용 레지스터의 크기와 같다.
- 리눅스 커널은 long 데이터의 크기가 1-WORD 크기와 같다.
- 리눅스 커널은 아키텍처마다
<asm/types.h>
의BITS_PER_LONG
에 long 데이터형 크기로 1-WORD 크기를 지정해 놓았다. - 옛날에는 같은 아키텍처도 32-bit 버전과 64-bit 버전이 따로 구현돼있었지만, 2.6버전 이후로 통합됐다.
- 아키텍처에 따라 C 자료형의 크기가 불명확한 것에 따라 장단점이 있다.
- 장점: long 크기가 1-WORD임이 보장된다, 아키텍처별로 명시적으로 자료형의 크기를 지정하지 않아도 된다 등
- 단점: 코드 상에서 자료형의 크기를 알 수가 없다.
- 따라서 자료형의 크기를 함부로 추정하지 않는 것이 좋다.
- 자료형이 실제 필요로 하는 공간과 형태가 바뀌어도 상관 없도록 코드를 작성해야 이식성 높은 코드를 작성할 수 있다.
- 때로는 개발자가 코드에서 자료형을 더욱 구체적으로 명시화 해줄 필요가 있다.
- 예를 들어, 레지스터나 패킷 같이 HW, NW 관련 코드를 작성해야 하는 경우
- 음수를 저장해야 하는 경우: 명시적으로 signed 키워드를 써주는 것을 권장한다.
- 커널은
<asm/types.h>
파일에 명시적으로 크기가 정해진 자료형(i.e. u8, u16, u32, u64 등)을 typedef로 정의해놨다. - 이 자료형은 namespace 문제 때문에 커널 내부 코드에서만 사용해야 한다.
- 만일, 사용자 공간에 노출해야 한다면, 언더스코어 2개를 덧붙여서
__u8
,__u16
처럼 사용하면 된다. 의미는 같다.
- 바이트 순서: 절대로 바이트 순서를 예측하지 마라. 범용 코드는 빅엔디안, 리틀엔디안 모두에서 동작해야 한다.
- 시간: 절대로 jiffies 값을 양수값과 비교해서는 안 된다. Hz값으로 곱하거나 나눠야 한다.
- 페이지 크기: 페이지 크기는 아키텍처마다 다르다. 당연히 4KB라고 생각해선 안 된다.
- 처리 순서: 아키텍처마다 다양한 방식으로 프로세서 처리 순서를 따르므로 적절한 배리어를 사용해야 한다.
참고