Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

15192
888
89788


[라즈베리파이] 커널 타이머 - 동적 타이머 실행 코드 7장. 타이머관리

동적 타이머를 초기화만 하면 동적 타이머를 쓸 수 없습니다. 동적 타이머를 실행해야 합니다. 이를 위해 add_timer()/add_timer_on() 이나 mod_timer() 함수에 적절한 인자를 채워 호출해야 합니다.

보통 add_timer() 함수를 써서 동적 타이머를 실행하며 이후 동적 타이머 만료 시간을 다시 설정한 후 동적 타이머를 실행시킬 때 mod_timer() 함수를 씁니다.  add_timer()와 mod_timer() 함수를 써서 로컬 타이머를 설정하는 코드를 살펴보겠습니다.

먼저 라즈비안에서 로컬 타이머를 실행하는 코드를 봅시다.
1 bool bcm2835_sdhost_send_command(struct bcm2835_host *host,
2  struct mmc_command *cmd)
3{
...
4 timeout = jiffies;
5 if (!cmd->data && cmd->busy_timeout > 9000)
6 timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
7 else
8 timeout += 10 * HZ;
9 mod_timer(&host->timer, timeout);

코드 분석에 앞서 jiffies와 timeout 변수 단위는 HZ라는 점을 염두하시기 바랍니다.

4번 줄 코드를 먼저 봅시다.
4 timeout = jiffies

timeout 이란 변수에 현재 시각 정보를 담고 있는 HZ 단위인 jiffies 변수를 저장합니다.
5~8번 줄은 조건에 따라 timeout 변수에 시각 정보를 더합니다. timeout 변수는 타이머 만료 시각을 나타냅니다.
5 if (!cmd->data && cmd->busy_timeout > 9000)
6 timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
7 else
8 timeout += 10 * HZ;

만약 8번 줄 코드를 실행하면 HZ에 10을 곱한 값을 timeout 변수에 더합니다. timeout 지역 변수에 저장된 값은 현재 시각 기준으로 10초 후 HZ단위 시각 정보를 의미합니다.  10초 후에 동적 타이머가 만료하는 겁니다.

9번 줄 코드는 mod_timer() 함수를 써서 타이머를 시작합니다. 로컬 타이머 구조체인 struct timer_list 포인터 주소와 로컬 타이머 만료 시간을 전달합니다.

&host->timer 타이머 속성은 bcm2835_sdhost_add_host () 함수에서 확인할 수 있습니다.
1 int bcm2835_sdhost_add_host(struct bcm2835_host *host)
2 {
3 struct mmc_host *mmc;
...
4 setup_timer(&host->timer, bcm2835_sdhost_timeout,
5     (unsigned long)host);

&host->timer 타이머는 이미 bcm2835_sdhost_add_host() 함수에서 초기화를 했는데 타이머 핸들러 함수가 bcm2835_sdhost_timeout() 입니다. &host->timer 변수 구조체는 struct timer_list입니다.

정리하면 timeout 변수가 지정한 로컬 타이머 만료 시간에 bcm2835_sdhost_timeout() 타이머 핸들러 함수가 호출합니다.

다음 코드는 add_timer() 함수를 써서 로컬 타이머를 설정합니다. 조금 복잡해 보이는 코드 같지만 함수 원리를 알면 어렵지 않습니다. 
[https://elixir.bootlin.com/linux/v4.14.43/source/drivers/mmc/host/vub300.c]
1 static int vub300_probe(struct usb_interface *interface,
2 const struct usb_device_id *id)
3{
...
4 init_timer(&vub300->sg_transfer_timer);
5 vub300->sg_transfer_timer.data = (unsigned long)vub300;
6 vub300->sg_transfer_timer.function = vub300_sg_timed_out;
7 kref_get(&vub300->kref);
8 init_timer(&vub300->inactivity_timer);
9 vub300->inactivity_timer.data = (unsigned long)vub300;
10 vub300->inactivity_timer.function = vub300_inactivity_timer_expired;
11 vub300->inactivity_timer.expires = jiffies + HZ;
12 add_timer(&vub300->inactivity_timer);

4번째 줄 코드부터 봅시다. init_timer() 함수로 로컬 타이머를 초기화 합니다.
4 init_timer(&vub300->sg_transfer_timer);

만약 이 코드가 CPU2에서 실행 중이면 &vub300->sg_transfer_timer.flags는 2가 됩니다.
이제 9~12줄 코드를 분석하겠습니다.
9 vub300->inactivity_timer.data = (unsigned long)vub300;
10 vub300->inactivity_timer.function = vub300_inactivity_timer_expired;
11 vub300->inactivity_timer.expires = jiffies + HZ;
12 add_timer(&vub300->inactivity_timer); 

9번 줄은 타이머 핸들러에 전달하는 매개 변수를 vub300 변수로 지정합니다. 이 매개 변수는 타이머 핸들러인 vub300_inactivity_timer_expired() 함수에 전달합니다. 대부분 매개 변수는 다바이스 속성 정보가 모두 포함된 구조체 주소 값을 넘겨 줍니다. 따라서 vub300 변수는 포인터 타입 변수입니다.

10번 줄 코드는 vub300_inactivity_timer_expired() 함수를 동적 타이머 핸들러로 등록합니다. 

11번 줄은 로컬 타이머가 만료할 시각을 설정합니다.  현재 시각 정보인 jiffies에 HZ를 더하니 1초 후에 타이머가 만료합니다.

12번 줄 코드를 보면 add_timer() 함수를 써서 타이머를 설정합니다.

그러면 add_timer()와 mod_timer() 함수의 차이점은 뭘까요? add_timer() 함수는 로컬 타이머 속성을 설정하고 호출해야 하며, mod_timer() 함수는 단지 로컬 타이머가 종료될 시간을 jiffies 기준으로 설정합니다. 보통 add_timer() 함수를 써서 동적 타이머의 상세 정보를 설정하고 실행합니다. 이후 동적 타이머 만료 시간을 다시 설정한 후 동적 타이머를 실행할 때 mod_timer() 함수를 씁니다.





핑백

덧글

댓글 입력 영역