kernel timer를 사용하려고 합니다. 맞는지 봐주실래요? ^^
커널 모듈에서 네트워크 패킷을 캡쳐하여 정보 분석 큐에 기초 정보와 시간 정보를 기록하여 어플리 케이션 레벨로 덤핑하는 모듈을 만들고 있습니다.
패킷이 올때마다 시간 함수를 호출하여 기록하기에는 호출 횟수가 너무 많고 호출시 획득되는 데이터 간의 차이가 거의 없기 때문에(초단위 측정을 해서리) 커널에 타이머를 두고 1초 단위로 시간 변수를 접근하여 기록해두고
패킷이 올때마다 시간변수의 내용을 기록하는 방법을 사용하려고 합니다.
그런데 커널 타이머를 설정하는데 다음과 같은 방법을 썼습니다.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/sched.h>
struct timer_list my_mod_timer; // 타이머 큐에 넣기 위한 타이머 변수
void func(unsigned long key); // 타이머의 초과 시간이 완료된 경우 실행할 타이머 핸들러
static int start()
{
init_timer(&my_mod_timer); // 타이머 노드 초기화
my_mod_timer.expires = (jiffies + 1 * HZ); // 1초단위 입니다.
my_mod_timer.data = 777; // 타이머 번호 설정
my_mod_timer.function = func; //핸들러 설정
add_timer(&my_mod_timer); // 타이머 큐에 추가
printk("module mod_prog is initialized\n");
return 0;
}
void void end()
{
del_timer(&my_mod_timer); // 타이머 큐에서 제거
printk("module mod_prog is cleanuped\n");
}
void func(unsigned long key)
{
if(key == 777)
{
printk("sucess\n");
init_timer(&my_mod_timer); // 노드 재 설정
my_mod_timer.expires = (jiffies + 1 * HZ);
my_mod_timer.data = 777;
my_mod_timer.function = func;
add_timer(&my_mod_timer); // 타이머 노드를 다시 집어 넣습니다.
}
}
EXPORT_NO_SYMBOLS;
module_init(start);
module_exit(end);
초기에는 초기화 함수에서 타이머 큐에 한번 넣으면 될 줄 알고 프로그래밍 했는데 초기화시 한번 동작하고 동작하지 않더군요.. 그래서 타이머 핸들러가 호출될때마다 큐에 다시 집어 넣는 방식을 사용했습니다.
이렇게 하니까 생각한대로 동작하더군요..
여기까지 짜면서 제가 알아낸 타이머 방식은 다음입니다.
1. expires에 현재 시간(jiffies)에 원하는 시간을 더하고 큐에 추가합니다.
2. 큐에서는 expires에 저장된 값과 현재 시간을 비교하여 같으면 노드에 연결된 콜백 함수를 호출합니다. 콜백 함수를 호출하면서 타이머 노드는 제거가 되고요.
이런 구조이기 때문에 타이머 노드를 타이머 큐에 계속 넣는 방법을 선택했는데. 이게 일반적인 방법인지 더나은 방법이 있는지 궁금합니다.
좋은 답변 바랍니다.
먼저 시간함수가 어떻게 구현되어 있는지 확인해보셨습니까?타이머 함
먼저 시간함수가 어떻게 구현되어 있는지 확인해보셨습니까?
타이머 함수를 등록해서 자신의 커널모듈만을 위한 시각을 별도로 유지하는것이 나을 정도로 비효율적으로 구현되어 있던가요?
어떤 부분이 비효율적이라는 것인지???
타이머란 것은 현재 시간에서 정의한 시간이 흐르면 action을 취하는 것이 타이머인 걸로 알고 있습니다.
한번 설정에 한번의 action이죠.. 전자 시계에 붙어 있는 타이머를 비롯해 모든 타이머가 그런 식입니다. expires를 설정하면 expires가 지난 시점에 내부에 정의된 action을 취하죠..
구현하고자 하는 주기적인 타이머란 기존의 타이머를 이용하는 정책의 문제라고 생각합니다.
그렇다면 커널 내부에서 지원할 것은 기본적인 타이머이고 application layer에서 연속적인 타이머를 원한다면 필요한 추가 기능(저와 같이 주기적으로 타이머를 재설정 하는.. )이 구현된다고 생각합니다.
제 생각에는 효율이나 비효율보다 각 layer에서 구현해야할 것이 틀리다고 생각합니다. 매카니즘 위주인 커널과 정책 위주인 어플리케이션 처럼요..
효율 비효율을 평가하는게 적절한지 의문입니다. 더불어 제가 원하는 기능 즉 주기적인 타이머 프로그램(그것도 커널에서)을 혹 해보셨다면 제가 사용한 방법이 맞는지 아니면 다른 방법이 있는지를 답변해 주시면 고맙겠습니다.
timer function에서 modtimer를 쓸 수도 있습니다.
도움이 되기를...
일단 타이머로 깨어나서 액션을 취한 다음에 그때 시간을 보고 다시 타이머
일단 타이머로 깨어나서 액션을 취한 다음에 그때 시간을 보고 다시 타이머를 설정하는 방법을 쓰면 jitter가 축적됩니다. Jitter는 커널의 각종 모듈들이 locking 을 하거나 하면서 인터럽트를 끔으로써 자신의 태스크가 깨어나야 할 시간에 못깨어나고 나중에 깨어나기 때문에 생깁니다. 결국 조금 지나면 자기 자신의 시간에 오차가 점차 커진다는것이고 이 오차의 증가가 시스템의 사양에 따라 혹은 동작 상태에 따라 매번 변한다는 것입니다. 따라서 자신의 시계를 정확하게 유지하려면 8253 이나 RTC 의 주기적 하드웨어 인터럽트를 사용하는 수밖에 없습니다. 결국 현재 커널의 시각유지 코드와 거의 비슷한 코드를 만들어야 겠죠.
그런데 이 모든 문제를 생각하기 전에 먼저 봐야 될게 "과연 내가 wheel 을 reinvent 해야 하느냐?" 하는 문제입니다.
혹 이런 방법을 생각해내시기전에 do_gettimeofday() 가 어떻게 구현되어 있는지 확인해 보셨습니까? 이 루틴이 정말로 너무나도 느리거나, 꼭 필요한 기능을 빼어놓았다면 자신의 코드를 다시 만들어야겠죠. 그런데 제가 얼핏 추측하기엔 그렇지 않은것 같습니다.
결국, 제가 보기엔 do_gettimeofday() 가 최소한의 locking과 최소한의 연산으로 정확한 시각을 커널 모듈에 제공하는 함수라고 보여지며, 이를 그냥 쓰시는 것이 제일 쉽고, 빠르고, 정확하게 원하는 기능을 얻을수 있는 방법으로 생각됩니다.
이미 sk_buff 에 패킷이 시스템에 도착한 시간이 있습니다.
만약 sk_buff 를 이용하여 패킷을 받으시는거라면, 이미 sk_buff 구조체에 패킷이 들어온 시간이 있습니다.. 물론 훨씬 윗단이라서 sk_buff 를 받아 보실 수 없다면 소용없는 얘기이구요..
sk_buff 의 stamp 변수가 그것입니다..
댓글 달기