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

per-cpu 변수 사용하기

per-cpu 변수를 만들었으니 어떻게 쓰는지를 봐야겠지요.

가장 대표적인게 per_cpu매크로를 이용하는 방법입니다. per_cpu는 사실 DEFINE_PER_CPU 매크로에서 설명할 때 설명했던것 같이 원래 .data.percpu섹션에 있던 변수의 주소에 cpu_pda[cpu].data_offset을 더하는 작업을 매크로로 만든것 뿐입니다. 그런 주소값을 더하는 작업을 RELOC_HIDE라는 매크로로 처리합니다.

RELOC_HIDE 매크로의 정의를 보면 컴파일러마다 다르게 만들어놨는데요 주석을 읽어보면 gcc가 최초 .data.percpu 섹션에 정의된 변수의 주소에 이상한 값을 더하려한다는걸 알고 뭔가 최적화를 하거나 나름대로 뭔가를 더 하려고할 수 있는데, 그걸 방지하는게 목적이라고 써있습니다. gcc는 foo라는 변수가 0x1010 위치에 8바이트 크기로 저장된걸 알고있는데 갑자기 &foo + 0x1000 이라는 연산을 한다면 똑똑한 gcc는 에러라고 생각할 수 있다는 것입니다.

좀더 자세한 설명은 아래 참고자료를 읽어보세요.

참고: http://studyfoss.egloos.com/5374731

어쨌든 gcc가 아닌 intel 컴파일러용 코드 include/linux/compiler-intel.h를 보면 주소를 더할 뿐이라는걸 알 수 있습니다.

좀더 최신 버전에서 per-cpu 구현에 대한 설명도 참고 자료를 확인하시기 바랍니다.

참고: http://studyfoss.egloos.com/5375570

그럼 per_cpu 매크로를 사용하는 예제 코드를 보겠습니다.

v2.6.11 코드를 기준으로 recalc_bh_state라는 함수가 있습니다.

struct bh_accounting {
    int nr;			/* Number of live bh's */
	int ratelimit;		/* Limit cacheline bouncing */
};

static DEFINE_PER_CPU(struct bh_accounting, bh_accounting) = {0, 0};

static void recalc_bh_state(void)
{
	int i;
	int tot = 0;

	if (__get_cpu_var(bh_accounting).ratelimit++ < 4096)
		return;
	__get_cpu_var(bh_accounting).ratelimit = 0;
	for_each_cpu(i)
		tot += per_cpu(bh_accounting, i).nr;
	buffer_heads_over_limit = (tot > max_buffer_heads);
}

struct bh_accouting이라는 데이터구조는 버퍼헤드의 갯수 nr 정보를 가지고 있습니다. 만약 커널 전역 변수로 struct bh_accouting타입의 객체를 만들어놨으면 버퍼해드를 만들때마다 각 프로세서가 캐시를 날리고 메모리를 읽고 다른 프로세서의 캐시도 날리고 등의 작업을 할 것입니다.

그래서 커널에서는 DEFINE_PER_CPU로 bh_accouting 객체를 정적으로 만들어놨습니다. 그리고 recalc_bh_state 함수에서 각 cpu마다 bh_accounting 변수를 참조해서 총 갯수를 계산합니다. per_cpu 매크로의 결과가 포인터가 아니라는 것에 주의해야합니다.

댓글

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