커널 타이머

EEEFFEE·2023년 11월 28일
0

Armv8 Architecture

목록 보기
7/15

23.11.28 최초 작성
24.01.23 struct timer_list 추가

1. 커널 타이머

  • 시스템에 부하가 많이 걸리거나 작업 처리에 시간이 많이 걸릴 경우 타이머를 활용해 적절한 처리를 해야 함

2. jiffies

  • 커널 드라이버에서 실행 시간을 체크하는 단위
  • 1초에 Hz만큼 증가함 (Armv8 : 기본 250Hz)
  • jiffeis자료형에 값이 저장되며 jiffeis_64의 시작주소 ~ 4byte공간에 위치함

ex)


// /source/sound/isa/wss/wss_lib.c

void snd_wss_mce_down(struct snd_wss *chip)
{
	unsigned long flags;
	unsigned long end_time;
	int timeout;
	int hw_mask = WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK | WSS_HW_AD1848;

	snd_wss_busy_wait(chip);

	...

	/* check condition up to 250 ms */
	end_time = jiffies + msecs_to_jiffies(250);
	while (snd_wss_in(chip, CS4231_TEST_INIT) &
		CS4231_CALIB_IN_PROGRESS) {

		if (time_after(jiffies, end_time)) {
			snd_printk(KERN_ERR "mce_down - "
					"auto calibration time out (2)\n");
			return;
		}
		msleep(1);
	}

	...
}

2.1 관련 함수

  • msecs_to_jiffies 인자로 받은 값(msec)을 jiffies단위 시간 정보로 변환해 줌

//	/source/include/linux/jiffies.h
static __always_inline unsigned long msecs_to_jiffies(const unsigned int m)
{
	if (__builtin_constant_p(m)) {
		if ((int)m < 0)
			return MAX_JIFFY_OFFSET;
		return _msecs_to_jiffies(m);
	} else {
		return __msecs_to_jiffies(m);
	}
}

  • time_after(a,b) : a > b 이면 true, 아니면 false
  • time_before(a,b) : a > b 이면 false, 아니면 true
  • sched_clock() : 해당 코드가 실행할 때의 시간정보 반환 (unsigned long long)

3. 동적 타이머

  • 현재 시간 정보(jiffies)기준으로 특정 시간 이후 만료될 타이머
  • 만료될 시 동적 타이머 핸들러 함수 호출
  • 디버깅 정보 출력, 특정 시간 후 루틴 수행에 활용

3.1 timer_list

  • struct timer_list : 커널 타이머를 관리하는 구조체

//	include/linux/timer.h

struct timer_list {
	struct hlist_node	entry;							//timer_bases전역변수에 타이머를 등록할때 사용
	unsigned long		expires;						//만료시각 저장 (jiffies)
	void			(*function)(struct timer_list *);	//동적 타이머 핸들러의 주소 저장
	u32			flags;

#ifdef CONFIG_LOCKDEP
	struct lockdep_map	lockdep_map;
#endif
};

3.1.1 timer_bases per-cpu

  • struct timer_bases : 전체 동적 타이머를 관리

//	kernel/time/timer.c
static DEFINE_PER_CPU(struct timer_base, timer_bases[NR_BASES]);

static __latent_entropy void run_timer_softirq(struct softirq_action *h)
{
	struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);

	__run_timers(base);
	if (IS_ENABLED(CONFIG_NO_HZ_COMMON))
		__run_timers(this_cpu_ptr(&timer_bases[BASE_DEF]));
}

  • struct timer_base :

struct timer_base {
	raw_spinlock_t		lock;
	struct timer_list	*running_timer;				//현재 타이머 핸들러를 실행중인 동적 타이머 자료구조(만료됨)
#ifdef CONFIG_PREEMPT_RT
	spinlock_t		expiry_lock;
	atomic_t		timer_waiters;
#endif
	unsigned long		clk;
	unsigned long		next_expiry;
	unsigned int		cpu;						//현재 타이머 벡터 cpu id
	bool			next_expiry_recalc;				//다음에 만료될 시각 저장
	bool			is_idle;
	bool			timers_pending;
	DECLARE_BITMAP(pending_map, WHEEL_SIZE);		//타이머 휠 인덱스마다 1비트 업데이트
	struct hlist_head	vectors[WHEEL_SIZE];		//타이머 휠 리스트
} ____cacheline_aligned;

3.2 동적 타이머 실행 흐름

  • /source/include/linux/timer.h
    • timer_setup() : 타이머 초기화
    • struct timer_list : 타이머의 정보를 저장하는 자료구조
  • /source/kernel/time/timer.c
    • add_timer() : 타이머 추가
    • mod_timer() : 타이머 설정
    • run_timer_softirq() : softirq를 통해 타이머 스레드 활성화
    • __run_timers() : 타이머 작동
    • __collect_expired_timers() : 만료된 타이머 확인
    • call_timer_fn() : 만료된 타이머의 타이머 핸들러 호출

0개의 댓글