Система зависает при запуске двух разных LKM для получения статистики батареи каждую секунду
Я разрабатываю модуль ядра Linux (LKM), который анализирует параметры батареи моего ноутбука и записывает в файл журнала ядра (/var/log/kern.log) для каждого заданного промежутка времени. Используя данные из файла журнала, я буду строить график в реальном времени.
Вы можете подумать, почему я занимаюсь этим. Это часть моей академики, это ответ на нее. Кроме того, я нахожу это довольно интересным.
Весь мой анализ этой задачи до недавнего времени был простым. Шаги, которые я выполнил:
- Параметры можно получить, как указано в лучшем ответе здесь, и некоторые другие параметры были найдены здесь. Используя эти две ссылки, моя задача стала легкой.
- Код для выполнения функции LKM в периодическое время был извлечен отсюда. Это было изменено для запуска каждую секунду.
С этими двумя LKM я неправильно понял, что моя задача была простой, так как единственной оставшейся задачей было объединение этих двух LKM и запись необходимых данных параметров в файл журнала.
Я объединил код обоих LKM и создал один LKM и подключил его к своему ядру 64-битной ОС Ubuntu 14.04. Система мгновенно замерла, заставив меня шокироваться. Я был невежественен в этом пункте.
После этого я сократил код анализатора батареи LKM и таймера LKM, которые наименее необходимы для получения желаемого результата. Я нашел сокращенный код, который работает удобно.
Позже я перенес код для анализа параметров батареи в функцию и экспортировал ее (используя EXPORT_SYMBOL(<function_name>)
, чтобы вызываться из метода таймера. Я был чертовски уверен, что это будет работать наверняка, так как оба LKM работали здорово.
Я хотел бы дать вам реальную картину того, что произошло на моей машине, с помощью следующих кодов LKM и видео.
bat_stat_analyzer.c - LKM для получения параметров батареи и записи в файл журнала ядра.
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/power_supply.h>
static int result = 0;
static struct power_supply *psy;
static void bat_stat(void);
EXPORT_SYMBOL(bat_stat);
static void bat_stat(void) {
union power_supply_propval value;
int charge_status, voltage_now, current_now, capacity;
psy = power_supply_get_by_name("BAT1");
result = psy->get_property(psy,POWER_SUPPLY_PROP_STATUS, &value);
charge_status = (!result) ? value.intval : -1;
result = psy->get_property(psy,POWER_SUPPLY_PROP_VOLTAGE_NOW, &value);
voltage_now = (!result) ? value.intval : -1;
result = psy->get_property(psy,POWER_SUPPLY_PROP_CURRENT_NOW, &value);
current_now = (!result) ? value.intval : -1;
result = psy->get_property(psy,POWER_SUPPLY_PROP_CAPACITY, &value);
capacity = (!result) ? value.intval : -1;
printk(KERN_INFO "%s:%d,%d,%d,%d\n",
__func__, charge_status, voltage_now, current_now, capacity);
}
static int __init bat_stat_init(void) /* Constructor */
{
bat_stat();
return 0;
}
static void __exit bat_stat_exit(void) /* Destructor */
{
printk(KERN_INFO "Good bye\n");
}
module_init(bat_stat_init);
module_exit(bat_stat_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sriram Kumar <sriramhearing_at_gmail_dot_com>");
MODULE_DESCRIPTION("First Battery Analyzer");
bat_stat_repeater.c - LKM для повторного вызова функции
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
#include <linux/jiffies.h>
#include <linux/time.h>
#include <linux/hrtimer.h>
static unsigned long period_ms;
static unsigned long period_ns;
static ktime_t ktime_period_ns;
static struct hrtimer my_hrtimer;
extern int bat_stat(void);
//~ static void bat_stat_repeat(unsigned long data)
static enum hrtimer_restart bat_stat_repeat(struct hrtimer *timer)
{
unsigned long tjnow;
ktime_t kt_now;
bat_stat();
printk(KERN_INFO "Repeating...\n");
tjnow = jiffies;
kt_now = hrtimer_cb_get_time(&my_hrtimer);
hrtimer_forward(&my_hrtimer, kt_now, ktime_period_ns);
return HRTIMER_RESTART;
}
static int __init bat_stat_init(void)
{
struct timespec tp_hr_res;
period_ms = 1000;
hrtimer_get_res(CLOCK_MONOTONIC, &tp_hr_res);
hrtimer_init(&my_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
my_hrtimer.function = &bat_stat_repeat;
period_ns = period_ms*( (unsigned long)1E6L );
ktime_period_ns = ktime_set(0,period_ns);
hrtimer_start(&my_hrtimer, ktime_period_ns, HRTIMER_MODE_REL);
return 0;
}
static void __exit bat_stat_exit(void)
{
int ret_cancel = 0;
while( hrtimer_callback_running(&my_hrtimer) ) {
ret_cancel++;
}
if (ret_cancel != 0) {
printk(KERN_INFO " testjiffy Waited for hrtimer callback to finish (%d)\n", ret_cancel);
}
if (hrtimer_active(&my_hrtimer) != 0) {
ret_cancel = hrtimer_cancel(&my_hrtimer);
printk(KERN_INFO " testjiffy active hrtimer cancelled: %d\n", ret_cancel);
}
if (hrtimer_is_queued(&my_hrtimer) != 0) {
ret_cancel = hrtimer_cancel(&my_hrtimer);
printk(KERN_INFO " testjiffy queued hrtimer cancelled: %d\n", ret_cancel);
}
printk(KERN_INFO "Exit testjiffy\n");
}
module_init(bat_stat_init);
module_exit(bat_stat_exit);
MODULE_LICENSE("GPL");
Makefile, который я использовал, выглядел как показано ниже, так как он использовался для компиляции обоих LKM.
obj-m := <file_name>.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
Вывод, который я получил с помощью этих двух LKM, можно посмотреть по этой ссылке на YouTube (всего 2 минуты видео). Если вы хотите увидеть его как изображение, оно прикреплено ниже:
Я бы хотел знать:
- Есть ли лучший и / или более простой способ добиться того, что я пытаюсь сделать? или же
- В чем проблема с моими LKM, из-за которых мой процессор заглушается?
Заранее спасибо.
1 ответ
У вас есть проблема из-за использования hrtimers
, Этот механизм разработан для высокой точности, обратный вызов вызывается в контексте hardirq (с отключенными IRQ), поэтому ваша функция обратного вызова должна быть атомарной. Тем не менее, функции, которые вы вызываете из обратного вызова, не являются атомарными и могут спать (из-за мьютексов). Обычные таймеры имеют схожие проблемы, поэтому, чтобы исправить это, вы должны использовать другой способ или повторяющуюся задачу, например, рабочие очереди.
Некоторые другие небольшие проблемы в вашем коде:
- ваш
batt_stat
объявления функций в обоих модулях разные - вы не проверяете вывод power_supply_get_by_name() в первом модуле
Кроме того, я действительно не вижу причин разделять это на два модуля ядра, вы должны использовать только один.