원문 은 리눅스 커널 documentation 의 static - keys 입니다 . 전문 번역 이 아닌 개인 적 이해 를 바탕 으로 해석 한 것 입니다 . 원문 을 충실히 다루 지 도 않 았 고 , “ Analysis ” 부분 은 직접 분석 한 내용 을 담 았 습니다 . 오자 나 오류 는 전적 으로 그 로 인한 결과 일 것 입니다 . 알려 주 시 면 기쁘 게 수정 / 반영 하 겠 습니다 . 
 Static key 는 GCC 기능 과 코드 패 칭 기술 을 통해 성능 이 중요 한 fast - path 커널 코드 사이 에 자주 사용 되 지 않 는 기능 을 삽입 가능 하 게 한다 . 다음 예제 를 참고 하 라 : 
 if ( static _ branch _ unlikely ( & key ) ) 
 do unlikely code 
 do likely code 
 likely 코드 경로 에 최대한 영향 을 미치 지 않 는 방향 으로 static _ branch _ unlikely ( ) 분기 가 삽입 될 것 이 다 . 
 현재 tracepoints 는 조건 분기 로 구현 되 어 있 고 , 조건 검사 는 각 tracepoint 마다 전역 변수 를 체크 하 는 것 으로 이루어진다 . 이러 한 조건 검사 자체 의 오버헤드 는 미미 하 지만 전역 변수 에 할당 된 메모리 캐시 라인 이 다른 메모리 공간 과 공유 될 때 ( 하나 의 메모리 캐시 라인 은 서로 다른 여러 메모리 공간 에서 공유 되 므로 ) 메모리 캐시 사용량 이 늘어날수록 그 오버헤드 가 커진다 . 커널 내 tracepoints 가 늘어날수록 오버헤드 는 무시 할 수 없 는 문제 가 된다 . 게다가 tracepoints 는 보통 사용 되 지 않 는대 다 직접 적 인 커널 기능 을 제공 하 지 도 않 는다 . 따라서 tracepoints 조건문 의 영향 을 최소 화 하 는 것 이 효과 적 일 것 이 다 . 
 static key 매 커 니즘 은 이 와 같이 tracepoints 오버헤드 를 최소 화 하 기 위해 개발 되 었 지만 다른 커널 코드 경로 에 도 사용 할 수 있 다 . 
 gcc 버전 4 . 5 에서 레이블 로 분기 할 수 있 는 asm goto 구문 이 추가 되 었 다 : http : / / gcc . gnu . org / ml / gcc - patches / 2009 - 07 / msg 01556 . html 
 asm goto 를 사용 하 면 메모리 로드 없이 분기 하 거나 또는 분기 하 지 않 고 순차 적 으로 실행 되 는 코드 흐름 을 만들 수 있 다 . 또한 런타임 에 코드 흐름 을 변경 할 수 있 다 . asm goto 는 자동 적 으로 volatile 속성 을 갖 는다 . 
 가령 기본 적 으로 분기 하 지 않 는 다음 과 같 은 구문 이 있 다면 : 
 printk 함수 는 실행 되 지 않 을 것 이 고 , 분기 조건문 은 단지 no - op 명령어 로 대체 될 것 이 다 . 이후 런타임 에 분기 흐름 을 타 도록 변경 하 면 no - op 명령어 는 jump 명령 으로 대체 되 고 순차 적 이 었 던 코드 흐름 은 깨지 게 된다 . 현재 코드 를 런타임 에 다른 코드 로 대체 해야 하 기 때문 에 코드 흐름 을 변경 하 는 것 은 상대 적 으로 성능 상 부담 이 크 다 . 하지만 분기 조건 이 결정 된 이후 에 ( 코드 흐름 을 변경 하 는 경우 가 아니 라면 ) 조건 검사 가 없 으므로 분기 하 는 데 에 아무 비용 이 들 지 않 는다 . 
 static key 를 구현 하 는 데 사용 된 이 기반 코드 패 칭 매 커 니즘 을 jump label patching 이 라고 부른다 . 
 static key 라는 조건 분기 최적화 를 사용 하 기 위해서 는 키 ( key ) 를 먼저 다음 과 같이 정의 ( 등록 ) 해야 한다 : 
 키 는 런타임 에 등록 할 수 없 고 반드시 global 변수 여야 한다 . 즉 , 스택 또는 동적 으로 할당 된 메모리 공간 에 위치 할 수 없 다 . 
 등록 한 키 는 다음 과 같이 사용 된다 : 
 if ( static _ branch _ likely ( & key ) ) 
 DEFINE _ STATIC _ KEY _ TRUE ( ) 또는 DEFINE _ STATIC _ KEY _ FALSE ( ) 로 등록 된 키 는 static _ branch _ likely ( ) 또는 static _ branch _ unlikely ( ) 구문 에서 사용 될 수 있 다 . 
 분기 조건 은 다음 과 같이 참 true 으로 설정 할 수 있 다 : 
 또는 거짓 false 으로 설정 할 수 있 다 . 
 그리고 분기 조건 은 다음 과 같이 참조 카운트 reference counts 로 변경 switched 할 수 있 다 . 
 참조 카운트 에 따라 static _ branch _ inc ( ) 는 분기 조건 을 참으로 만들 고 , static _ branch _ dec ( ) 는 분기 조건 을 거짓 으로 만든 다 . 예컨데 키 가 참 true 으로 초기 화 되 었 다면 static _ branch _ dec ( ) 는 분기 조건 을 거짓 으로 만들 것 이 다 . 그 뒤 에 실행 되 는 static _ branch _ inc ( ) 는 분기 조건 을 참으로 만든 다 . 만약 키 가 거짓 으로 초기 화 되 었 다면 마찬가지 로 static _ branch _ inc ( ) 는 분기 조건 을 참으로 만들 것 이 고 뒤따른 static _ branch _ dec ( ) 는 분기 조건 을 거짓 으로 만들 것 이 다 . 
 키 등록 부터 살펴보 자 : 
 # define DEFINE _ STATIC _ KEY _ TRUE ( name ) \ 
 한 단계 추상 화 된 랩퍼 wrapper 타입 내부 의 실제 타입 은 다음 과 같 다 : 
 /* Set lsb bit to 1 if branch is default true , 0 ot */ 
 enabled : 1 이면 true , 0 이면 false 
 entries : enabled 와 동일 한 값 으로 등록 . 차후 초기 화 과정 에서 섹션 에서 읽 어 들인 실제 entry 주소 와 합산 , true / false 값 유지 
 next : 미 분석 
 다음 으로 static _ branch _ likely ( ) 를 살펴보 면 : 
 # define static _ branch _ likely ( x ) \ 
 if ( __ builtin _ types _ compatible _ p ( typeof ( * x ) , struct static _ key _ true ) ) \ 
 else if ( __ builtin _ types _ compatible _ p ( typeof ( * x ) , struct static _ key _ false ) ) \ 
 위 코드 의 조건문 은 컴파일러 최적화 에 의해 컴파일 시점 에 모두 제거 된다 . 대신 그 위치 에 분기 또는 nop 코드 가 생성 된다 . likely , unlikely 과 true , false 의 조합 으로 생성 되 는 코드 의 결과 는 다음 과 같 다 : 
 type \ branch 
 true ( 1 ) … … 
 NOP JMP L 
 false ( 0 ) … … 
 JMP L NOP 
 asm _ volatile _ goto ( " 1 : \ n \ t " 
 WASM ( nop ) "\ n \ t " 
 ". pushsection __ jump _ table , \" aw \"\ n \ t " 
 ". word 1 b , % l [ l _ yes ] , % c 0 \ n \ t " 
 static __ always _ inline bool arch _ static _ branch _ jump ( struct static _ key * key , bool branch ) 
 WASM ( b ) " % l [ l _ yes ] \ n \ t " 
 인라인 어셈블리 코드 상 에 데이터 영역 . word 1 b , % l [ l _ yes ] , % c 03 부분 은 struct jump _ entry 타입 으로 __ jump _ table 메모리 섹션 에 쌓이 게 된다 . 이때 어셈블리 코드 상 의 레이블 1 의 위치 address 는 jump _ entry -> code 변수 에 저장 되 고 , l _ yes C 레이블 은 jump _ entry -> target 변수 에 , 그리고 &(( char *) key ) [ branch ] ( 데이터 타입 의 짝수 시작 주소 또는 홀수 시작 주소 ) 는 jump _ entry -> key 변수 에 저장 된다 . 
 branch 값 이 true ( likely ) 일 때 , key 변수 가 가리키 는 static _ key 구 조체 의 시작 주소 는 ( char *)&[ 1 ] 이 되 고 , false ( unlikely ) 일 때 는 ( char *)&[ 0 ] 가 된다 . 즉 , entry -> key 를 읽 었 을 때 최하위 비트 가 1 이 라면 likely , 0 이 라면 unlikely 라고 디 코딩 된다 . 
 결국 static _ key 구조 체의 멤버 변수 entries 의 최 하위 비트 에 는 초기 타입 값 이 true 인지 false 인지 가 인코딩 되 고 , jump _ entry 구조 체의 멤버 변수 key 의 최 하위 비트 에 는 분기 타입 이 likely 인지 unlikely 인지 가 인코딩 된다 . 
 jump _ lable 초기 화시 4 분기 유 / 무에 따라 jump _ entry -> code 즉 , 레이블 1 위치 에 분기 코드 가 삽입 되 거나 nop 코드 가 삽입 된다 5 . 
 초기 화시 jump _ label _ init ( ) 에서 정렬 은 왜 하나 ? 
 동일 한 키 가 여러 코드 에서 사용 된 경우 사용 된 모든 코드 에서 흐름 을 변경 해야 한다 . 이 를 위해서 __ jump _ table 섹션 의 entry 들 을 키 순 으로 정렬 해서 업데이트 할 시 해당 키 의 첫 번 째 섹션 위치 에서 시작 해 동일 한 키 를 갖 는 연속 된 entry 의 코드 를 모두 변경 하 게 된다 . 
 왜 JUMP _ LABLE _ NOP 의 경우 에 만 nop 을 다시 쓰 는가 ? at jump _ label _ init ( ) in kernel / jump _ label . c 
 동적 으로 코드 흐름 변경 하 는 코드 분석 
 yaos 버전 구현 ( 최적화 에 따라서 else 구문 사라지 나 ? ) 
 asm goto 에서 else 구문 살림 
 최적 화 없이 리눅스 커널 은 컴파일 되 지 않 음 
 Extended - Asm 
 __ builtin _ types _ compatible _ p ( ) - 인자 로 넘어간 두 변수 의 타입 이 호환 성 을 갖 는다면 ( 동일 하 다면 ) 1 을 리턴 하 고 , 그렇 지 않 다면 ( 다르 다면 ) 0 을 리턴 한다 . 최상위 레벨 의 한 정사 const , volatile 속성 은 무시 된다 . 즉 , const int 와 int 는 동일 타입 으로 간주 한다 . int [ ] 와 int [ 5 ] 역시 동일 타입 으로 간주 한다 . 아키텍처 에 따라 타입 의 사이즈 가 동일 한 int 와 char * 는 동일 타입 으로 간주 하 지 않 는다 . short * 과 short ** 은 서로 다른 타입 이 며 , 각 enum 역시 동일 타입 으로 간주 하 지 않 는다 . ↩ ︎ 
 arch _ static _ branch ( ) 또는 arch _ static _ branch _ jump ( ) . arch _ static _ branch 가 nop 을 삽입 하 는 반면 arch _ static _ branch _ jump 는 분기 코드 를 삽입 한다 ↩ ︎ 
 % c 는 컴파일러 가 상수 에 붙이 는 일련 의 정보 를 추가 하 지 않 도록 함 ↩ ︎ 
 jump _ label _ init ( ) at start _ kernel ( ) in / init / main . c ↩ ︎ 
 사실 컴파일 시점 에 이미 조건문 은 모두 제거 된 상태 이나 static _ key 구 조체 설정 과 동적 변경 을 위해 초기 화 가 필요 ↩ ︎