너무나 오랜만에 커널을 다시 보게 되었다. 8년쯤 되었나보다. 그 때는 3.x 의 커널버전이었는데, 이제는 5.x 가 되었다.
소스를 들여다보니, 이런! 커널의 진입장벽은 더 높아졌고, 모르는 내용이 너무 많아서 차근히 정리해보기로 했다.
우선 _Generic keyword 부터 시작해보자! (커널은 5.4 를 기준으로 하였다)
이 코드를 보게 된 것은 sequence lock 을 보게 되면서이다. sequence lock (seqlock) 은 이후에 다시 설명하도록 한다.
sequence lock 의 read 를 위해서 read_seqcount_begin() 이라는 매크로를 사용하는데, 여기서 코드를 따라가다보면, _Generic keyword 를 만나게 된다. 안녕?
(__seqprop() 에서 _Generic 으로 정의)
<include/linux/seqlock.h>
[...]
#define __seqprop_case(s, lockname, prop) \
seqcount_##lockname##_t: __seqprop_##lockname##_##prop((void *)(s))
#define __seqprop(s, prop) _Generic(*(s), \
seqcount_t: __seqprop_##prop((void *)(s)), \ ---- (5)
__seqprop_case((s), raw_spinlock, prop), \
__seqprop_case((s), spinlock, prop), \
__seqprop_case((s), rwlock, prop), \
__seqprop_case((s), mutex, prop), \
__seqprop_case((s), ww_mutex, prop))
#define seqprop_ptr(s) __seqprop(s, ptr) ---- (4)
#define seqprop_sequence(s) __seqprop(s, sequence)
#define seqprop_preemptible(s) __seqprop(s, preemptible)
#define seqprop_assert(s) __seqprop(s, assert)
[...]
/**
* read_seqcount_begin() - begin a seqcount_t read critical section
* @s: Pointer to seqcount_t or any of the seqcount_LOCKNAME_t variants
*
* Return: count to be passed to read_seqcount_retry()
*/
#define read_seqcount_begin(s) \
({ \
seqcount_lockdep_reader_access(seqprop_ptr(s)); \ ---- (3)
raw_read_seqcount_begin(s); \
})
[...]
<include/linux/fs.h>
[...]
static inline loff_t i_size_read(const struct inode *inode) ---- (1)
{
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
loff_t i_size;
unsigned int seq;
do {
seq = read_seqcount_begin(&inode->i_size_seqcount); ---- (2)
i_size = inode->i_size;
} while (read_seqcount_retry(&inode->i_size_seqcount, seq));
return i_size;
#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPTION)
loff_t i_size;
preempt_disable();
i_size = inode->i_size;
preempt_enable();
return i_size;
#else
return inode->i_size;
#endif
}
[...]
static inline seqcount_t *__seqprop_ptr(seqcount_t *s) ---- (6)
{
return s;
}
[...]
https://en.cppreference.com/w/c/language/generic
우선 _Generic keword 는 C11 부터 지원했던 c 의 keyword 이다. 사용 문법을 확인해보면, 다음과 같다
Syntax
_Generic(controlling-expresssion, association-list)
association-list 는 ',' 로 구분되는 항목이고, 각각은 아래와 같은 문법을 갖는다.
type-name : expression
default : expression
모든 expression 은 comma(',')를 제외한 어떤 expression 이 올 수 있다. comma 는 association-list 를 구분하기 위해서 사용하기 때문에 제외된다.
type-name | 가변적으로 변하지 않는 완전한 객체 타입 |
controlling-expression | default association 이 사용되지 않는다면, type-name 중 하나와 일치하는 어떤 표현식도 가능 |
expression | 모든 type, 값 카테고리를 나타내는 표현식 |
2개의 동일한 type 이 association-list 에 올 수 없다. default 는 반드시 한 번만 사용되어야 한다. default 가 사용되지 않는다면, controlling-expression 에는 반드시 type-name 과 일치하는 type 이어야 한다. 그렇지 않다면 컴파일 에러가 발생한다.
그럼 위 문법에 기초해서 기존 seqlock.h 에 있던 내용을 다시 분석해보자.
최초 (1) i_size_read() 함수에서 시작된 것이다.
이 함수에서 (2) seq = read_seqcount_begin(&inode->i_size_seqcount); 을 호출한다. i_size_seqcount 필드를 찾아보면 seqcount_t 타입으로 정의되어 있다.
따라서 (3) read_seqcount_begin() 의 seqprop_ptr(s) 는 (4)(5) 매크로 정의를 통해서 (6) __seqprop_ptr((void *)(s)) 로 컴파일시 정의된다.
커널의 예제가 너무 어려워 보인다면 아래 예제를 보자
https://www.tutorialspoint.com/generic-keyword-in-c-1-20
#include <stdio.h>
#define typecheck(T) _Generic( (T), char: 1, int: 2, long: 3, float: 4, default: 0)
int main(void) {
printf( "passing a long value to the macro, result is %d \n", typecheck(2353463456356465));
printf( "passing a float value to the macro, result is %d \n", typecheck(4.32f));
printf( "passing a int value to the macro, result is %d \n", typecheck(324));
printf( "passing a string value to the macro, result is %d \n", typecheck("Hello"));
return 0;
}
typecheck(T) 매크로의 파라미터 T 가 char 이면 1 을, int 이면, 2, long 3, float 4 그외는 0 으로 대체해주는 매크로이다.
위 코드를 실행하면 아래와 같다.
passing a long value to the macro, result is 3
passing a float value to the macro, result is 4
passing a int value to the macro, result is 2
passing a string value to the macro, result is 0
이렇게 _Generic 은 보통 다양한 타입을 처리하기 위한 매크로 정의에서 사용된다. 복잡해보이지만, 하나의 매크로를 사용해서 다양한 타입의 파라미터 처리가 가능하도록 하는 keyword 라고 생각하면 될 것 같다.
'IT 기술 > 리눅스 커널' 카테고리의 다른 글
[linux] find_first_zero_bit() 분석 (0) | 2013.05.14 |
---|---|
sparse : kernel static analysis tool (0) | 2013.02.12 |
커널 로컬 버전 세팅 (0) | 2012.11.09 |
Linux kernel CPU Frequency 변경(DVFS) 코드 (2) | 2011.02.11 |
kjournald 에 IPPRIO_CLASS_RT 권한 부여 (0) | 2010.05.03 |