Linux 커널 타이머 사용에 대한 자세한 설명

WBOY
풀어 주다: 2024-02-09 13:50:27
앞으로
916명이 탐색했습니다.

LINUX 커널 타이머는 커널이 미래의 특정 시점(지피 기준)에 특정 기능의 예약 및 실행을 제어하는 ​​데 사용하는 메커니즘입니다. 이 메커니즘의 구현은 및 kernel/timer.c 파일에 있습니다.

Linux 커널 타이머 사용에 대한 자세한 설명

예약된 기능은 비동기식으로 실행되어야 하며 이는 "소프트웨어 인터럽트"와 유사하며 프로세스가 아닌 컨텍스트에 있습니다. 따라서 일정 기능은 다음 규칙을 따라야 합니다.

  1. 현재 포인터가 없으면 사용자 공간에 대한 액세스가 허용되지 않습니다. 프로세스 컨텍스트가 없기 때문에 해당 코드는 중단된 프로세스와 연결되지 않습니다.
  2. 절전 모드 또는 절전 모드를 유발할 수 있는 기능 및 스케줄을 실행할 수 없습니다.
  3. 액세스되는 모든 데이터 구조는 경쟁 조건을 방지하기 위해 동시 액세스로부터 보호되어야 합니다.

커널 타이머 스케줄링 기능은 한 번 실행되면 다시 실행되지 않습니다(자동 로그아웃과 동일). 그러나 예약된 기능 내에서 자체 일정을 조정하여 주기적으로 실행하는 것은 가능합니다.

SMP 시스템에서 스케줄링 기능은 캐시 지역성을 최대한 확보하기 위해 항상 등록된 동일한 CPU에서 실행됩니다.

타이머 API

커널 타이머의 데이터 구조

으아악

만료 필드는 타이머가 실행할 것으로 예상되는 jiffies 값을 나타냅니다. jiffies 값에 도달하면 함수가 호출되고 데이터가 매개변수로 전달됩니다. 타이머가 커널에 등록되면 입력 필드는 타이머를 커널 연결 목록에 연결하는 데 사용됩니다. 기본 필드는 커널의 내부 구현에서 사용됩니다.
만료 값은 32비트입니다. 커널 타이머는 미래의 긴 시점에 적용되지 않기 때문입니다.

초기화

structtimer_list를 사용하기 전에 모든 필드가 올바르게 설정되었는지 확인하기 위해 데이터 구조를 초기화해야 합니다. 초기화 방법에는 두 가지가 있습니다.
방법 1:
DEFINE_TIMER(타이머_이름, 함수_이름, 만료_값, 데이터);
이 매크로는 timer_name이라는 커널 타이머를 정적으로 생성하고 해당 기능, 만료, 이름 및 기본 필드를 초기화합니다.

방법 2:

으아악

init_timer()를 통해 타이머를 동적으로 정의한 후 처리 함수의 주소와 매개변수를 타이머 목록에 바인딩합니다.
어떤 방법을 사용하여 초기화하든 본질은 필드에 값을 할당하는 것이므로 add_timer()를 실행하기 전에 만료, 함수 및 데이터 필드를 직접 수정할 수 있습니다.
위의 매크로와 함수에 대한 정의는 include/linux/timer.h를 참조하세요.

등록

타이머가 적용되려면 커널의 전용 연결 리스트에 연결되어야 합니다. 이는 add_timer(struct timer_list *timer)를 통해 달성할 수 있습니다.

재등록

타이머의 예약 시간을 수정하려면 mod_timer(struct timer_list *timer, unsigned longexpires)를 호출하면 됩니다. mod_timer()는 타이머 기능이 실행되었는지 여부에 관계없이 타이머를 커널에 다시 등록합니다.

로그아웃

타이머 등록을 취소하려면 del_timer(struct timer_list *timer) 또는 del_timer_sync(struct timer_list *timer)를 사용할 수 있습니다. 그 중 del_timer_sync는 SMP 시스템에서 사용됩니다(SMP가 아닌 시스템에서는 del_timer와 같습니다). 로그아웃하려는 타이머 함수가 다른 CPU에서 실행 중일 때 del_timer_sync()는 실행이 완료될 때까지 대기하므로 이는 기능이 최대 절전 모드로 전환됩니다. 또한 예약된 기능과 동일한 잠금을 놓고 경쟁하는 것도 피해야 합니다. 이미 실행되었으며 자체적으로 다시 등록되지 않은 타이머의 경우 등록 취소 기능은 실제로 아무 작업도 수행하지 않습니다.

int 타이머_pending(const struct 타이머_list *timer)
이 함수는 실행 예약을 기다리는 커널 목록에 타이머가 추가되었는지 확인하는 데 사용됩니다. 타이머 기능이 실행되려고 할 때 커널은 커널 연결 목록에서 해당 타이머를 삭제합니다(로그아웃과 동일)

一个简单的例子

#include 
\#include 
\#include 

struct timer_list mytimer;

static void myfunc(unsigned long data)
{
    printk("%s/n", (char *)data);
    mod_timer(&mytimer, jiffies + 2*HZ);
}

static int __init mytimer_init(void)
{
    setup_timer(&mytimer, myfunc, (unsigned long)"Hello, world!");
    mytimer.expires = jiffies + HZ;
    add_timer(&mytimer);

​    return 0;
}

static void __exit mytimer_exit(void)
{
    del_timer(&mytimer);
}

module_init(mytimer_init);
module_exit(mytimer_exit);

例子2
static struct timer_list power_button_poll_timer;

static void power_button_poll(unsigned long dummy)
{
 if (gpio_line_get(N2100_POWER_BUTTON) == 0) {
 ctrl_alt_del();
 return;
 }

 power_button_poll_timer.expires = jiffies + (HZ / 10);
 add_timer(&power_button_poll_timer);
}


static void __init n2100_init_machine(void)
{
;
;
 init_timer(&power_button_poll_timer);
 power_button_poll_timer.function = power_button_poll;
 power_button_poll_timer.expires = jiffies + (HZ / 10);
 add_timer(&power_button_poll_timer);
}
로그인 후 복사

例子3

设备open时初始化和注册定时器

static int corkscrew_open(struct net_device *dev)

{
;
;
 init_timer(&vp->timer);  
 vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
 vp->timer.data = (unsigned long) dev;
 vp->timer.function = &corkscrew_timer; /* timer handler */
 add_timer(&vp->timer);
:
;
}
定时器超时处理函数,对定时器的超时时间重新赋值

static void corkscrew_timer(unsigned long data)
{
;
;
  vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
  add_timer(&vp->timer);
;
;
}

设备close时删除定时器
static int corkscrew_close(struct net_device *dev)
{
 ;
;
 del_timer(&vp->timer);
;
;
}


例子4

本例子用DEFINE_TIMER静态创建定时器

\#include 
\#include 
\#include 
\#include 
\#include 
\#include 

static void ledtrig_ide_timerfunc(unsigned long data);

DEFINE_LED_TRIGGER(ledtrig_ide);
static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0);
static int ide_activity;
static int ide_lastactivity;

void ledtrig_ide_activity(void)
{
    ide_activity++;
    if (!timer_pending(&ledtrig_ide_timer))
       mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
}
EXPORT_SYMBOL(ledtrig_ide_activity);

static void ledtrig_ide_timerfunc(unsigned long data)
{
    if (ide_lastactivity != ide_activity) {
       ide_lastactivity = ide_activity;
       led_trigger_event(ledtrig_ide, LED_FULL);
       mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
    } else {
       led_trigger_event(ledtrig_ide, LED_OFF);
    }
}

static int __init ledtrig_ide_init(void)
{
    led_trigger_register_simple("ide-disk", &ledtrig_ide);
    return 0;
}

static void __exit ledtrig_ide_exit(void)
{
    led_trigger_unregister_simple(ledtrig_ide);
}

module_init(ledtrig_ide_init);
module_exit(ledtrig_ide_exit);

MODULE_AUTHOR("Richard Purdie ");
MODULE_DESCRIPTION("LED IDE Disk Activity Trigger");
MODULE_LICENSE("GPL");
로그인 후 복사

위 내용은 Linux 커널 타이머 사용에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:lxlinux.net
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!