안녕하세요
우선 cpu 성능의 척도가 되는 Bogomips 를 설명하기 전에 간략하게 PIT(Programmable Interval Timer, 프로그래밍 가능한 구간 타이머) 를 알아보겠습니다.
PIT 는 0x40 - 0x43 포트로 프로그래밍이 가능합니다. 즉 100Hz 로 프로그래밍 해놓으면 10ms 마다 한번씩 IRQ에 타이머 인터럽트가 발생하게 됩니다.
8254 칩의 내부 발진기 주파수는 1.193180 인가 뭐 그럴겁니다 ㅡ.ㅡ; amd 의 것은 다르다고 하지만 어쨌든.. 리눅스에선
#define CLOCK_TICK_RATE 1193180 입니다
즉 1초에 1193180번 왔다갔다 할겁니다.
그래서 0x40 포트에 1193180 을 넣어버리면 1초마다 IRQ0이 발생하게 됩니다
근데 이렇게 1초마다 인터럽트가 발생하게 두면 너무 느리겠죠? 아마 이렇게 리눅스를 프로그래밍하면 context-switching 하는게 눈에 보일거고 사실 그전에 돌아가지도 않겠죠...
그래서 여기에 100을 나눈 숫자를 포트에 집어넣으면
1/100 초마다 IRQ0 이 발생됩니다.
리눅스에선 IRQ0 이 발생하면 do_timer() 가 실행되는데 간단하게 소스를 보면
volatile long jiffies;
do_timer()
{
jiffies++;
}
여기서 알수있듯이 인터럽트가 발생할때마다
jiffies 값이 올라갑니다. 이 jiffies 값을 가지고 알람이나 스케쥴링 등등 많은것을 합니다. 매우 중요한 변수지요
이제 bogomips 를 재는 소스를 분석하기 전에
보고밉스가 무엇인가 부터 알고 넘어가도록 합시다
보고밉스는 linus 가 생각해낸 cpu 성능의 척도 입니다. 즉
1 tick(즉 jiffies 가 1 늘어난 동안) 동안 수행 가능한 명령어수 정도로 알고 계시면 됩니다.
아마 리눅스 부팅할때 Calibrating delay loop... 450.210 BogoMIPS! 이렇게 나온걸 보신적 있으실 겁니다. 이 수치가 높을수록 (절대적인 수치는 아니지만) 좋습니다.
커널이 시작해서 여러 초기화를 한후에
sti 명령어를 주어서 인터럽트가 발생하게끔 합니다. 그러면 타이머 인터럽트가 마구 발생하겠죠? 그리고 나서 calibrate_delay 라는 함수를 호출합니다.
이제 소스를 보도록 하죠
#define LPS_PREC 8
void __init calibrate_delay(void)
{
unsigned long ticks, loopbit;
int lps_precision = LPS_PREC;
loops_per_jiffy = (1<<12);
printk("Calibrating delay loop... ");
while (loops_per_jiffy <<= 1) {
/* wait for "start of" clock tick */
ticks = jiffies; 현재 틱 수를 저장합니다.
__delay(loops_per_jiffy); 처음에는 4096(1<<12) 번 무한루프를 돕니다.
ticks = jiffies - ticks; 방금 저장해둔 틱에서 뺍니다.
근데 이 코드가 의외로 쉬울수도 있고 어려울수도 있습니다.
그냥 일반 어플리케이션 개발하던 사람이 보면 당연히 0이 나와야 한다고 생각할지도 모르지만
10ms 마다 jiffies 가 알게모르게 1씩 올라갑니다.
여기서 하는짓은 처음 4096번 무한루프를 돌고 한틱이 지나갔나 알아보는 것입니다. 만약 4096번 돌고도
아직 jiffies - ticks 가 0이면 ( 루프가 도는동안 IRQ0 가 발생하지 않았으면) 2배로 곱해서 다음엔 8192 번 돌고
또 아니라면 반복합니다.
if (ticks)
break;
}
아래 코드는 더욱 정확히 재기 위한것입니다. 리눅스 커널 1.0 에선 이런 코드가 없었는데
2.4 를 보니 있군요. 이에는 숫자를 2배씩 곱하기 때문에 정확한 숫자를 구할수가 없습니다
예를들어 6000번이 한계이면 8192 라는 값으로 잡히게 되어 있습니다. 이것을 방지하기 위해서
loops_per_jiffy >>= 1 세밀하게 다시 2로 나누고 (4096 이라고 저장하고..)
loopbit = loops_per_jiffy;
while ( lps_precision-- && (loopbit >>= 1) ) {
4096 + 2048 해서 한틱이 지나갔으면 값을 프린트 하고 아니면
다시 거기다가 1024 를 더하고 또 아니라면 512 를 더하고 이런식으로 해서 거의 정확하게 구하는 것입니다.
loops_per_jiffy |= loopbit;
ticks = jiffies;
__delay(loops_per_jiffy);
if (jiffies != ticks) /* longer than 1 tick */
loops_per_jiffy &= ~loopbit;
}
/* Round the value and print it */
printk("%lu.%02lu BogoMIPS\n",
loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100);
여긴 프린트 ㅡ.ㅡ;
}
PC에 내장되어 있는 CPU는 8259에서 IRQ를 받습니다.. 그리고 8259는 8253이라는 클럭 디바이스에 연결되어 있지요..
CLOCK_TICK_RATE가 1193180으로 정의되어 있는 이유는 기본적으로 8253의 칩 클럭이 1.19318MHz이기 때문입니다.
그것을 i386 아키텍쳐의 리눅스는 100Hz Timer로 쓰고 있기 때문에 LATCH를 시키려고 100Hz로 나누어 downcounting을 하는 것을 볼 수 있죠.. 그럼 Latch값이 11931.8가 되고 8253의 frequency는 1193180 / 11931.8 = 100 Hz가 되어 주기가 10ms 가 되는 것을 볼 수 있죠..
따라서 리눅스 커널을 클럭 디바이스로 8253을 쓰지 않는 플랫폼에 포팅한다고 하면 저 수치는 칩 데이타에 근거하여 바뀌어져야 합니다.
CLOCK_TICK_RATE가 1193180으로 정의되어 있는 이유는 기본적으로 8253의 칩 클럭이 1.19318MHz이기 때문입니다.
그것을 i386 아키텍쳐의 리눅스는 100Hz Timer로 쓰고 있기 때문에 LATCH를 시키려고 100Hz로 나누어 downcounting을 하는 것을 볼 수 있죠.. 그럼 Latch값이 11931.8가 되고 8253의 frequency는 1193180 / 11931.8 = 100 Hz가 되어 주기가 10ms 가 되는 것을 볼 수 있죠..
따라서 리눅스 커널을 클럭 디바이스로 8253을 쓰지 않는 플랫폼에 포팅한다고 하면 저 수치는 칩 데이타에 근거하여 바뀌어져야 합니다.
출처 : http://www.asmlove.co.kr/Board/Lecture02/44094/page/3
'Linux' 카테고리의 다른 글
리눅스 원격접속 VNC 서버 사용하기 (0) | 2010.07.15 |
---|---|
리눅스에서 시디이미지 만들기 (0) | 2010.07.08 |
CSCOPE settings for vim (0) | 2010.04.02 |
스크립트(awk,sed,vi,gcc..) (0) | 2009.10.24 |
고급 타이머 (0) | 2009.07.30 |
WRITTEN BY
- RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.
,