[세미나] 타이밍 측정
개요
- 운영체제를 관리하는 대부분의 커널 함수가 시간의 흐름에 따라 동작
- 대부분의 커널 함수가 이벤트 기반이 아닌 시간 기반 조절 방식
- 타이밍 측정 = 타이머 인터럽트를 통하여 해결하고
- 모든 커널 동작 시간을 조율 = 타이머 인럽트 핸들러에서
- 컴퓨터의 많은 작업은 시간 정보를 기반으로 작동한다.
- 예) 일정시간 사용하지 않으면 화면 꺼짐
- 예) 일정 시간 동안 사용하지 않는 파일 삭제
- 타이밍 측정과 관련됨 메커니즘의 분류
- 현재 날짜와 시간정보를 유지하며 사용자 요청시 알려주는 메커니즘
- 관련 시스템 콜: TIME(), FTIME(),GETTIMEOFDAY()
- 일정 시간이 지나면 알려주는 메커니즘
- 현재 날짜와 시간정보를 유지하며 사용자 요청시 알려주는 메커니즘
- 문서 구성
- 타이밍 측정 관련 하드웨어
- 커널 구조 & 함수
- 시스템 콜과 서비스 루틴
하드웨어 시계
- 시계의 종류
- 실시간 시계(Real time Clock) : 커널에 현재 날짜와 시간을 알려줌
- 타임 스탬프 카운터 (Time Stamp Counter) : 커널에 현재 날짜와 시간을 알려줌
- 프로그래밍 가능한 구간 타이머(Programmable interval timer) : 고정된 주파수로 인터럽트 발생
실시간 시계 (RTC)
- 전원이 꺼진후에도 계속 작동, 비휘발성 장치 이용, 배터리 사용
- 2Hz ~ 8192Hz 사이의 주파수로 IRQ8에 정기적 인터럽트 발생
- 다른 칩의 영향을 받지 않고 시간 계산
- 리눅스에서는 날짜 시간을 알리는 용도로만 사용
- /dev/rtc장치 파일 조작을 총하여 RTC프로그래밍 가능
- 커널 초기화시 RTC시간을 읽어 xtime구조체 변수에 저장
타임 스탬프 카운터 (TSC)
- 64bit크기의 레지스터 (인텔 80x86 마이크로 프로세스)
- rdtsc(Read Timestamp counter) 어셈블리어 명령 인식 (프로그램 타임 구하는 함수)
- 각 클럭 시그널 수신시 증가
- PIT보다 정확한 시간 측정
프로그래밍 가능한 구간 타이머(PIT)
- 시스템 타이머
- 알람 시계와 비슷한 기능
- PIT역할 = 지정 시간에 타이머 인터럽트 발생
- 커널에 시간 간격이 하나 이상 지났다는 사실 알려줌
- 틱(tick) : 타이머 인터럽트 발생 간격, 시간 간격
- 100Hz, 10ms마다 한번씩 IRQ0에 타이머 인터럽트 발생 하는것
- 틱을 통해 커널이 현재 시각과 시스템 가종 시간을 유지
- 시스템과 관련하여 모든 동작 시간을 조율
- 타이머 인터럽트의 진동수(Tick Rate : HZ)
- 시스템 타이머의 진동수는 시스템의 부팅시에 미리 정의된 HZ에 의해 정해짐
- <arm/param.h>에 정의 #define HZ 100
- 아키텍쳐에 따라 다른 값이 올수 있음
- 현재 대부분의 플랫폼에서 100HZ로 정의됨
- 커널의 시간 개념은 전적으로 시스템 타이머의 주기성에 달려 있음
- 그러므로, 적절한 시스템 타이머의 주기값이 중요
- HZ값이 높을 경우 장점 (짧은 tick) : 시간 관련 이벤트의 정확도가 높아 진다.
- HZ값이 높을 경우 단점 : 타이머 인터럽트 핸들러로 인한 부하 증가
- 그러므로, 적절한 시스템 타이머의 주기값이 중요
타이머 인터럽트 핸들러
- 타이머 인터럽트 수행 동작
- 시스템 시작부터 경과한 시간을갱신
- (긴급, 인터럽트 핸들러가 관리)
- 날짜와 시각을 갱신
- (덜 긴급, Timer_BH&TQUEUE_BH의 함수가 처리)
- 프로세스에게 할당된 cpu선점 시간 관련
- (덜 긴급, Timer_BH&TQUEUE_BH의 함수가 처리)
- 자원 사용 통계 갱신
- (덜 긴급, Timer_BH&TQUEUE_BH의 함수가 처리)
- 시스템이 지정한 시간이 넘었는지 확인후 해당 함수 호출
- (덜 긴급, Timer_BH&TQUEUE_BH의 함수가 처리)
- 시스템 시작부터 경과한 시간을갱신
첫째 작업 = 긴급하다고 고려 하므로 타이머 인터럽트 핸들러 자체에서 처리
나머지작업=덜 긴급 하므로 TIMER_BH, TQUEUE_BH bottom half에서 호출하는 함수가 처리
- 커널의 기본적인 시간 관리 기능 2가지
- 현재 시간을 최신으로유지
- 방법1.a : CPU에 TSC 레지스터가 존재시 do_gettimeofday()로 계산 -------> do_get_fast_time 변수에 저장
- 방법1.b : CPU에 TSC 레지스터가 부재시 do_normal_gettime()로 계산 -------> do_get_fast_time 변수에 저장
- 현재 시간을 최신으로유지
- 현재 초 내에서 경과한 마이크로초 수를 헤아림
- 방법1.a : TSC 레지스터가 존재시 do_fast_gettimeoffset()로 계산 -------> do_gettimeoffset 변수에 저장
- 방법1.b : TSC 레지스터가 부재시 do_slow_gettimesetoff()로 계산 -------> do_gettimeoffset 변수에 저장
- 현재 초 내에서 경과한 마이크로초 수를 헤아림
bottom_half()
PIT의 인터럽트 서비스 루틴
- IRQ0 인터럽트 초기화
- irqaction 디스크립터의 handler 필드에 timer_interrupt()함수 주소 포함
- status 필드에 SA_INTERRUPT 플래그 설정 (인터럽트 금지)
- timer_interrupt()실행
- cpu에 TSC레지스터 있는지 체크
- rdtsc 어셈블리어 명령을 실행하여 TSC레지스터 값을 last_tsc_low 변수에 저장
- 8254칩 내부 발진기 상태를 읽어 타이머 인터럽트가 발생부터 인터럽트 서비스 루틴을 실행까지 지연시간 계산
- 지연된 시간을 마이크로초 단위로 delay_at_last_interrupt변수에 저장
- do_time_interrupt()실행
- do_time()함수 실행
- 시스템 시작 시간부터 흘러간 시간을 나타내는 값 수정
- 타이밍 측정과 관련한 세가지 주요 변수 참고
- do_time()함수 실행
- Jiffies : 시스템이 시작할때 부터 지나간 틱수, 타이머 인터럽트시 증가
- lost_tick : xtime을 마지막으로 갱신 후 발생한 틱
- lost_ticks_system : xtime을 마지막으로 갱신후 프로세스가 커널 모드에서 동작동안 발생한 틱 수
2. adjtimex()을 호출한 경우
- 660초(11분)마다 한번씩 set_rtc_mmss()함수 호출하여 실시간 시계를 수정
- 네트워크에 있는 시간을 동기화 시킴
이 이외의 작업은 중요한 것이 아니므로 TIMER_BH에서 처리
TIMER_BH 하반부 함수
- TIMER_BH()와 연결된 timer_bh() 함수는 보조 함수들을 호출한다.
- update_times()
- run_old_timers()
- run_timer_list()
역할 1 : 날짜와 시간 갱신
- timeval.xtime : 현재 날짜와 시간 저장
- xtime.tv_sec : 1970년 1월 1일 자정부터 초 단위의 시간 정보 저장
- xtime.tv_usec : 마지막 초에서 경과한 시각
- 시스템 초기화
- time_init() 호출 : 날짜 시간 설정 함수, get_cmos_time()을 이용
- get_cmos_time() 호출 : RTC에서 시간 정보 읽어 드림
- xtime초기화
- 매 틱 발생시 update_times() 사용하여 xtime갱신
역할 2 : 자원 사용 통계 갱신
- top등의 유틸리티 등이 사용
- 갱신시 lost_ticks와 lost_tick_system을 함께 사용
4. update_times() 함수는 인터 럽트 허용후 다음 동작 수행
- lost_ticks를 tick 변수에 저장한 후 0으로 만든다
- lost_ticks_system을 system에 저장한 후 0으로 만든다.
- calc_load(tick)를 호출한다.
- TASK_RUNNUNG & TASK_UNINTERRUPTIBLE 상태의 프로세스 갯수 카운트
- update_process_times(ticks, system)를 호출한다.
- Kernel_stat.kstat 에 저장된 커널 통계를 갱신
- update_one_process() 호출 : times()을 통해 알수 있는 필드 정보 갱신 times()설명
역할 3 : CPU 시분할
- 각 프로세스는 보통 퀀텀(quantum)이라는 제한된 시간 동안만 CPU를 사용한다. 시간 만료시 schedule()통하여 새 프로세스 선택
- 이러한 시간 측정을 위해 타이머 인터럽트는 중요하다.
- 프로세스 디스크립터의 counter 필드 : 남은 CPU의 Tick수
- 퀀텀 = Tick * 10ms
- 매 Tick이 발생시 update_process_times()를 이용하여 counter값 감소
역할 4 : 타이머 역할
- 주어진 시간 간격이 지났을때 특정 함수를 호출
- 특정 시간에 함수를 호출 하게함
- setitimer(), alarm()
- 커널과 프로세스에서 모두 사용 가능
타이머 구현
- 타이머의 특정 [만료시간 필드]에 만료 시간 저장
- jiffies에 원하는 틱수 더함
- 커널이 타이머 검사시 마다 [만료시간 필드]를 현재 jiffies값과 비교
- 저장된값 <= jiffies :타이머 종료
- 비교시 사용되는 메크로
- time_after
- time_before
- time_after_eq
- time_before_eq
타이머의 종료
- 정적 타이머 (static timer) : 커널 사용
- 동적 타이머 (Dynamic timer) : 커널 사용
- 간격 타이머 (Interval timer) : 사용자 모드의 프로세스가 사용
주의 사항
리눅스에서는 타이머의 기능이 botton half에서 수행되므로 수백 ms의 지연이 발생한다고 가정되기 때문에
정확한 시간 응답을 요구하는 실시간 응용 프로그램에는 적합 하지 않다.
1. 정적 타이머
- 정적으로 할당된 커널 자료 구조에 의지
- 초창기에 나온것으로 옛날 타이머라고도 한다.
- timer_table 배열에 저장된다
- 32개의 엔트리를 포함
- 각 엔트리는 timer_struct 구조체로 이루어진다.
structure timer_struct {
unsigned long expires; //타이머가 만료되는 시간 지정
void (*fn)(void); //타이머 만료실 실행할 함수 주소
};
정적 타이머 활성화 순서
- 실행할 함수를 fn필드에 등록한다.
- 만료 시간 계산후 expires필드에 저장
- timer_active의 해당 플래그를 설정한다.
2. 동적 타이머
- 동적으로 생성및 삭제가 가능하다
- 동적 타이머의 수 제한이 없다.
- 각 동적 타이머를 다음 timer_list 구조체에 저장한다.
struct timer_list {
struct timer_list *next; //이중 원형 연결 리스트를 구현시 사용
struct timer_list *prev; //이중 원형 연결 리스트를 구현시 사용
unsigned long expires; //타이머가 만료되는 시간 지정
unsigned long data; // 타이머 함수에 전달할 매개 변수를 지정
void (*function)(unsigned long); // 타이머 만료실 실행할 함수 주소
};
동적 타이머 생성및 활성화
- 새 struct timer_list 를 생성
- init_timer(&t) 함수를 호출해서 객체를 초기화 한다
- next를 null로 설정
- prev를 null로 설정
- expires 필드 지정
- 타이머 정보가 없을 경우 : 해당 값을 지정
- 타이머 정보가 있을경우 : mod_timer() 함수를 호출하여 갱신
- function 필드에 타이머 만료시 실행할 함수 주소 입력
- 경우에 따라서 data필드에 매개 변수도 입력
- 타이머 정보가 없을경우 add_timer(&t) 호출해서 t항목을 올바른 리스트에 삽입
동적 타이머 만료시
6. 리스트 제거
- 방법 1 : 커널이 자동으로 t 항목을 리스트에서 제거
- 방법 2 : 프로세스가 del_timer() 호출하여 타이머러를 리스트에서 제거
만료된 타이머를 찾아 내는 알고리즘
정적 타이머
- run_old_timers() 함수가 timer_table의 항목 32개를 for루프를 돌면서 탐색
동적 타이머
- 문제점 : 동적 타이머는 수의 제한이 없다.
- 매 틱마다 긴 리스트를 검색 하는데 시간이 많이 걸림
- 리스트를 정력해서 유지 하는 방법도 삽입과 제거에 시간이 많이 걸린다.
동적 타이머 응용
- 프로세스 타임 아웃 : 요구한 서비스를 제공하지 못할경우 커널이 해당 프로세스를 정해진 시간 동안 멈추게 하는것.
- 커널은 동적 타이머를 사용하여 프로세스 타임아웃 구현
ex ) 2초 동안 정지
timeout = 2*HZ
current->state = TASK_INTERRUPTIBLE;
timeout = schedule_timeout(timeout);
schedule_timeout()
struct timer_list timer;
expire = timeout + jiffies;
init_timer (&timer);
timer.expires + expire;
timer.data = (unsigned long) current;
timer.function = process_timeout;
add_timer(&timer);
schedule(); // 타이머가 만료될 때까지 프로세스는 보류된다.
del_timer(&timer);
timeout = expire - jiffies;
return (timeout < 0 ? 0 : timeout);
schedule()
- 실행할 다른 프로세스 선택
- 타임 아웃이 만료시 0 반환
- 다른 이유로 프로세스가 깨어난 경우 만료까지 남은 tick수를 반환
History
Last edited on 05/30/2007 15:59 by adioshun
Comments (0)