мой тестовый код драйвера, я хочу использовать mod_timer для таймера на 10 миллисекунд, но результат всегда составляет 20 миллисекунд. что с этим не так? ..
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/unistd.h>
#include <linux/delay.h>
static struct timer_list my_timer;
void my_timer_callback(struct timer_list *timer)
{
printk("my_timer_callback called (%lu).\n", jiffies);
mod_timer(&my_timer, jiffies + msecs_to_jiffies(10));
}
static int __init my_init(void)
{
printk("Timer module loaded\n");
timer_setup(&my_timer, my_timer_callback, 0);
mod_timer(&my_timer, jiffies + msecs_to_jiffies(10));
return 0;
}
static void __exit my_exit(void)
{
printk("Timer module unloaded\n");
del_timer(&my_timer);
}
module_init(my_init);
module_exit(my_exit);
MODULE_AUTHOR("[email protected]");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
результат dmesg:
[ 70.391971] my_timer_callback called (4294944328).
[ 70.411947] my_timer_callback called (4294944330).
[ 70.431944] my_timer_callback called (4294944332).
[ 70.451961] my_timer_callback called (4294944334).
версия ядра: 5.10
.config: КОНФИГ_ГЦ=100
значение ХЗ равно 100
./sound/isa/sb/sb8_midi.c: mod_timer(&chip->midi_timer, 1 + jiffies); ./sound/isa/sb/sb8_midi.c: mod_timer(&chip->midi_timer, 1 + jiffies); ./sound/isa/sb/emu8000_pcm.c: mod_timer(&rec->timer, jiffies + 1); ./sound/isa/sb/emu8000_pcm.c: mod_timer(&rec->timer, jiffies + 1);
код ядра также использует jiffies
Разве jiffies не является u64, то есть неподписанным?
Есть ли связь с беззнаковым? Кажется, это связано с неподписанным.
Этот вопрос похож на: Надежность add_timer ядра Linux при разрешении в один миг?. Если вы считаете, что это другое, отредактируйте вопрос, поясните, чем он отличается и/или как ответы на этот вопрос не помогают решить вашу проблему.
См. проблему с mod_timer в ядре Linux 4.18 для возможного решения.
Что произойдет, если заменить mod_timer(&my_timer, jiffies + msecs_to_jiffies(10));
на mod_timer(&my_timer, my_timer.expires + msecs_to_jiffies(10));
в my_timer_callback()
?
@AllanWind Нет, jiffies
— это volatile unsigned long
. get_jiffies_64()
возвращает u64
.
@IanAbbott Второй раз (обратный вызов) и последующие таймеры работают нормально, почему? Почему они не работали раньше? [ 71.822001] my_timer_callback called (4294944471). [ 71.831990] my_timer_callback called (4294944472). [ 71.841988] my_timer_callback called (4294944473). [ 71.851989] my_timer_callback called (4294944474).
Можете ли вы напечатать оба timer->expires
(или my_timer.expires
, поскольку это одно и то же) и jiffies
в обратном вызове, чтобы проверить наличие расхождений?
Я хочу сказать, что вы печатаете значение как подписанное.
@AllanWind Он был изменен на %lu.
@IanAbbott Это результат печати обоих файлов . [ 32.651901] my_timer_callback called (4294940554). [ 32.651922] my_timer.expires=4294940553 [ 32.661892] my_timer_callback called (4294940555). [ 32.661911] my_timer.expires=4294940554 [ 32.671892] my_timer_callback called (4294940556). [ 32.671906] my_timer.expires=4294940555 [ 32.681892] my_timer_callback called (4294940557). [ 32.681907] my_timer.expires=4294940556
Похоже, обратный вызов вызывается через 1 миг после истечения срока действия. (В вашем случае миг составляет 10 мс).
@IanAbbott В файле kernel/time/timer.c есть несколько комментариев. Кажется, есть какая-то корреляция, но я пока не смог ее выяснить. Когда я пытаюсь установить таймер от 10 мс до 1000 мс, мой обратный вызов всегда происходит через 960 мс или 1040 мс. Дрейф (80 мс) — это уровень детализации 1. * HZ 100 * Level Offset Granularity Range * 0 0 10 ms 0 ms - 630 ms * 1 64 80 ms 640 ms - 5110 ms (640ms - ~5s)
По опыту OP и как отмечено в этом сообщении электронной почты , вполне возможно, что при вызове функций истечения срока действия таймера может возникнуть задержка в один миг из-за того, что некоторые процессоры читают старое значение jiffies, в то время как другой его обновляет.
Поскольку функция обратного вызова таймера OP добавляет относительную сумму к jiffies
, чтобы установить следующее время истечения срока действия, время между обратными вызовами может быть на один миг дольше, чем требуется. Эту проблему можно смягчить, установив следующее время истечения срока действия относительно предыдущего. Если задержка в один миг постоянна, то все функции обратного вызова будут выполняться с опозданием на один миг. Однако возможно, что некоторые функции обратного вызова будут запущены вовремя, а некоторые — с опозданием, что может быть нежелательно.
Если вариант использования требует более точного времени, было бы лучше использовать API hrtimer, как показано в модифицированной версии кода OP ниже:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/hrtimer.h>
#include <linux/unistd.h>
#include <linux/delay.h>
static struct hrtimer my_timer;
static enum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{
s64 now = ktime_to_ns(hrtimer_cb_get_time(timer));
s64 expires = ktime_to_ns(hrtimer_get_softexpires(timer));
s64 diff = now - expires;
printk("my_timer_callback called (now %lld, expired %lld, diff %lld).\n",
now, expires, diff);
hrtimer_add_expires_ns(timer, 10 * NSEC_PER_MSEC);
return HRTIMER_RESTART;
}
static int __init my_init(void)
{
printk("Timer module loaded\n");
hrtimer_init(&my_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
my_timer.function = my_timer_callback;
hrtimer_start(&my_timer,
ktime_add_ms(hrtimer_cb_get_time(&my_timer), 10),
HRTIMER_MODE_ABS);
return 0;
}
static void __exit my_exit(void)
{
printk("Timer module unloaded\n");
hrtimer_cancel(&my_timer);
}
module_init(my_init);
module_exit(my_exit);
MODULE_AUTHOR("[email protected]");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
Пример вывода dmesg из модуля показан ниже:
[ 7119.967286] my_timer_callback called (now 7119822903369, expired 7119822900229, diff 3140).
[ 7119.977294] my_timer_callback called (now 7119832911804, expired 7119832900229, diff 11575).
[ 7119.987347] my_timer_callback called (now 7119842964493, expired 7119842900229, diff 64264).
[ 7119.997286] my_timer_callback called (now 7119852903102, expired 7119852900229, diff 2873).
[ 7120.007348] my_timer_callback called (now 7119862964879, expired 7119862900229, diff 64650).
[ 7120.017348] my_timer_callback called (now 7119872964924, expired 7119872900229, diff 64695).
[ 7120.027347] my_timer_callback called (now 7119882964377, expired 7119882900229, diff 64148).
[ 7120.037333] my_timer_callback called (now 7119892950004, expired 7119892900229, diff 49775).
[ 7120.047348] my_timer_callback called (now 7119902964627, expired 7119902900229, diff 64398).
[ 7120.057348] my_timer_callback called (now 7119912964748, expired 7119912900229, diff 64519).
[ 7120.067337] my_timer_callback called (now 7119922952994, expired 7119922900229, diff 52765).
[ 7120.077350] my_timer_callback called (now 7119932966586, expired 7119932900229, diff 66357).
[ 7120.087348] my_timer_callback called (now 7119942964245, expired 7119942900229, diff 64016).
[ 7120.097334] my_timer_callback called (now 7119952950074, expired 7119952900229, diff 49845).
[ 7120.107351] my_timer_callback called (now 7119962966616, expired 7119962900229, diff 66387).
Какова ценность ХЗ?